diff --git a/tool/书源整理工具/BookSourceMgr.dpr b/tool/书源整理工具/BookSourceMgr.dpr
new file mode 100644
index 000000000..6e3280e73
--- /dev/null
+++ b/tool/书源整理工具/BookSourceMgr.dpr
@@ -0,0 +1,22 @@
+program BookSourceMgr;
+
+uses
+ Forms,
+ uFrmMain in 'uFrmMain.pas' {Form1},
+ Themes,
+ Styles,
+ uFrmWait in 'uFrmWait.pas' {Form2},
+ uBookSourceBean in 'uBookSourceBean.pas',
+ uFrmEditSource in 'uFrmEditSource.pas' {frmEditSource},
+ uFrmReplaceGroup in 'uFrmReplaceGroup.pas' {frmReplaceGroup},
+ uBookSourceBean20 in 'uBookSourceBean20.pas';
+
+{$R *.res}
+
+begin
+ Application.Initialize;
+ Application.MainFormOnTaskbar := True;
+ Application.Title := 'ĶԴ';
+ Application.CreateForm(TForm1, Form1);
+ Application.Run;
+end.
diff --git a/tool/书源整理工具/BookSourceMgr.dproj b/tool/书源整理工具/BookSourceMgr.dproj
new file mode 100644
index 000000000..446dc3461
--- /dev/null
+++ b/tool/书源整理工具/BookSourceMgr.dproj
@@ -0,0 +1,654 @@
+
+
+ {b1648034-9a9f-40d4-a355-b3335a5a9446}
+ Debug
+ DCC32
+ bin\BookSourceMgr.exe
+ BookSourceMgr.dpr
+ VCL
+ 18.3
+ True
+ Debug
+ Win64
+ 3
+ Application
+
+
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Cfg_1
+ true
+ true
+
+
+ true
+ Cfg_1
+ true
+ true
+
+
+ true
+ Base
+ true
+
+
+ true
+ Cfg_2
+ true
+ true
+
+
+ true
+ Cfg_2
+ true
+ true
+
+
+ BookSourceMgr
+ Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace)
+ 2052
+ CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=
+ D:\Documents\Desktop\delphi\SynEdit\Source;D:\Documents\Desktop\delphi\YxdJson\source;D:\Documents\Desktop\delphi\YxdIocp\YxdIocp\source\IOCP;D:\Documents\Desktop\delphi\YxdWorker\YxdWorker\source;E:\Projects\Delphi\YxdIocp\source\IOCP;E:\Projects\Delphi\YxdJson\source;E:\Projects\Delphi\YxdWorker\source;\\Mac\Home\Desktop\Project\SynEdit\SynEdit\Source;$(DCC_UnitSearchPath)
+
+
+ System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)
+ Debug
+ true
+ CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)
+ 1033
+ $(BDS)\bin\default_app.manifest
+ doc\Main.ico
+ true
+ $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png
+ $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png
+ true
+
+
+ $(BDS)\bin\default_app.manifest
+ BookSourceMgr_Icon1.ico
+ true
+ $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png
+ $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png
+ System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace)
+ Debug
+ true
+ CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
+ 1033
+
+
+ 7.0
+ 0
+ False
+ 0
+ RELEASE;$(DCC_Define)
+
+
+ true
+ true
+ CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)
+ true
+ 1033
+
+
+ true
+ true
+
+
+ 7.0
+ DEBUG;$(DCC_Define)
+ .\bin
+ .\dcu
+ .\dcu
+ .\dcu
+ D:\Documents\Desktop\YxdJson\source;D:\Documents\Desktop\YxdIocp\YxdIocp\source\IOCP;D:\Documents\Desktop\YxdWorker\YxdWorker\source;$(DCC_ResourcePath)
+ D:\Documents\Desktop\YxdJson\source;D:\Documents\Desktop\YxdIocp\YxdIocp\source\IOCP;D:\Documents\Desktop\YxdWorker\YxdWorker\source;$(DCC_ObjPath)
+ D:\Documents\Desktop\YxdJson\source;D:\Documents\Desktop\YxdIocp\YxdIocp\source\IOCP;D:\Documents\Desktop\YxdWorker\YxdWorker\source;$(DCC_IncludePath)
+
+
+ true
+ true
+ CompanyName=YangYxd;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=阅读书源管理工具;ProgramID=com.embarcadero.$(MSBuildProjectName)
+ true
+ 1033
+ true
+
+
+ true
+ true
+ CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
+ true
+ 1033
+ doc\Main.ico
+
+
+ Delphi.Personality.12
+
+
+
+
+ False
+ True
+ False
+
+
+ False
+ False
+ 1
+ 0
+ 0
+ 0
+ False
+ False
+ False
+ False
+ False
+ 2052
+ 936
+
+
+
+
+ 1.0.0.0
+
+
+
+
+
+ 1.0.0.0
+
+
+
+ Embarcadero Git Integration
+ Microsoft Office 2000 Sample Automation Server Wrapper Components
+ Microsoft Office XP Sample Automation Server Wrapper Components
+
+
+ BookSourceMgr.dpr
+
+
+
+ True
+ True
+
+
+
+
+ BookSourceMgr.exe
+ true
+
+
+
+
+ 1
+
+
+ Contents\MacOS
+ 1
+
+
+ Contents\MacOS
+ 0
+
+
+
+
+ classes
+ 1
+
+
+
+
+ library\lib\armeabi-v7a
+ 1
+
+
+
+
+ library\lib\armeabi
+ 1
+
+
+
+
+ library\lib\mips
+ 1
+
+
+
+
+ library\lib\armeabi-v7a
+ 1
+
+
+
+
+ res\drawable
+ 1
+
+
+
+
+ res\values
+ 1
+
+
+
+
+ res\drawable
+ 1
+
+
+
+
+ res\drawable-xxhdpi
+ 1
+
+
+
+
+ res\drawable-ldpi
+ 1
+
+
+
+
+ res\drawable-mdpi
+ 1
+
+
+
+
+ res\drawable-hdpi
+ 1
+
+
+
+
+ res\drawable-xhdpi
+ 1
+
+
+
+
+ res\drawable-small
+ 1
+
+
+
+
+ res\drawable-normal
+ 1
+
+
+
+
+ res\drawable-large
+ 1
+
+
+
+
+ res\drawable-xlarge
+ 1
+
+
+
+
+ 1
+
+
+ Contents\MacOS
+ 1
+
+
+ 0
+
+
+
+
+ Contents\MacOS
+ 1
+ .framework
+
+
+ 0
+
+
+
+
+ 1
+ .dylib
+
+
+ 1
+ .dylib
+
+
+ 1
+ .dylib
+
+
+ Contents\MacOS
+ 1
+ .dylib
+
+
+ 0
+ .dll;.bpl
+
+
+
+
+ 1
+ .dylib
+
+
+ 1
+ .dylib
+
+
+ 1
+ .dylib
+
+
+ Contents\MacOS
+ 1
+ .dylib
+
+
+ 0
+ .bpl
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ Contents\Resources\StartUp\
+ 0
+
+
+ 0
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+
+
+ ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF
+ 1
+
+
+ ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+
+
+ ..\
+ 1
+
+
+ ..\
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ ..\
+ 1
+
+
+
+
+ Contents
+ 1
+
+
+
+
+ Contents\Resources
+ 1
+
+
+
+
+ library\lib\armeabi-v7a
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ Contents\MacOS
+ 1
+
+
+ 0
+
+
+
+
+ 1
+
+
+ 1
+
+
+
+
+ Assets
+ 1
+
+
+ Assets
+ 1
+
+
+
+
+ Assets
+ 1
+
+
+ Assets
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+ 12
+
+
+
+ MainSource
+
+
+
+
+
+
+ dfm
+
+
+
+
+ dfm
+
+
+
+ dfm
+
+
+
+ Cfg_2
+ Base
+
+
+ Base
+
+
+ Cfg_1
+ Base
+
+
+
+
+
+
+
+
diff --git a/tool/书源整理工具/BookSourceMgr.otares b/tool/书源整理工具/BookSourceMgr.otares
new file mode 100644
index 000000000..75c11775b
Binary files /dev/null and b/tool/书源整理工具/BookSourceMgr.otares differ
diff --git a/tool/书源整理工具/BookSourceMgr.res b/tool/书源整理工具/BookSourceMgr.res
new file mode 100644
index 000000000..bd2e810df
Binary files /dev/null and b/tool/书源整理工具/BookSourceMgr.res differ
diff --git a/tool/书源整理工具/ReadMe.txt b/tool/书源整理工具/ReadMe.txt
new file mode 100644
index 000000000..5388a7775
--- /dev/null
+++ b/tool/书源整理工具/ReadMe.txt
@@ -0,0 +1,3 @@
+õ SynEdit
+
+https://github.com/SynEdit/SynEdit
\ No newline at end of file
diff --git a/tool/书源整理工具/doc/Main.ico b/tool/书源整理工具/doc/Main.ico
new file mode 100644
index 000000000..3ddcecd74
Binary files /dev/null and b/tool/书源整理工具/doc/Main.ico differ
diff --git a/tool/书源整理工具/uBookSourceBean.pas b/tool/书源整理工具/uBookSourceBean.pas
new file mode 100644
index 000000000..d73ae620d
--- /dev/null
+++ b/tool/书源整理工具/uBookSourceBean.pas
@@ -0,0 +1,361 @@
+unit uBookSourceBean;
+
+interface
+
+uses
+ YxdJson, Classes, SysUtils, Math;
+
+type
+ TBookSourceItem = class(JSONObject)
+ private
+ function GetIndexValue(const Index: Integer): string;
+ procedure SetIndexValue(const Index: Integer; const Value: string);
+ function GetEnable: Boolean;
+ function GetSerialNumber: Integer;
+ function GetWeight: Integer;
+ procedure SetEnable(const Value: Boolean);
+ procedure SetSerialNumber(const Value: Integer);
+ procedure SetWeight(const Value: Integer);
+ function GetLastUpdateTime: Int64;
+ procedure SetLastUpdateTime(const Value: Int64);
+ function GetScore: Integer;
+ procedure SetScore(const Value: Integer);
+ function GetEnabledExplore: Boolean;
+ procedure SetEnabledExplore(const Value: Boolean);
+ function GetBookSourceType: Integer;
+ procedure SetBookSourceType(const Value: Integer);
+ public
+ procedure AddGroup(const Name: string);
+ procedure RemoveGroup(const Name: string);
+ procedure ReplaceGroup(const Name, NewName: string);
+ function GetGroupList(): TArray;
+
+ procedure SetStrValue(const AKey, Value: string);
+ function GetStr(const AKey: string): string;
+
+ property bookSourceType: Integer read GetBookSourceType write SetBookSourceType; // Դ
+ property bookSourceGroup: string index 0 read GetIndexValue write SetIndexValue; // Դ
+ property bookSourceName: string index 1 read GetIndexValue write SetIndexValue; // Դ
+ property bookSourceUrl: string index 2 read GetIndexValue write SetIndexValue; // ԴURL
+ property bookUrlPattern: string index 45 read GetIndexValue write SetIndexValue; // ֤
+ property header: string index 3 read GetIndexValue write SetIndexValue; // ͷ
+ property loginUrl: string index 4 read GetIndexValue write SetIndexValue; // ¼URL
+ property searchUrl: string index 6 read GetIndexValue write SetIndexValue; // ַ
+ property exploreUrl: string index 7 read GetIndexValue write SetIndexValue; // ֵַ
+
+ property ruleSearch_bookList: string index 8 read GetIndexValue write SetIndexValue;
+ property ruleSearch_name: string index 9 read GetIndexValue write SetIndexValue;
+ property ruleSearch_author: string index 10 read GetIndexValue write SetIndexValue;
+ property ruleSearch_kind: string index 11 read GetIndexValue write SetIndexValue;
+ property ruleSearch_wordCount: string index 12 read GetIndexValue write SetIndexValue;
+ property ruleSearch_lastChapter: string index 13 read GetIndexValue write SetIndexValue;
+ property ruleSearch_intro: string index 14 read GetIndexValue write SetIndexValue;
+ property ruleSearch_coverUrl: string index 15 read GetIndexValue write SetIndexValue;
+ property ruleSearch_bookUrl: string index 16 read GetIndexValue write SetIndexValue;
+
+ property ruleExplore_bookList: string index 17 read GetIndexValue write SetIndexValue;
+ property ruleExplore_name: string index 18 read GetIndexValue write SetIndexValue;
+ property ruleExplore_author: string index 19 read GetIndexValue write SetIndexValue;
+ property ruleExplore_kind: string index 20 read GetIndexValue write SetIndexValue;
+ property ruleExplore_wordCount: string index 21 read GetIndexValue write SetIndexValue;
+ property ruleExplore_lastChapter: string index 22 read GetIndexValue write SetIndexValue;
+ property ruleExplore_intro: string index 23 read GetIndexValue write SetIndexValue;
+ property ruleExplore_coverUrl: string index 24 read GetIndexValue write SetIndexValue;
+ property ruleExplore_bookUrl: string index 25 read GetIndexValue write SetIndexValue;
+
+ property ruleBookInfo_init: string index 26 read GetIndexValue write SetIndexValue;
+ property ruleBookInfo_name: string index 27 read GetIndexValue write SetIndexValue;
+ property ruleBookInfo_author: string index 28 read GetIndexValue write SetIndexValue;
+ property ruleBookInfo_kind: string index 29 read GetIndexValue write SetIndexValue;
+ property ruleBookInfo_wordCount: string index 30 read GetIndexValue write SetIndexValue;
+ property ruleBookInfo_lastChapter: string index 31 read GetIndexValue write SetIndexValue;
+ property ruleBookInfo_intro: string index 32 read GetIndexValue write SetIndexValue;
+ property ruleBookInfo_coverUrl: string index 33 read GetIndexValue write SetIndexValue;
+ property ruleBookInfo_tocUrl: string index 34 read GetIndexValue write SetIndexValue;
+
+ property ruleToc_chapterList: string index 35 read GetIndexValue write SetIndexValue;
+ property ruleToc_chapterName: string index 36 read GetIndexValue write SetIndexValue;
+ property ruleToc_chapterUrl: string index 37 read GetIndexValue write SetIndexValue;
+ property ruleToc_isVip: string index 38 read GetIndexValue write SetIndexValue;
+ property ruleToc_updateTime: string index 39 read GetIndexValue write SetIndexValue;
+ property ruleToc_nextTocUrl: string index 40 read GetIndexValue write SetIndexValue;
+
+ property ruleContent_content: string index 41 read GetIndexValue write SetIndexValue;
+ property ruleContent_nextContentUrl: string index 42 read GetIndexValue write SetIndexValue;
+ property ruleContent_webJs: string index 43 read GetIndexValue write SetIndexValue;
+ property ruleContent_sourceRegex: string index 44 read GetIndexValue write SetIndexValue;
+
+ property lastUpdateTime: Int64 read GetLastUpdateTime write SetLastUpdateTime; // ʱ
+ property enable: Boolean read GetEnable write SetEnable;
+ property enabledExplore: Boolean read GetEnabledExplore write SetEnabledExplore;
+ property customOrder: Integer read GetSerialNumber write SetSerialNumber;
+ property weight: Integer read GetWeight write SetWeight;
+ property score: Integer read GetScore write SetScore; //
+ end;
+
+implementation
+
+{ TBookSourceItem }
+
+const
+ SKeyArray: array [0..45] of string = (
+ 'bookSourceGroup',
+ 'bookSourceName',
+ 'bookSourceUrl',
+ 'header',
+ 'loginUrl',
+ 'bookSourceType',
+ 'searchUrl',
+ 'exploreUrl',
+
+ 'ruleSearch_bookList',
+ 'ruleSearch_name',
+ 'ruleSearch_author',
+ 'ruleSearch_kind',
+ 'ruleSearch_wordCount',
+ 'ruleSearch_lastChapter',
+ 'ruleSearch_intro',
+ 'ruleSearch_coverUrl',
+ 'ruleSearch_bookUrl',
+
+ 'ruleExplore_bookList',
+ 'ruleExplore_name',
+ 'ruleExplore_author',
+ 'ruleExplore_kind',
+ 'ruleExplore_wordCount',
+ 'ruleExplore_lastChapter',
+ 'ruleExplore_intro',
+ 'ruleExplore_coverUrl',
+ 'ruleExplore_bookUrl',
+
+ 'ruleBookInfo_init',
+ 'ruleBookInfo_name',
+ 'ruleBookInfo_author',
+ 'ruleBookInfo_kind',
+ 'ruleBookInfo_wordCount',
+ 'ruleBookInfo_lastChapter',
+ 'ruleBookInfo_intro',
+ 'ruleBookInfo_coverUrl',
+ 'ruleBookInfo_tocUrl',
+
+ 'ruleToc_chapterList',
+ 'ruleToc_chapterName',
+ 'ruleToc_chapterUrl',
+ 'ruleToc_isVip',
+ 'ruleToc_updateTime',
+ 'ruleToc_nextTocUrl',
+
+ 'ruleContent_content',
+ 'ruleContent_nextContentUrl',
+ 'ruleContent_webJs',
+ 'ruleContent_sourceRegex',
+
+ 'bookUrlPattern'
+ );
+ SEnabled = 'enable';
+ SEnabledExplore = 'enabledExplore';
+ SSerialNumber = 'customOrder';
+ SWeight = 'weight';
+
+procedure TBookSourceItem.AddGroup(const Name: string);
+var
+ S: string;
+ List: TArray;
+ I: Integer;
+begin
+ S := Trim(bookSourceGroup);
+ if S = '' then
+ bookSourceGroup := Name
+ else begin
+ List := GetGroupList();
+ for I := Low(List) to High(List) do begin
+ if Trim(List[I]) = Name then
+ Exit;
+ end;
+ bookSourceGroup := bookSourceGroup + '; ' + Name;
+ end;
+end;
+
+function TBookSourceItem.GetBookSourceType: Integer;
+begin
+ Result := Self.I[SKeyArray[5]];
+end;
+
+function TBookSourceItem.GetEnable: Boolean;
+begin
+ Result := Self.B[SEnabled];
+end;
+
+function TBookSourceItem.GetEnabledExplore: Boolean;
+begin
+ Result := Self.B[SEnabledExplore];
+end;
+
+function TBookSourceItem.GetGroupList: TArray;
+var
+ S: string;
+begin
+ S := Trim(bookSourceGroup);
+ Result := S.Split([',', ';', ':', '', '']);
+end;
+
+function TBookSourceItem.GetIndexValue(const Index: Integer): string;
+begin
+ Result := GetStr(SKeyArray[Index]);
+end;
+
+function TBookSourceItem.GetLastUpdateTime: Int64;
+begin
+ Result := Self.I['lastUpdateTime'];
+end;
+
+function TBookSourceItem.GetScore: Integer;
+begin
+ Result := Self.I['score'];
+end;
+
+function TBookSourceItem.GetSerialNumber: Integer;
+begin
+ Result := Self.I[SSerialNumber];
+end;
+
+function TBookSourceItem.GetStr(const AKey: string): string;
+var
+ Key, PKey: string;
+ J: Integer;
+ Json: JSONObject;
+begin
+ Key := AKey;
+ J := Key.IndexOf('_');
+ if J > 0 then begin
+ PKey := Key.Substring(0, J);
+ Json := Self.O[PKey];
+ if Json = nil then
+ Result := ''
+ else begin
+ Key := Key.Substring(J + 1);
+ Result := Json.S[Key];
+ end;
+ end else
+ Result := Self.S[Key];
+end;
+
+function TBookSourceItem.GetWeight: Integer;
+begin
+ Result := SElf.I[SWeight];
+end;
+
+procedure TBookSourceItem.RemoveGroup(const Name: string);
+var
+ S: string;
+ List: TArray;
+ I, J: Integer;
+ SB: TStringBuilder;
+begin
+ S := Trim(bookSourceGroup);
+ if S <> '' then begin
+ J := 0;
+ List := GetGroupList();
+ SB := TStringBuilder.Create(Length(bookSourceGroup) * 2);
+ for I := Low(List) to High(List) do begin
+ if Trim(List[I]) <> Name then begin
+ if J > 0 then
+ SB.Append('; ');
+ SB.Append(Trim(List[I]));
+ Inc(J);
+ end;
+ end;
+ bookSourceGroup := SB.ToString;
+ end;
+end;
+
+procedure TBookSourceItem.ReplaceGroup(const Name, NewName: string);
+var
+ S: string;
+ List: TArray;
+ I, J: Integer;
+ SB: TStringBuilder;
+begin
+ S := Trim(bookSourceGroup);
+ if S <> '' then begin
+ J := 0;
+ List := GetGroupList();
+ SB := TStringBuilder.Create(Length(bookSourceGroup) * 2);
+ for I := Low(List) to High(List) do begin
+ if Trim(List[I]) <> Name then begin
+ if J > 0 then
+ SB.Append('; ');
+ SB.Append(Trim(List[I]));
+ Inc(J);
+ end else if NewName <> '' then begin
+ if J > 0 then
+ SB.Append('; ');
+ SB.Append(Trim(NewName));
+ Inc(J);
+ end;
+ end;
+ bookSourceGroup := SB.ToString;
+ end;
+end;
+
+procedure TBookSourceItem.SetBookSourceType(const Value: Integer);
+begin
+ Self.I[SKeyArray[5]] := Value;
+end;
+
+procedure TBookSourceItem.SetEnable(const Value: Boolean);
+begin
+ Self.B[SEnabled] := Value;
+end;
+
+procedure TBookSourceItem.SetEnabledExplore(const Value: Boolean);
+begin
+ Self.B[SEnabledExplore] := Value;
+end;
+
+procedure TBookSourceItem.SetIndexValue(const Index: Integer;
+ const Value: string);
+begin
+ SetStrValue(SKeyArray[Index], Value);
+end;
+
+procedure TBookSourceItem.SetStrValue(const AKey, Value: string);
+var
+ Key, PKey: string;
+ J: Integer;
+ Json: JSONObject;
+begin
+ Key := AKey;
+ J := Key.IndexOf('_');
+ if J > 0 then begin
+ PKey := Key.Substring(0, J);
+ Json := Self.O[PKey];
+ if Json = nil then
+ Json := Self.AddChildObject(PKey);
+ Key := Key.Substring(J + 1);
+ Json.S[Key] := Value;
+ end else
+ Self.S[Key] := Value;
+end;
+
+procedure TBookSourceItem.SetLastUpdateTime(const Value: Int64);
+begin
+ Self.I['lastUpdateTime'] := Value;
+end;
+
+procedure TBookSourceItem.SetScore(const Value: Integer);
+begin
+ Self.I['score'] := Value;
+end;
+
+procedure TBookSourceItem.SetSerialNumber(const Value: Integer);
+begin
+ Self.I[SSerialNumber] := Value;
+end;
+
+procedure TBookSourceItem.SetWeight(const Value: Integer);
+begin
+ Self.I[SWeight] := Value;
+end;
+
+end.
diff --git a/tool/书源整理工具/uBookSourceBean20.pas b/tool/书源整理工具/uBookSourceBean20.pas
new file mode 100644
index 000000000..21ec57d59
--- /dev/null
+++ b/tool/书源整理工具/uBookSourceBean20.pas
@@ -0,0 +1,247 @@
+unit uBookSourceBean20;
+
+interface
+
+uses
+ YxdJson, Classes, SysUtils, Math;
+
+type
+ TBookSource20Item = class(JSONObject)
+ private
+ function GetIndexValue(const Index: Integer): string;
+ procedure SetIndexValue(const Index: Integer; const Value: string);
+ function GetEnable: Boolean;
+ function GetSerialNumber: Integer;
+ function GetWeight: Integer;
+ procedure SetEnable(const Value: Boolean);
+ procedure SetSerialNumber(const Value: Integer);
+ procedure SetWeight(const Value: Integer);
+ function GetLastUpdateTime: Int64;
+ procedure SetLastUpdateTime(const Value: Int64);
+ function GetScore: Integer;
+ procedure SetScore(const Value: Integer);
+ public
+ procedure AddGroup(const Name: string);
+ procedure RemoveGroup(const Name: string);
+ procedure ReplaceGroup(const Name, NewName: string);
+ function GetGroupList(): TArray;
+
+ property bookSourceType: string index 28 read GetIndexValue write SetIndexValue; // Դ
+ property bookSourceGroup: string index 0 read GetIndexValue write SetIndexValue; // Դ
+ property bookSourceName: string index 1 read GetIndexValue write SetIndexValue; // Դ
+ property bookSourceUrl: string index 2 read GetIndexValue write SetIndexValue; // ԴURL
+ property httpUserAgent: string index 3 read GetIndexValue write SetIndexValue; // HttpUserAgent
+ property loginUrl: string index 4 read GetIndexValue write SetIndexValue; // ¼URL
+ property ruleBookAuthor: string index 5 read GetIndexValue write SetIndexValue; // ߹
+ property ruleBookContent: string index 6 read GetIndexValue write SetIndexValue; // Ĺ
+ property ruleBookKind: string index 7 read GetIndexValue write SetIndexValue; //
+ property ruleBookLastChapter: string index 8 read GetIndexValue write SetIndexValue; // ½ڹ
+ property ruleBookName: string index 9 read GetIndexValue write SetIndexValue; //
+ property ruleBookUrlPattern: string index 10 read GetIndexValue write SetIndexValue; // 鼮URL
+ property ruleChapterList: string index 11 read GetIndexValue write SetIndexValue; // Ŀ¼б
+ property ruleChapterName: string index 12 read GetIndexValue write SetIndexValue; // ½ƹ
+ property ruleChapterUrl: string index 13 read GetIndexValue write SetIndexValue; // Ŀ¼URL
+ property ruleChapterUrlNext: string index 14 read GetIndexValue write SetIndexValue; // Ŀ¼һҳUrl
+ property ruleContentUrl: string index 15 read GetIndexValue write SetIndexValue; // ½URL
+ property ruleContentUrlNext: string index 16 read GetIndexValue write SetIndexValue; // һҳURL
+ property ruleCoverUrl: string index 17 read GetIndexValue write SetIndexValue; //
+ property ruleFindUrl: string index 18 read GetIndexValue write SetIndexValue; // ֹ
+ property ruleIntroduce: string index 19 read GetIndexValue write SetIndexValue; //
+ property ruleSearchAuthor: string index 20 read GetIndexValue write SetIndexValue; // ߹
+ property ruleSearchCoverUrl: string index 21 read GetIndexValue write SetIndexValue; //
+ property ruleSearchKind: string index 22 read GetIndexValue write SetIndexValue; //
+ property ruleSearchLastChapter: string index 23 read GetIndexValue write SetIndexValue; // ½ڹ
+ property ruleSearchList: string index 24 read GetIndexValue write SetIndexValue; // б
+ property ruleSearchName: string index 25 read GetIndexValue write SetIndexValue; //
+ property ruleSearchNoteUrl: string index 26 read GetIndexValue write SetIndexValue; // 鼮URL
+ property ruleSearchUrl: string index 27 read GetIndexValue write SetIndexValue; // ַ
+
+ property lastUpdateTime: Int64 read GetLastUpdateTime write SetLastUpdateTime; // ʱ
+ property enable: Boolean read GetEnable write SetEnable;
+ property serialNumber: Integer read GetSerialNumber write SetSerialNumber;
+ property weight: Integer read GetWeight write SetWeight;
+ property score: Integer read GetScore write SetScore; //
+ end;
+
+implementation
+
+{ TBookSourceItem }
+
+const
+ SKeyArray: array [0..28] of string = (
+ 'bookSourceGroup',
+ 'bookSourceName',
+ 'bookSourceUrl',
+ 'httpUserAgent',
+ 'loginUrl',
+ 'ruleBookAuthor',
+ 'ruleBookContent',
+ 'ruleBookKind',
+ 'ruleBookLastChapter',
+ 'ruleBookName',
+ 'ruleBookUrlPattern',
+ 'ruleChapterList',
+ 'ruleChapterName',
+ 'ruleChapterUrl',
+ 'ruleChapterUrlNext',
+ 'ruleContentUrl',
+ 'ruleContentUrlNext',
+ 'ruleCoverUrl',
+ 'ruleFindUrl',
+ 'ruleIntroduce',
+ 'ruleSearchAuthor',
+ 'ruleSearchCoverUrl',
+ 'ruleSearchKind',
+ 'ruleSearchLastChapter',
+ 'ruleSearchList',
+ 'ruleSearchName',
+ 'ruleSearchNoteUrl',
+ 'ruleSearchUrl',
+ 'bookSourceType'
+ );
+ SEnabled = 'enable';
+ SSerialNumber = 'serialNumber';
+ SWeight = 'weight';
+
+procedure TBookSource20Item.AddGroup(const Name: string);
+var
+ S: string;
+ List: TArray;
+ I: Integer;
+begin
+ S := Trim(bookSourceGroup);
+ if S = '' then
+ bookSourceGroup := Name
+ else begin
+ List := GetGroupList();
+ for I := Low(List) to High(List) do begin
+ if Trim(List[I]) = Name then
+ Exit;
+ end;
+ bookSourceGroup := bookSourceGroup + '; ' + Name;
+ end;
+end;
+
+function TBookSource20Item.GetEnable: Boolean;
+begin
+ Result := Self.B[SEnabled];
+end;
+
+function TBookSource20Item.GetGroupList: TArray;
+var
+ S: string;
+begin
+ S := Trim(bookSourceGroup);
+ Result := S.Split([',', ';', ':', '', '']);
+end;
+
+function TBookSource20Item.GetIndexValue(const Index: Integer): string;
+begin
+ Result := Self.S[SKeyArray[Index]];
+end;
+
+function TBookSource20Item.GetLastUpdateTime: Int64;
+begin
+ Result := Self.I['lastUpdateTime'];
+end;
+
+function TBookSource20Item.GetScore: Integer;
+begin
+ Result := Self.I['score'];
+end;
+
+function TBookSource20Item.GetSerialNumber: Integer;
+begin
+ Result := Self.I[SSerialNumber];
+end;
+
+function TBookSource20Item.GetWeight: Integer;
+begin
+ Result := SElf.I[SWeight];
+end;
+
+procedure TBookSource20Item.RemoveGroup(const Name: string);
+var
+ S: string;
+ List: TArray;
+ I, J: Integer;
+ SB: TStringBuilder;
+begin
+ S := Trim(bookSourceGroup);
+ if S <> '' then begin
+ J := 0;
+ List := GetGroupList();
+ SB := TStringBuilder.Create(Length(bookSourceGroup) * 2);
+ for I := Low(List) to High(List) do begin
+ if Trim(List[I]) <> Name then begin
+ if J > 0 then
+ SB.Append('; ');
+ SB.Append(Trim(List[I]));
+ Inc(J);
+ end;
+ end;
+ bookSourceGroup := SB.ToString;
+ end;
+end;
+
+procedure TBookSource20Item.ReplaceGroup(const Name, NewName: string);
+var
+ S: string;
+ List: TArray;
+ I, J: Integer;
+ SB: TStringBuilder;
+begin
+ S := Trim(bookSourceGroup);
+ if S <> '' then begin
+ J := 0;
+ List := GetGroupList();
+ SB := TStringBuilder.Create(Length(bookSourceGroup) * 2);
+ for I := Low(List) to High(List) do begin
+ if Trim(List[I]) <> Name then begin
+ if J > 0 then
+ SB.Append('; ');
+ SB.Append(Trim(List[I]));
+ Inc(J);
+ end else if NewName <> '' then begin
+ if J > 0 then
+ SB.Append('; ');
+ SB.Append(Trim(NewName));
+ Inc(J);
+ end;
+ end;
+ bookSourceGroup := SB.ToString;
+ end;
+end;
+
+procedure TBookSource20Item.SetEnable(const Value: Boolean);
+begin
+ Self.B[SEnabled] := Value;
+end;
+
+procedure TBookSource20Item.SetIndexValue(const Index: Integer;
+ const Value: string);
+begin
+ Self.S[SKeyArray[Index]] := Value;
+end;
+
+procedure TBookSource20Item.SetLastUpdateTime(const Value: Int64);
+begin
+ Self.I['lastUpdateTime'] := Value;
+end;
+
+procedure TBookSource20Item.SetScore(const Value: Integer);
+begin
+ Self.I['score'] := Value;
+end;
+
+procedure TBookSource20Item.SetSerialNumber(const Value: Integer);
+begin
+ Self.I[SSerialNumber] := Value;
+end;
+
+procedure TBookSource20Item.SetWeight(const Value: Integer);
+begin
+ Self.I[SWeight] := Value;
+end;
+
+end.
diff --git a/tool/书源整理工具/uFrmEditSource.dfm b/tool/书源整理工具/uFrmEditSource.dfm
new file mode 100644
index 000000000..21a757976
--- /dev/null
+++ b/tool/书源整理工具/uFrmEditSource.dfm
@@ -0,0 +1,1397 @@
+object frmEditSource: TfrmEditSource
+ Left = 0
+ Top = 0
+ BorderIcons = [biSystemMenu]
+ Caption = #32534#36753#20070#28304
+ ClientHeight = 743
+ ClientWidth = 1198
+ Color = clBtnFace
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -12
+ Font.Name = 'Courier New'
+ Font.Style = []
+ OldCreateOrder = False
+ Position = poScreenCenter
+ OnClose = FormClose
+ OnCreate = FormCreate
+ OnMouseWheel = FormMouseWheel
+ OnResize = FormResize
+ OnShow = FormShow
+ PixelsPerInch = 96
+ TextHeight = 15
+ object Panel1: TPanel
+ Left = 0
+ Top = 700
+ Width = 1198
+ Height = 43
+ Align = alBottom
+ BevelOuter = bvNone
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = []
+ ParentFont = False
+ ShowCaption = False
+ TabOrder = 0
+ DesignSize = (
+ 1198
+ 43)
+ object Label29: TLabel
+ Left = 413
+ Top = 19
+ Width = 60
+ Height = 12
+ Caption = #25628#32034#26435#37325#65306
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clTeal
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = []
+ ParentFont = False
+ end
+ object Label30: TLabel
+ Left = 564
+ Top = 19
+ Width = 60
+ Height = 12
+ Caption = #25490#24207#32534#21495#65306
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clTeal
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = []
+ ParentFont = False
+ end
+ object Button1: TButton
+ Left = 1098
+ Top = 10
+ Width = 88
+ Height = 25
+ Anchors = [akTop, akRight]
+ Caption = #30830#23450'(&O)'
+ Default = True
+ TabOrder = 0
+ OnClick = Button1Click
+ end
+ object Button2: TButton
+ Left = 995
+ Top = 10
+ Width = 88
+ Height = 25
+ Anchors = [akTop, akRight]
+ Cancel = True
+ Caption = #21462#28040'(&C)'
+ ModalResult = 2
+ TabOrder = 1
+ OnClick = Button2Click
+ end
+ object CheckBox1: TCheckBox
+ Left = 12
+ Top = 16
+ Width = 113
+ Height = 17
+ Caption = #21551#29992#20070#28304
+ TabOrder = 2
+ end
+ object Edit27: TEdit
+ Left = 477
+ Top = 14
+ Width = 76
+ Height = 20
+ Hint = 'weight'
+ NumbersOnly = True
+ ParentShowHint = False
+ ShowHint = True
+ TabOrder = 3
+ Text = '0'
+ end
+ object Edit28: TEdit
+ Left = 627
+ Top = 14
+ Width = 87
+ Height = 20
+ Hint = 'weight'
+ NumbersOnly = True
+ ParentShowHint = False
+ ShowHint = True
+ TabOrder = 4
+ Text = '0'
+ end
+ object Button3: TButton
+ Left = 793
+ Top = 10
+ Width = 107
+ Height = 25
+ Anchors = [akTop, akRight]
+ Caption = #27979#35797#20070#28304'(&T)'
+ ModalResult = 2
+ TabOrder = 5
+ OnClick = Button3Click
+ end
+ object CheckBox2: TCheckBox
+ Left = 268
+ Top = 16
+ Width = 80
+ Height = 17
+ Caption = #38899#39057#28304
+ TabOrder = 6
+ OnClick = CheckBox2Click
+ end
+ object CheckBox3: TCheckBox
+ Left = 333
+ Top = 16
+ Width = 80
+ Height = 17
+ Caption = #22270#28304
+ TabOrder = 7
+ OnClick = CheckBox3Click
+ end
+ object CheckBox4: TCheckBox
+ Left = 90
+ Top = 12
+ Width = 87
+ Height = 25
+ Caption = #21551#29992#21457#29616
+ TabOrder = 8
+ end
+ object CheckBox5: TCheckBox
+ Left = 169
+ Top = 12
+ Width = 87
+ Height = 25
+ Caption = #21551#29992#25628#32034
+ TabOrder = 9
+ end
+ end
+ object ScrollBox1: TScrollBox
+ AlignWithMargins = True
+ Left = 3
+ Top = 3
+ Width = 1192
+ Height = 694
+ HorzScrollBar.Smooth = True
+ HorzScrollBar.Style = ssFlat
+ VertScrollBar.Smooth = True
+ VertScrollBar.Style = ssFlat
+ VertScrollBar.Tracking = True
+ Align = alClient
+ BevelInner = bvNone
+ BevelOuter = bvNone
+ BorderStyle = bsNone
+ Color = clWindow
+ Padding.Bottom = 8
+ ParentColor = False
+ TabOrder = 1
+ object Panel2: TPanel
+ Left = 0
+ Top = 175
+ Width = 1175
+ Height = 295
+ Hint = 'ruleSearch'
+ Align = alTop
+ BevelOuter = bvNone
+ Caption = 'Panel2'
+ ShowCaption = False
+ TabOrder = 1
+ DesignSize = (
+ 1175
+ 295)
+ object Label33: TLabel
+ Left = 9
+ Top = 7
+ Width = 151
+ Height = 16
+ Caption = #25628#32034' - ruleSearch'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clBlack
+ Font.Height = -16
+ Font.Name = #40657#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label6: TLabel
+ Left = 24
+ Top = 38
+ Width = 142
+ Height = 12
+ Caption = #25628#32034#22320#22336#65306'(searchUrl)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label7: TLabel
+ Left = 24
+ Top = 64
+ Width = 135
+ Height = 12
+ Caption = #21015#34920#35268#21017#65306'(bookList)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label8: TLabel
+ Left = 24
+ Top = 89
+ Width = 107
+ Height = 12
+ Caption = #20070#21517#35268#21017#65306'(name)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label9: TLabel
+ Left = 24
+ Top = 115
+ Width = 121
+ Height = 12
+ Caption = #20316#32773#35268#21017#65306'(author)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label10: TLabel
+ Left = 24
+ Top = 140
+ Width = 107
+ Height = 12
+ Caption = #20998#31867#35268#21017#65306'(kind)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label34: TLabel
+ Left = 24
+ Top = 165
+ Width = 142
+ Height = 12
+ Caption = #23383#25968#35268#21017#65306'(wordCount)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label11: TLabel
+ Left = 24
+ Top = 190
+ Width = 162
+ Height = 12
+ Caption = #26368#26032#31456#33410#35268#21017#65306'(lastChapter)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = []
+ ParentFont = False
+ end
+ object Label12: TLabel
+ Left = 24
+ Top = 215
+ Width = 135
+ Height = 12
+ Caption = #23553#38754#35268#21017#65306'(coverUrl)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label13: TLabel
+ Left = 24
+ Top = 241
+ Width = 128
+ Height = 12
+ Caption = #35814#24773#22320#22336#65306'(bookUrl)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label20: TLabel
+ Left = 24
+ Top = 267
+ Width = 114
+ Height = 12
+ Caption = #31616#20171#35268#21017#65306'(intro)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Edit18: TEdit
+ Left = 280
+ Top = 236
+ Width = 890
+ Height = 23
+ Hint = 'ruleSearch_bookUrl'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 8
+ TextHint = #36873#25321#33410#28857#20070#31821#35814#24773#39029#32593#22336
+ end
+ object Edit11: TEdit
+ Left = 280
+ Top = 262
+ Width = 890
+ Height = 23
+ Hint = 'ruleSearch_intro'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 9
+ TextHint = #36873#25321#20070#31821#31616#20171
+ end
+ object Edit10: TEdit
+ Left = 280
+ Top = 210
+ Width = 890
+ Height = 23
+ Hint = 'ruleSearch_coverUrl'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 7
+ TextHint = #36873#25321#33410#28857#20070#31821#23553#38754
+ end
+ object Edit9: TEdit
+ Left = 280
+ Top = 185
+ Width = 890
+ Height = 23
+ Hint = 'ruleSearch_lastChapter'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 6
+ TextHint = #36873#25321#33410#28857#26368#26032#31456#33410
+ end
+ object Edit30: TEdit
+ Left = 280
+ Top = 160
+ Width = 890
+ Height = 23
+ Hint = 'ruleSearch_wordCount'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 5
+ TextHint = #36873#25321#33410#28857#23383#25968#20449#24687
+ end
+ object Edit8: TEdit
+ Left = 280
+ Top = 135
+ Width = 890
+ Height = 23
+ Hint = 'ruleSearch_kind'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 4
+ TextHint = #36873#25321#33410#28857#20998#31867#20449#24687
+ end
+ object Edit7: TEdit
+ Left = 280
+ Top = 110
+ Width = 890
+ Height = 23
+ Hint = 'ruleSearch_author'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 3
+ TextHint = #36873#25321#33410#28857#20316#32773
+ end
+ object Edit6: TEdit
+ Left = 280
+ Top = 85
+ Width = 890
+ Height = 23
+ Hint = 'ruleSearch_name'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 2
+ TextHint = #36873#25321#33410#28857#20070#21517
+ end
+ object Edit5: TEdit
+ Left = 280
+ Top = 59
+ Width = 890
+ Height = 23
+ Hint = 'ruleSearch_bookList'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 1
+ TextHint = #36873#25321#20070#31821#33410#28857
+ end
+ object Edit4: TEdit
+ Left = 280
+ Top = 33
+ Width = 890
+ Height = 23
+ Hint = 'searchUrl'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 0
+ TextHint = '['#22495#21517#21487#30465#30053'] /search.php@kw={{key}}'
+ end
+ end
+ object Panel3: TPanel
+ Left = 0
+ Top = 470
+ Width = 1175
+ Height = 385
+ Hint = 'ruleExplore'
+ Align = alTop
+ BevelOuter = bvNone
+ Caption = 'Panel2'
+ ShowCaption = False
+ TabOrder = 2
+ DesignSize = (
+ 1175
+ 385)
+ object Label14: TLabel
+ Left = 9
+ Top = 7
+ Width = 160
+ Height = 16
+ Caption = #21457#29616' - ruleExplore'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clBlack
+ Font.Height = -16
+ Font.Name = #40657#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label15: TLabel
+ Left = 24
+ Top = 38
+ Width = 149
+ Height = 12
+ Caption = #21457#29616#22320#22336#65306'(exploreUrl)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label16: TLabel
+ Left = 24
+ Top = 156
+ Width = 135
+ Height = 12
+ Caption = #21015#34920#35268#21017#65306'(bookList)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label17: TLabel
+ Left = 24
+ Top = 181
+ Width = 107
+ Height = 12
+ Caption = #20070#21517#35268#21017#65306'(name)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label18: TLabel
+ Left = 24
+ Top = 207
+ Width = 121
+ Height = 12
+ Caption = #20316#32773#35268#21017#65306'(author)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label19: TLabel
+ Left = 24
+ Top = 232
+ Width = 107
+ Height = 12
+ Caption = #20998#31867#35268#21017#65306'(kind)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label21: TLabel
+ Left = 24
+ Top = 257
+ Width = 142
+ Height = 12
+ Caption = #23383#25968#35268#21017#65306'(wordCount)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label22: TLabel
+ Left = 24
+ Top = 282
+ Width = 162
+ Height = 12
+ Caption = #26368#26032#31456#33410#35268#21017#65306'(lastChapter)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = []
+ ParentFont = False
+ end
+ object Label23: TLabel
+ Left = 24
+ Top = 307
+ Width = 135
+ Height = 12
+ Caption = #23553#38754#35268#21017#65306'(coverUrl)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label24: TLabel
+ Left = 24
+ Top = 333
+ Width = 128
+ Height = 12
+ Caption = #35814#24773#22320#22336#65306'(bookUrl)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label25: TLabel
+ Left = 24
+ Top = 359
+ Width = 114
+ Height = 12
+ Caption = #31616#20171#35268#21017#65306'(intro)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label2: TLabel
+ Left = 32
+ Top = 70
+ Width = 240
+ Height = 60
+ Caption =
+ #20869#23481#33021#26174#31034#22312#21457#29616#33756#21333#13#10#27599#34892#19968#26465#21457#29616#20998#31867#65288#32593#22336#22495#21517#21487#30465#30053#65289#65292#20363#65306#13#10#21517#31216'1::'#32593#22336'(Url)1'#13#10#21517#31216'2::'#32593#22336'(Url)2'#13#10 +
+ '...'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGray
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = []
+ ParentFont = False
+ end
+ object Edit12: TEdit
+ Left = 280
+ Top = 328
+ Width = 890
+ Height = 23
+ Hint = 'ruleExplore_bookUrl'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 8
+ TextHint = #36873#25321#33410#28857#20070#31821#35814#24773#39029#32593#22336
+ end
+ object Edit13: TEdit
+ Left = 280
+ Top = 354
+ Width = 890
+ Height = 23
+ Hint = 'ruleExplore_intro'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 9
+ TextHint = #36873#25321#20070#31821#31616#20171
+ end
+ object Edit14: TEdit
+ Left = 280
+ Top = 302
+ Width = 890
+ Height = 23
+ Hint = 'ruleExplore_coverUrl'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 7
+ TextHint = #36873#25321#33410#28857#20070#31821#23553#38754
+ end
+ object Edit15: TEdit
+ Left = 280
+ Top = 277
+ Width = 890
+ Height = 23
+ Hint = 'ruleExplore_lastChapter'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 6
+ TextHint = #36873#25321#33410#28857#26368#26032#31456#33410
+ end
+ object Edit16: TEdit
+ Left = 280
+ Top = 252
+ Width = 890
+ Height = 23
+ Hint = 'ruleExplore_wordCount'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 5
+ TextHint = #36873#25321#33410#28857#23383#25968#20449#24687
+ end
+ object Edit17: TEdit
+ Left = 280
+ Top = 227
+ Width = 890
+ Height = 23
+ Hint = 'ruleExplore_kind'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 4
+ TextHint = #36873#25321#33410#28857#20998#31867#20449#24687
+ end
+ object Edit19: TEdit
+ Left = 280
+ Top = 202
+ Width = 890
+ Height = 23
+ Hint = 'ruleExplore_author'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 3
+ TextHint = #36873#25321#33410#28857#20316#32773
+ end
+ object Edit20: TEdit
+ Left = 280
+ Top = 176
+ Width = 890
+ Height = 23
+ Hint = 'ruleExplore_name'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 2
+ TextHint = #36873#25321#33410#28857#20070#21517
+ end
+ object Edit21: TEdit
+ Left = 280
+ Top = 151
+ Width = 890
+ Height = 23
+ Hint = 'ruleExplore_bookList'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 1
+ TextHint = #36873#25321#20070#31821#33410#28857
+ end
+ object Memo1: TMemo
+ Left = 280
+ Top = 33
+ Width = 890
+ Height = 115
+ Hint = 'exploreUrl'
+ Margins.Bottom = 8
+ Anchors = [akLeft, akTop, akRight]
+ ScrollBars = ssVertical
+ TabOrder = 0
+ end
+ end
+ object Panel4: TPanel
+ Left = 0
+ Top = 855
+ Width = 1175
+ Height = 270
+ Hint = 'ruleSearch'
+ Align = alTop
+ BevelOuter = bvNone
+ Caption = 'Panel2'
+ ShowCaption = False
+ TabOrder = 3
+ DesignSize = (
+ 1175
+ 270)
+ object Label26: TLabel
+ Left = 9
+ Top = 7
+ Width = 169
+ Height = 16
+ Caption = #35814#24773' - ruleBookInfo'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clBlack
+ Font.Height = -16
+ Font.Name = #40657#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label27: TLabel
+ Left = 24
+ Top = 38
+ Width = 120
+ Height = 12
+ Caption = #39044#22788#29702#35268#21017#65306'(init)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label35: TLabel
+ Left = 24
+ Top = 64
+ Width = 107
+ Height = 12
+ Caption = #20070#21517#35268#21017#65306'(name)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label36: TLabel
+ Left = 24
+ Top = 90
+ Width = 121
+ Height = 12
+ Caption = #20316#32773#35268#21017#65306'(author)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label37: TLabel
+ Left = 24
+ Top = 115
+ Width = 107
+ Height = 12
+ Caption = #20998#31867#35268#21017#65306'(kind)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label38: TLabel
+ Left = 24
+ Top = 140
+ Width = 142
+ Height = 12
+ Caption = #23383#25968#35268#21017#65306'(wordCount)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label39: TLabel
+ Left = 24
+ Top = 165
+ Width = 162
+ Height = 12
+ Caption = #26368#26032#31456#33410#35268#21017#65306'(lastChapter)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = []
+ ParentFont = False
+ end
+ object Label40: TLabel
+ Left = 24
+ Top = 190
+ Width = 135
+ Height = 12
+ Caption = #23553#38754#35268#21017#65306'(coverUrl)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label41: TLabel
+ Left = 24
+ Top = 216
+ Width = 121
+ Height = 12
+ Caption = #30446#24405#22320#22336#65306'(tocUrl)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label42: TLabel
+ Left = 24
+ Top = 242
+ Width = 114
+ Height = 12
+ Caption = #31616#20171#35268#21017#65306'(intro)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Edit22: TEdit
+ Left = 280
+ Top = 211
+ Width = 890
+ Height = 23
+ Hint = 'ruleBookInfo_tocUrl'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 7
+ TextHint = #36873#25321#33410#28857#20070#31821#30446#24405#21015#34920#22320#22336
+ end
+ object Edit23: TEdit
+ Left = 280
+ Top = 237
+ Width = 890
+ Height = 23
+ Hint = 'ruleBookInfo_intro'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 8
+ TextHint = #36873#25321#20070#31821#31616#20171
+ end
+ object Edit24: TEdit
+ Left = 280
+ Top = 185
+ Width = 890
+ Height = 23
+ Hint = 'ruleBookInfo_coverUrl'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 6
+ TextHint = #36873#25321#33410#28857#20070#31821#23553#38754
+ end
+ object Edit25: TEdit
+ Left = 280
+ Top = 160
+ Width = 890
+ Height = 23
+ Hint = 'ruleBookInfo_lastChapter'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 5
+ TextHint = #36873#25321#33410#28857#26368#26032#31456#33410
+ end
+ object Edit26: TEdit
+ Left = 280
+ Top = 135
+ Width = 890
+ Height = 23
+ Hint = 'ruleBookInfo_wordCount'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 4
+ TextHint = #36873#25321#33410#28857#23383#25968#20449#24687
+ end
+ object Edit31: TEdit
+ Left = 280
+ Top = 110
+ Width = 890
+ Height = 23
+ Hint = 'ruleBookInfo_kind'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 3
+ TextHint = #36873#25321#33410#28857#20998#31867#20449#24687
+ end
+ object Edit32: TEdit
+ Left = 280
+ Top = 85
+ Width = 890
+ Height = 23
+ Hint = 'ruleBookInfo_author'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 2
+ TextHint = #36873#25321#33410#28857#20316#32773
+ end
+ object Edit33: TEdit
+ Left = 280
+ Top = 59
+ Width = 890
+ Height = 23
+ Hint = 'ruleBookInfo_name'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 1
+ TextHint = #36873#25321#33410#28857#20070#21517
+ end
+ object Edit35: TEdit
+ Left = 280
+ Top = 33
+ Width = 890
+ Height = 23
+ Hint = 'ruleBookInfo_init'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 0
+ TextHint = #29992#20110#21152#36895#35814#24773#20449#24687#26816#32034#65292#21482#25903#25345'AllInOne'#35268#21017
+ end
+ end
+ object Panel6: TPanel
+ Left = 0
+ Top = 0
+ Width = 1175
+ Height = 175
+ Align = alTop
+ BevelOuter = bvNone
+ ShowCaption = False
+ TabOrder = 0
+ DesignSize = (
+ 1175
+ 175)
+ object Label32: TLabel
+ Left = 8
+ Top = 121
+ Width = 222
+ Height = 12
+ Caption = #35831#27714#22836#65306'(header) '#65288#33258#23450#20041#23458#25143#31471#26631#35782#65289
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clTeal
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = []
+ ParentFont = False
+ end
+ object Label5: TLabel
+ Left = 8
+ Top = 94
+ Width = 114
+ Height = 12
+ Caption = #30331#24405'URL'#65306'(loginUrl)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clTeal
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = []
+ ParentFont = False
+ end
+ object Label31: TLabel
+ Left = 8
+ Top = 67
+ Width = 174
+ Height = 12
+ Caption = #20070#31821'URL'#27491#21017#65306'(bookUrlPattern)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clTeal
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = []
+ ParentFont = False
+ end
+ object Label4: TLabel
+ Left = 8
+ Top = 40
+ Width = 165
+ Height = 12
+ Caption = #20070#28304'URL'#65306'(bookSourceUrl)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clRed
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label3: TLabel
+ Left = 8
+ Top = 13
+ Width = 177
+ Height = 12
+ Caption = #20070#28304#21517#31216#65306'(bookSourceName)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clRed
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label1: TLabel
+ Left = 588
+ Top = 13
+ Width = 184
+ Height = 12
+ Caption = #20998#32452#21517#31216#65306'(bookSourceGroup)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Memo2: TMemo
+ Left = 280
+ Top = 116
+ Width = 890
+ Height = 53
+ Hint = 'header'
+ Margins.Bottom = 8
+ Anchors = [akLeft, akTop, akRight]
+ ParentShowHint = False
+ ScrollBars = ssVertical
+ ShowHint = False
+ TabOrder = 4
+ end
+ object Edit3: TEdit
+ Left = 280
+ Top = 89
+ Width = 890
+ Height = 23
+ Hint = 'loginUrl'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 3
+ TextHint = #22635#20889#32593#31449#30331#24405#32593#22336#65292#20165#22312#38656#35201#30331#24405#30340#20070#28304#26377#29992
+ end
+ object Edit2: TEdit
+ Left = 280
+ Top = 35
+ Width = 890
+ Height = 23
+ Hint = 'bookSourceUrl'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 1
+ TextHint = #65288#24517#22635#65289#36890#24120#22635#20889#32593#31449#20027#39029
+ end
+ object Edit1: TEdit
+ Left = 280
+ Top = 8
+ Width = 290
+ Height = 23
+ Hint = 'bookSourceName'
+ TabOrder = 0
+ TextHint = #65288#24517#22635#65289#26174#31034#22312#20070#28304#21015#34920
+ end
+ object ComboBox1: TComboBox
+ Left = 778
+ Top = 8
+ Width = 392
+ Height = 23
+ Hint = 'bookSourceGroup'
+ Anchors = [akLeft, akTop, akRight]
+ DropDownCount = 20
+ TabOrder = 5
+ end
+ object Edit29: TEdit
+ Left = 280
+ Top = 62
+ Width = 890
+ Height = 23
+ Hint = 'bookUrlPattern'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 2
+ TextHint = #24403#35814#24773#39029'URL'#19982#28304'URL'#30340#22495#21517#19981#19968#33268#26102#26377#25928#65292#29992#20110#28155#21152#32593#22336
+ end
+ end
+ object Panel5: TPanel
+ Left = 0
+ Top = 1315
+ Width = 1175
+ Height = 195
+ Hint = 'ruleSearch'
+ Align = alTop
+ BevelOuter = bvNone
+ Caption = 'Panel2'
+ ShowCaption = False
+ TabOrder = 5
+ DesignSize = (
+ 1175
+ 195)
+ object Label28: TLabel
+ Left = 9
+ Top = 7
+ Width = 160
+ Height = 16
+ Caption = #27491#25991' - ruleContent'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clBlack
+ Font.Height = -16
+ Font.Name = #40657#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label43: TLabel
+ Left = 24
+ Top = 38
+ Width = 128
+ Height = 12
+ Caption = #27491#25991#35268#21017#65306'(content)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label44: TLabel
+ Left = 24
+ Top = 64
+ Width = 177
+ Height = 12
+ Caption = #32763#39029#35268#21017#65306'(nextContentUrl)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label45: TLabel
+ Left = 24
+ Top = 90
+ Width = 156
+ Height = 12
+ Caption = #36164#28304#27491#21017#65306'(sourceRegex)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label46: TLabel
+ Left = 24
+ Top = 115
+ Width = 114
+ Height = 12
+ Caption = #33050#26412#27880#20837#65306'(webJs)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Edit41: TEdit
+ Left = 280
+ Top = 85
+ Width = 890
+ Height = 23
+ Hint = 'ruleContent_sourceRegex'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 2
+ TextHint = #21305#37197#36164#28304#30340'URL'#29305#24449#65292#29992#20110#21957#25506
+ end
+ object Edit42: TEdit
+ Left = 280
+ Top = 59
+ Width = 890
+ Height = 23
+ Hint = 'ruleContent_nextContentUrl'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 1
+ TextHint = #36873#25321#19979#19968#20998#39029#65288#19981#26159#19979#19968#31456#65289#38142#25509#65288#35268#21017#32467#26524#20026'String'#31867#22411#30340'Url)'
+ end
+ object Edit43: TEdit
+ Left = 280
+ Top = 33
+ Width = 890
+ Height = 23
+ Hint = 'ruleContent_content'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 0
+ TextHint = #36873#25321#27491#25991#20869#23481
+ end
+ object Memo3: TMemo
+ Left = 280
+ Top = 112
+ Width = 890
+ Height = 53
+ Hint = 'ruleContent_webJs'
+ Margins.Bottom = 8
+ Anchors = [akLeft, akTop, akRight]
+ ParentShowHint = False
+ ScrollBars = ssVertical
+ ShowHint = False
+ TabOrder = 3
+ end
+ end
+ object Panel7: TPanel
+ Left = 0
+ Top = 1125
+ Width = 1175
+ Height = 190
+ Hint = 'ruleSearch'
+ Align = alTop
+ BevelOuter = bvNone
+ Caption = 'Panel2'
+ ShowCaption = False
+ TabOrder = 4
+ DesignSize = (
+ 1175
+ 190)
+ object Label48: TLabel
+ Left = 9
+ Top = 7
+ Width = 124
+ Height = 16
+ Caption = #30446#24405' - ruleToc'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clBlack
+ Font.Height = -16
+ Font.Name = #40657#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label49: TLabel
+ Left = 24
+ Top = 38
+ Width = 156
+ Height = 12
+ Caption = #21015#34920#35268#21017#65306'(chapterList)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label50: TLabel
+ Left = 24
+ Top = 64
+ Width = 156
+ Height = 12
+ Caption = #31456#33410#21517#31216#65306'(chapterName)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label51: TLabel
+ Left = 24
+ Top = 90
+ Width = 149
+ Height = 12
+ Caption = #31456#33410#22320#22336#65306'(chapterUrl)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label52: TLabel
+ Left = 24
+ Top = 115
+ Width = 114
+ Height = 12
+ Caption = #25910#36153#26631#35782#65306'(isVip)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label53: TLabel
+ Left = 24
+ Top = 140
+ Width = 149
+ Height = 12
+ Caption = #32763#39029#35268#21017#65306'(nextTocUrl)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Label47: TLabel
+ Left = 24
+ Top = 166
+ Width = 149
+ Height = 12
+ Caption = #31456#33410#20449#24687#65306'(updateTime)'
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clGreen
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Style = [fsBold]
+ ParentFont = False
+ end
+ object Edit34: TEdit
+ Left = 280
+ Top = 135
+ Width = 890
+ Height = 23
+ Hint = 'ruleToc_nextTocUrl'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 4
+ TextHint = #36873#25321#30446#24405#19979#19968#39029#30340#38142#25509#65288#35268#21017#32467#26524#20026' List '#25110' String'#65289
+ end
+ object Edit36: TEdit
+ Left = 280
+ Top = 110
+ Width = 890
+ Height = 23
+ Hint = 'ruleToc_isVip'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 3
+ TextHint = #31456#33410#26159#21542#20026'VIP'#31456#33410#65288#35268#21017#32467#26524#20026'Bool'#65289
+ end
+ object Edit37: TEdit
+ Left = 280
+ Top = 84
+ Width = 890
+ Height = 23
+ Hint = 'ruleToc_chapterUrl'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 2
+ TextHint = #36873#25321#31456#33410#38142#25509
+ end
+ object Edit38: TEdit
+ Left = 280
+ Top = 59
+ Width = 890
+ Height = 23
+ Hint = 'ruleToc_chapterName'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 1
+ TextHint = #36873#25321#31456#33410#21517#31216
+ end
+ object Edit44: TEdit
+ Left = 280
+ Top = 33
+ Width = 890
+ Height = 23
+ Hint = 'ruleToc_chapterList'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 0
+ TextHint = #36873#25321#30446#24405#21015#34920#30340#31456#33410#33410#28857#65288#35268#21017#32467#26524#20026'List'#65289
+ end
+ object Edit39: TEdit
+ Left = 280
+ Top = 161
+ Width = 890
+ Height = 23
+ Hint = 'ruleToc_updateTime'
+ Anchors = [akLeft, akTop, akRight]
+ TabOrder = 5
+ TextHint = #36873#25321#31456#33410#20449#24687
+ end
+ end
+ end
+end
diff --git a/tool/书源整理工具/uFrmEditSource.pas b/tool/书源整理工具/uFrmEditSource.pas
new file mode 100644
index 000000000..28b2e8846
--- /dev/null
+++ b/tool/书源整理工具/uFrmEditSource.pas
@@ -0,0 +1,668 @@
+unit uFrmEditSource;
+
+interface
+
+uses
+ uBookSourceBean, YxdJson, RegularExpressions,
+ Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
+ Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
+
+type
+ TNotifyEventA = reference to procedure (Item: TBookSourceItem);
+
+ TfrmEditSource = class(TForm)
+ Panel1: TPanel;
+ Button1: TButton;
+ Button2: TButton;
+ CheckBox1: TCheckBox;
+ Label29: TLabel;
+ Edit27: TEdit;
+ Label30: TLabel;
+ Edit28: TEdit;
+ Button3: TButton;
+ CheckBox2: TCheckBox;
+ CheckBox3: TCheckBox;
+ ScrollBox1: TScrollBox;
+ Panel2: TPanel;
+ Label33: TLabel;
+ Label6: TLabel;
+ Label7: TLabel;
+ Label8: TLabel;
+ Label9: TLabel;
+ Label10: TLabel;
+ Label34: TLabel;
+ Label11: TLabel;
+ Label12: TLabel;
+ Label13: TLabel;
+ Label20: TLabel;
+ Edit18: TEdit;
+ Edit11: TEdit;
+ Edit10: TEdit;
+ Edit9: TEdit;
+ Edit30: TEdit;
+ Edit8: TEdit;
+ Edit7: TEdit;
+ Edit6: TEdit;
+ Edit5: TEdit;
+ Edit4: TEdit;
+ Panel3: TPanel;
+ Label14: TLabel;
+ Label15: TLabel;
+ Label16: TLabel;
+ Label17: TLabel;
+ Label18: TLabel;
+ Label19: TLabel;
+ Label21: TLabel;
+ Label22: TLabel;
+ Label23: TLabel;
+ Label24: TLabel;
+ Label25: TLabel;
+ Label2: TLabel;
+ Edit12: TEdit;
+ Edit13: TEdit;
+ Edit14: TEdit;
+ Edit15: TEdit;
+ Edit16: TEdit;
+ Edit17: TEdit;
+ Edit19: TEdit;
+ Edit20: TEdit;
+ Edit21: TEdit;
+ Memo1: TMemo;
+ Panel4: TPanel;
+ Label26: TLabel;
+ Label27: TLabel;
+ Label35: TLabel;
+ Label36: TLabel;
+ Label37: TLabel;
+ Label38: TLabel;
+ Label39: TLabel;
+ Label40: TLabel;
+ Label41: TLabel;
+ Label42: TLabel;
+ Edit22: TEdit;
+ Edit23: TEdit;
+ Edit24: TEdit;
+ Edit25: TEdit;
+ Edit26: TEdit;
+ Edit31: TEdit;
+ Edit32: TEdit;
+ Edit33: TEdit;
+ Edit35: TEdit;
+ Panel6: TPanel;
+ Label32: TLabel;
+ Label5: TLabel;
+ Label31: TLabel;
+ Label4: TLabel;
+ Label3: TLabel;
+ Label1: TLabel;
+ Memo2: TMemo;
+ Edit3: TEdit;
+ Edit2: TEdit;
+ Edit1: TEdit;
+ ComboBox1: TComboBox;
+ Edit29: TEdit;
+ Panel5: TPanel;
+ Label28: TLabel;
+ Label43: TLabel;
+ Label44: TLabel;
+ Label45: TLabel;
+ Label46: TLabel;
+ Edit41: TEdit;
+ Edit42: TEdit;
+ Edit43: TEdit;
+ Memo3: TMemo;
+ Panel7: TPanel;
+ Label48: TLabel;
+ Label49: TLabel;
+ Label50: TLabel;
+ Label51: TLabel;
+ Label52: TLabel;
+ Label53: TLabel;
+ Edit34: TEdit;
+ Edit36: TEdit;
+ Edit37: TEdit;
+ Edit38: TEdit;
+ Edit44: TEdit;
+ CheckBox4: TCheckBox;
+ CheckBox5: TCheckBox;
+ Label47: TLabel;
+ Edit39: TEdit;
+ procedure FormShow(Sender: TObject);
+ procedure Button1Click(Sender: TObject);
+ procedure FormResize(Sender: TObject);
+ procedure FormClose(Sender: TObject; var Action: TCloseAction);
+ procedure Button2Click(Sender: TObject);
+ procedure FormCreate(Sender: TObject);
+ procedure Button3Click(Sender: TObject);
+ procedure FormMouseWheel(Sender: TObject; Shift: TShiftState;
+ WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
+ procedure CheckBox2Click(Sender: TObject);
+ procedure CheckBox3Click(Sender: TObject);
+ private
+ { Private declarations }
+ FDisableChange: Boolean;
+ public
+ { Public declarations }
+ Data: TBookSourceItem;
+ CallBack: TNotifyEventA;
+
+ procedure ApplayEdit(Data: TBookSourceItem);
+ end;
+
+var
+ frmEditSource: TfrmEditSource;
+
+procedure ShowEditSource(Item: TBookSourceItem; CallBack: TNotifyEventA = nil);
+function Is20Source(Item: JSONObject): Boolean;
+procedure ConvertToNewFormat(Item: TBookSourceItem);
+
+implementation
+
+{$R *.dfm}
+
+uses
+ uFrmMain, Math, uBookSourceBean20;
+
+var
+ LastW, LastH: Integer;
+
+procedure ShowEditSource(Item: TBookSourceItem; CallBack: TNotifyEventA);
+var
+ F: TfrmEditSource;
+begin
+ F := TfrmEditSource.Create(Application);
+ F.Data := Item;
+ F.CallBack := CallBack;
+
+ if Is20Source(Item) then
+ ConvertToNewFormat(F.Data);
+
+ F.Show;
+end;
+
+function Is20Source(Item: JSONObject): Boolean;
+begin
+ Result := Item.Contains('ruleSearchUrl') and (not Item.Contains('ruleSearch'))
+end;
+
+type
+ RegCallback = reference to procedure(const value: string);
+
+procedure ConvertToNewFormat(Item: TBookSourceItem);
+var
+ Data: TBookSource20Item;
+ NewData: TBookSourceItem;
+ Tmp: JSONObject;
+ S: string;
+
+ function RegMatchs(const pattern, txt: string; callback: RegCallback; all: Boolean = False): string;
+ var
+ match : TMatch;
+ groups: TGroupCollection;
+ group: TGroup;
+ begin
+ match := TRegEx.Match(txt, pattern, [roIgnoreCase]);
+ groups := match.Groups;
+ if groups.Count = 0 then Exit;
+ if all then begin
+ for group in groups do begin
+ if Assigned(callback) then
+ callback(group.Value);
+ end;
+ end else if Assigned(callback) then
+ callback(groups[0].Value);
+ end;
+
+ function toNewRule(oldRule: string): string;
+ var
+ reverse: Boolean;
+ allinone: Boolean;
+ I: Integer;
+ list: TArray;
+ begin
+ if oldRule = '' then begin
+ Result := '';
+ Exit;
+ end;
+ reverse := False;
+ allinone := False;
+ Result := oldRule;
+ if (Result.StartsWith('-')) then begin
+ reverse := True;
+ Result := Result.Substring(1);
+ end;
+ if Result.StartsWith('+') then begin
+ allinone := True;
+ Result := Result.Substring(1);
+ end;
+ if (not Result.StartsWith('@CSS:', True)) and
+ (not Result.StartsWith('@XPath:', True)) and
+ (not Result.StartsWith('//')) and
+ (not Result.StartsWith('##')) and
+ (not Result.StartsWith(':')) and
+ (not Result.StartsWith('@js:', True)) and
+ (not Result.StartsWith('', True))
+ then begin
+ if Result.Contains('#') and (not Result.Contains('##')) then begin
+ Result := Result.Replace('#', '##');
+ end;
+ if Result.Contains('|') and (not Result.Contains('||')) then begin
+ if (Result.Contains('##')) then begin
+ list := result.Split(['##']);
+ if (list[0].Contains('|')) then begin
+ result := list[0].Replace('|', '||');
+ for I := 0 to High(list) do
+ Result := Result + '##' + list[i];
+ end;
+ end else begin
+ Result := Result.Replace('|', '||');
+ end;
+ end;
+
+ if Result.Contains('&') and
+ (not Result.Contains('&&')) and
+ (not Result.Contains('http')) and
+ (not Result.Contains('/')) then begin
+ result := result.Replace('&', '&&');
+ end;
+
+ end;
+
+ if allinone = true then
+ Result := '+' + Result;
+
+ if reverse = true then
+ Result := '-' + Result;
+ end;
+
+ function toNewUrl(oldUrl: string): string;
+ var
+ url: string;
+ list: TArray;
+ jsList: TStrings;
+ map: JSONObject;
+ mather: Boolean;
+ I: Integer;
+ begin
+ if oldUrl = '' then begin
+ Result := '';
+ Exit;
+ end;
+ url := oldUrl;
+ if (oldUrl.startsWith('', true)) then begin
+ Result := url.replace('=searchKey', '={{key}}')
+ .replace('=searchPage', '={{page}}');
+ Exit;
+ end;
+ map := JSONObject.Create;
+ jsList := TStringList.Create;
+ try
+ RegMatchs('@Header:\{.+?\}', url,
+ procedure (const value: string) begin
+ url := url.Replace(value, '');
+ map.S['headers'] := value;
+ end
+ );
+
+ list := url.Split(['|']);
+ url := list[0];
+ if Length(list) > 1 then begin
+ list := list[1].Split(['=']);
+ if Length(list) > 1 then
+ map.S['charset'] := list[1];
+ end;
+
+ jsList.Clear;
+ RegMatchs('\{\{.+?\}\}', url,
+ procedure (const value: string) begin
+ jsList.Add(value);
+ url := url.Replace(value, '$${' + IntToStr(jsList.Count - 1) + '}');
+ end
+ );
+
+ url := url.replace('{', '<').replace('}', '>');
+ url := url.replace('searchKey', '{{key}}');
+
+ url := TRegEx.Replace(url, '', '{{page$1}}', [roIgnoreCase]);
+ url := TRegEx.Replace(url, 'searchPage([-+]1)', '{{page$1}}', [roIgnoreCase]);
+ url := url.replace('searchPage', '{{page}}');
+
+ for I := 0 to jsList.Count - 1 do begin
+ url := url.replace('$' + IntToStr(i), jsList[i].Replace('searchKey', 'key').Replace('searchPage', 'page'));
+ end;
+
+ list := url.Split(['@']);
+ url := list[0];
+ if (Length(list) > 1) then begin
+ map.S['method'] := 'POST';
+ map.S['body'] := list[1];
+ end;
+
+ if map.Count > 0 then
+ url := Url + ',' + map.ToString;
+
+ Result := url;
+ finally
+ FreeAndNil(jsList);
+ FreeAndNil(map);
+ end;
+ end;
+
+ function toNewUrls(const oldUrls: string): string;
+ var
+ urls: TArray;
+ i: Integer;
+ begin
+ if oldUrls = '' then begin
+ Result := '';
+ Exit;
+ end;
+ if (not oldUrls.Contains(#13)) and (not oldUrls.Contains('&&')) then begin
+ Result := toNewUrl(oldUrls);
+ Exit;
+ end;
+ Result := '';
+ urls := oldurls.Split(['&&', sLineBreak, #13]);
+ for I := 0 to High(urls) do begin
+ if Trim(urls[i]) = '' then Continue;
+ Result := Result + toNewUrl(urls[i]).Replace(sLineBreak, '').Replace(#13, '') + sLineBreak;
+ end;
+ end;
+
+ procedure SyncItem(const NewKey, LastKey: string; IsURL: Boolean = False; IsURLs: boolean = false);
+ var
+ v: string;
+ begin
+ v := Data.S[LastKey];
+ if (v = '') then Exit;
+ if IsURL then
+ v := toNewUrl(v)
+ else if IsURLs then
+ v := toNewUrls(v)
+ else
+ v := toNewRule(v);
+ NewData.SetStrValue(NewKey, v);
+ end;
+
+begin
+ Tmp := JSONObject.Create();
+ try
+ Data := TBookSource20Item(Item);
+ NewData := TBookSourceItem.Create();
+ NewData.enable := data.enable;
+ if (data.bookSourceType = 'AUDIO') then
+ NewData.bookSourceType := 1
+ else if (data.bookSourceType = 'IMAGE') then
+ NewData.bookSourceType := 2
+ else
+ NewData.bookSourceType := 0;
+ NewData.bookSourceGroup := data.bookSourceGroup;
+ NewData.bookSourceUrl := data.bookSourceUrl;
+ NewData.bookUrlPattern := data.ruleBookUrlPattern;
+ NewData.bookSourceName := data.bookSourceName;
+ NewData.customOrder := data.serialNumber;
+ NewData.weight := data.weight;
+ NewData.score := data.score;
+ NewData.loginUrl := data.loginUrl;
+ if (Data.httpUserAgent <> '') then begin
+ Tmp.s['User-Agent'] := Data.httpUserAgent;
+ NewData.header := tmp.ToString;
+ end;
+ SyncItem('ruleBookInfo_author', 'ruleBookAuthor');
+ SyncItem('searchUrl', 'ruleSearchUrl', True);
+ SyncItem('exploreUrl', 'ruleFindUrl', False, True);
+
+ SyncItem('ruleSearch_bookList', 'ruleSearchList');
+ SyncItem('ruleSearch_name', 'ruleSearchName');
+ SyncItem('ruleSearch_author', 'ruleSearchAuthor');
+ SyncItem('ruleSearch_kind', 'ruleSearchKind');
+ SyncItem('ruleSearch_intro', 'ruleSearchIntroduce');
+ SyncItem('ruleSearch_bookUrl', 'ruleSearchNoteUrl');
+ SyncItem('ruleSearch_coverUrl', 'ruleSearchCoverUrl');
+ SyncItem('ruleSearch_lastChapter', 'ruleSearchLastChapter');
+
+ SyncItem('ruleExplore_bookList', 'ruleFindList');
+ SyncItem('ruleExplore_name', 'ruleFindName');
+ SyncItem('ruleExplore_author', 'ruleFindAuthor');
+ SyncItem('ruleExplore_intro', 'ruleFindIntroduce');
+ SyncItem('ruleExplore_kind', 'ruleFindKind');
+ SyncItem('ruleExplore_bookUrl', 'ruleFindNoteUrl');
+ SyncItem('ruleExplore_coverUrl', 'ruleFindCoverUrl');
+ SyncItem('ruleExplore_lastChapter', 'ruleFindLastChapter');
+
+ SyncItem('ruleBookInfo_init', 'ruleBookInfoInit');
+ SyncItem('ruleBookInfo_name', 'ruleBookName');
+ SyncItem('ruleBookInfo_author', 'ruleBookAuthor');
+ SyncItem('ruleBookInfo_intro', 'ruleIntroduce');
+ SyncItem('ruleBookInfo_kind', 'ruleBookKind');
+ SyncItem('ruleBookInfo_coverUrl', 'ruleCoverUrl');
+ SyncItem('ruleBookInfo_lastChapter', 'ruleBookLastChapter');
+ SyncItem('ruleBookInfo_tocUrl', 'ruleChapterUrl');
+
+ SyncItem('ruleToc_chapterList', 'ruleChapterList');
+ SyncItem('ruleToc_chapterName', 'ruleChapterName');
+ SyncItem('ruleToc_chapterUrl', 'ruleContentUrl');
+ SyncItem('ruleToc_nextTocUrl', 'ruleChapterUrlNext');
+
+ S := Data.S['ruleBookContent'];
+ if S.StartsWith('$') and (not S.StartsWith('$.')) then
+ S := S.Substring(1);
+ NewData.SetStrValue('ruleContent_content', S);
+ SyncItem('ruleContent_nextContentUrl', 'ruleContentUrlNext');
+
+ Item.Clear;
+ Item.Assign(NewData);
+ finally
+ FreeAndNil(tmp);
+ end;
+end;
+
+procedure TfrmEditSource.ApplayEdit(Data: TBookSourceItem);
+
+ procedure SetEditValue(Parent: TWinControl);
+ var
+ I, J: Integer;
+ Item: TControl;
+ Json: JSONObject;
+ Key, PKey, Value: string;
+ begin
+ for I := 0 to Parent.ControlCount - 1 do begin
+ Item := Parent.Controls[I];
+ if not Item.Visible then Continue;
+
+ if Item is TPanel then begin
+ SetEditValue(TWinControl(Item));
+ Continue;
+ end;
+
+ Key := Item.Hint;
+ if Key = '' then Continue;
+
+ Json := Data;
+ J := Key.IndexOf('_');
+ if J > 0 then begin
+ PKey := Key.Substring(0, J);
+ Json := Data.O[PKey];
+ if Json = nil then
+ Json := Data.AddChildObject(PKey);
+ Key := Key.Substring(J + 1);
+ end;
+
+ Value := '';
+ if Item is TEdit then
+ Value := TEdit(Item).Text
+ else if Item is TComboBox then
+ Value := TComboBox(Item).Text
+ else if Item is TMemo then
+ Value := TMemo(Item).Text;
+
+ Json.S[Key] := Value;
+ continue;
+ if Value <> '' then
+ Json.S[Key] := Value
+ else
+ Json.Delete(Key);
+ end;
+ end;
+
+begin
+ Data.enable := CheckBox1.Checked;
+ if CheckBox2.Checked then
+ Data.bookSourceType := 1
+ else if CheckBox3.Checked then
+ Data.bookSourceType := 2
+ else
+ Data.bookSourceType := 0;
+ Data.weight := StrToIntDef(Edit27.Text, Data.weight);
+ Data.customOrder := StrToIntDef(Edit28.Text, Data.customOrder);
+ SetEditValue(ScrollBox1);
+end;
+
+procedure TfrmEditSource.Button1Click(Sender: TObject);
+begin
+ FDisableChange := True;
+ try
+ ApplayEdit(Data);
+ finally
+ FDisableChange := False;
+ end;
+
+ if Assigned(CallBack) then
+ CallBack(Data);
+ Close;
+end;
+
+procedure TfrmEditSource.Button2Click(Sender: TObject);
+begin
+ Close;
+end;
+
+procedure TfrmEditSource.Button3Click(Sender: TObject);
+var
+ Item: TBookSourceItem;
+ Msg: TStrings;
+begin
+ Msg := TStringList.Create;
+ Item := TBookSourceItem(JSONObject.Create);
+ try
+ Item.Parse(Data.ToString());
+ ApplayEdit(Item);
+ if Form1.CheckBookSourceItem(Item, True, Msg) then
+ MessageBox(Handle, PChar(Msg.Text), 'ϲ, ͨ!', 64)
+ else
+ MessageBox(Handle, PChar(Msg.Text), 'Դ쳣', 48)
+ finally
+ FreeAndNil(Item);
+ FreeAndNil(Msg);
+ end;
+end;
+
+procedure TfrmEditSource.CheckBox2Click(Sender: TObject);
+begin
+ if CheckBox2.Checked then
+ CheckBox3.Checked := False;
+end;
+
+procedure TfrmEditSource.CheckBox3Click(Sender: TObject);
+begin
+ if CheckBox3.Checked then
+ CheckBox2.Checked := False;
+end;
+
+procedure TfrmEditSource.FormClose(Sender: TObject; var Action: TCloseAction);
+begin
+ Action := caFree;
+end;
+
+procedure TfrmEditSource.FormCreate(Sender: TObject);
+begin
+ if LastW <= 0 then LastW := Self.Width;
+ if LastH <= 0 then LastH := Self.Height;
+ Self.SetBounds(Left, Top, LastW, LastH);
+end;
+
+procedure TfrmEditSource.FormMouseWheel(Sender: TObject; Shift: TShiftState;
+ WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
+begin
+ if WheelDelta < 0 then
+ ScrollBox1.Perform(WM_VSCROLL,SB_LINEDOWN,0)
+ else
+ ScrollBox1.Perform(WM_VSCROLL,SB_LINEUP,0);
+end;
+
+procedure TfrmEditSource.FormResize(Sender: TObject);
+begin
+ LastW := Width;
+ LastH := Height;
+end;
+
+procedure TfrmEditSource.FormShow(Sender: TObject);
+
+ procedure SetEditValue(Parent: TWinControl);
+ var
+ I, J: Integer;
+ Item: TControl;
+ Json: JSONObject;
+ Key, PKey, Value: string;
+ begin
+ for I := 0 to Parent.ControlCount - 1 do begin
+ Item := Parent.Controls[I];
+ if not Item.Visible then Continue;
+
+ if Item is TPanel then begin
+ SetEditValue(TWinControl(Item));
+ Continue;
+ end;
+
+ Key := Item.Hint;
+ if Key = '' then Continue;
+
+ Value := '';
+ Json := Data;
+ J := Key.IndexOf('_');
+ if J > 0 then begin
+ PKey := Key.Substring(0, J);
+ Json := Data.O[PKey];
+ if Json <> nil then begin
+ Key := Key.Substring(J + 1);
+ Value := Json.S[Key];
+ end;
+ end else
+ Value := Json.S[Key];
+
+
+ if Item is TEdit then
+ TEdit(Item).Text := Value
+ else if Item is TComboBox then
+ TComboBox(Item).Text := Value
+ else if Item is TMemo then
+ TMemo(Item).Text := Value
+ end;
+ end;
+
+begin
+ ComboBox1.Items := Form1.bookGroupList.Items;
+
+ if Assigned(Data) then begin
+ FDisableChange := True;
+ try
+ CheckBox1.Checked := Data.enable;
+ CheckBox2.Checked := False;
+ CheckBox3.Checked := False;
+
+ if Data.bookSourceType = 1 then
+ CheckBox2.Checked := True;
+ if Data.bookSourceType = 2 then
+ CheckBox3.Checked := True;
+
+ Edit27.Text := IntToStr(Data.weight);
+ Edit28.Text := IntToStr(Data.customOrder);
+ SetEditValue(ScrollBox1);
+
+ finally
+ FDisableChange := False;
+ end;
+ end;
+end;
+
+end.
diff --git a/tool/书源整理工具/uFrmMain.dfm b/tool/书源整理工具/uFrmMain.dfm
new file mode 100644
index 000000000..ab83e431f
--- /dev/null
+++ b/tool/书源整理工具/uFrmMain.dfm
@@ -0,0 +1,780 @@
+object Form1: TForm1
+ Left = 0
+ Top = 0
+ Caption = #38405#35835#20070#28304#25972#29702#24037#20855
+ ClientHeight = 362
+ ClientWidth = 1145
+ Color = 15921906
+ DoubleBuffered = True
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -11
+ Font.Name = 'Tahoma'
+ Font.Pitch = fpFixed
+ Font.Style = []
+ Menu = MainMenu1
+ OldCreateOrder = False
+ OnCreate = FormCreate
+ OnDestroy = FormDestroy
+ OnShow = FormShow
+ PixelsPerInch = 96
+ TextHeight = 13
+ object Splitter1: TSplitter
+ AlignWithMargins = True
+ Left = 313
+ Top = 0
+ Width = 4
+ Height = 331
+ Margins.Left = 0
+ Margins.Top = 0
+ Margins.Right = 0
+ Margins.Bottom = 0
+ Color = clSilver
+ ParentColor = False
+ ExplicitLeft = 257
+ ExplicitHeight = 697
+ end
+ object Panel1: TPanel
+ Left = 0
+ Top = 0
+ Width = 313
+ Height = 331
+ Align = alLeft
+ BevelOuter = bvNone
+ Padding.Left = 4
+ Padding.Top = 4
+ Padding.Bottom = 4
+ TabOrder = 1
+ object SrcList: TListBox
+ AlignWithMargins = True
+ Left = 4
+ Top = 59
+ Width = 309
+ Height = 238
+ Hint = #23558#20070#28304#25991#20214#25302#20837#27492#22788
+ Margins.Left = 0
+ Margins.Top = 2
+ Margins.Right = 0
+ Margins.Bottom = 0
+ Style = lbVirtual
+ Align = alClient
+ BorderStyle = bsNone
+ DoubleBuffered = True
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -12
+ Font.Name = #23435#20307
+ Font.Pitch = fpFixed
+ Font.Style = []
+ ItemHeight = 18
+ MultiSelect = True
+ ParentDoubleBuffered = False
+ ParentFont = False
+ ParentShowHint = False
+ PopupMenu = PopupMenu1
+ ShowHint = True
+ TabOrder = 0
+ OnClick = SrcListClick
+ OnData = SrcListData
+ OnDblClick = SrcListDblClick
+ OnKeyDown = SrcListKeyDown
+ end
+ object Panel4: TPanel
+ Left = 4
+ Top = 4
+ Width = 309
+ Height = 53
+ Align = alTop
+ BevelOuter = bvNone
+ DoubleBuffered = True
+ ParentDoubleBuffered = False
+ TabOrder = 1
+ DesignSize = (
+ 309
+ 53)
+ object lbCount: TLabel
+ Left = 0
+ Top = 7
+ Width = 60
+ Height = 13
+ Caption = #20070#28304#21015#34920#65306
+ Transparent = False
+ end
+ object bookGroupList: TComboBox
+ Left = 80
+ Top = 3
+ Width = 223
+ Height = 21
+ Anchors = [akLeft, akTop, akRight]
+ DropDownCount = 24
+ Font.Charset = GB2312_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -11
+ Font.Name = 'Tahoma'
+ Font.Pitch = fpFixed
+ Font.Style = []
+ ParentFont = False
+ TabOrder = 0
+ OnChange = bookGroupListChange
+ OnClick = bookGroupListChange
+ end
+ object RadioButton1: TRadioButton
+ Left = 3
+ Top = 30
+ Width = 57
+ Height = 17
+ Caption = #20840#37096
+ Checked = True
+ TabOrder = 1
+ TabStop = True
+ OnClick = RadioButton1Click
+ end
+ object RadioButton2: TRadioButton
+ Left = 59
+ Top = 30
+ Width = 57
+ Height = 17
+ Caption = #25991#26412
+ TabOrder = 2
+ OnClick = RadioButton2Click
+ end
+ object RadioButton3: TRadioButton
+ Left = 122
+ Top = 29
+ Width = 57
+ Height = 20
+ Caption = #38899#39057
+ TabOrder = 3
+ OnClick = RadioButton3Click
+ end
+ object RadioButton4: TRadioButton
+ Left = 182
+ Top = 29
+ Width = 57
+ Height = 20
+ Caption = #22270#28304
+ TabOrder = 4
+ OnClick = RadioButton4Click
+ end
+ end
+ object StaticText1: TStaticText
+ AlignWithMargins = True
+ Left = 7
+ Top = 87
+ Width = 303
+ Height = 207
+ Margins.Top = 30
+ Align = alClient
+ Alignment = taCenter
+ Caption = #40736#26631#25302#20837#20070#28304#25991#20214#21040#27492#22788
+ Color = clWindow
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = 6736896
+ Font.Height = -16
+ Font.Name = #24494#36719#38597#40657
+ Font.Pitch = fpFixed
+ Font.Style = []
+ ParentColor = False
+ ParentFont = False
+ PopupMenu = PopupMenu1
+ TabOrder = 2
+ Transparent = False
+ end
+ object Panel6: TPanel
+ Left = 4
+ Top = 297
+ Width = 309
+ Height = 30
+ Align = alBottom
+ BevelOuter = bvNone
+ Color = clWindow
+ ParentBackground = False
+ ShowCaption = False
+ TabOrder = 3
+ object Shape1: TShape
+ Left = 0
+ Top = 0
+ Width = 309
+ Height = 1
+ Align = alTop
+ Pen.Color = clSilver
+ end
+ object Button2: TButton
+ Left = 3
+ Top = 4
+ Width = 57
+ Height = 25
+ Caption = #32622#39030'(&T)'
+ TabOrder = 0
+ OnClick = Button2Click
+ end
+ object Button3: TButton
+ Left = 64
+ Top = 4
+ Width = 57
+ Height = 25
+ Caption = #19978#31227'(&U)'
+ TabOrder = 1
+ OnClick = Button3Click
+ end
+ object Button4: TButton
+ Left = 125
+ Top = 4
+ Width = 57
+ Height = 25
+ Caption = #19979#31227'(&N)'
+ TabOrder = 2
+ OnClick = Button4Click
+ end
+ object Button5: TButton
+ Left = 185
+ Top = 4
+ Width = 57
+ Height = 25
+ Caption = #32622#24213'(&B)'
+ TabOrder = 3
+ OnClick = Button5Click
+ end
+ end
+ end
+ object Panel2: TPanel
+ Left = 317
+ Top = 0
+ Width = 828
+ Height = 331
+ Align = alClient
+ BevelOuter = bvNone
+ ParentBackground = False
+ ParentColor = True
+ TabOrder = 0
+ object Splitter2: TSplitter
+ Left = 0
+ Top = 186
+ Width = 828
+ Height = 4
+ Cursor = crVSplit
+ Align = alBottom
+ Color = clSilver
+ ParentColor = False
+ ExplicitTop = 373
+ end
+ object Panel3: TPanel
+ Left = 0
+ Top = 0
+ Width = 828
+ Height = 36
+ Align = alTop
+ BevelOuter = bvNone
+ TabOrder = 1
+ object Label1: TLabel
+ Left = 144
+ Top = 10
+ Width = 64
+ Height = 13
+ Caption = #24037#20316#32447#31243#25968':'
+ Transparent = False
+ end
+ object Button1: TButton
+ Left = 6
+ Top = 6
+ Width = 128
+ Height = 24
+ Caption = #24320#22987#22788#29702'(&B)'
+ TabOrder = 0
+ OnClick = Button1Click
+ end
+ object Edit1: TEdit
+ Left = 212
+ Top = 7
+ Width = 35
+ Height = 21
+ Alignment = taCenter
+ NumbersOnly = True
+ TabOrder = 1
+ Text = '60'
+ end
+ object CheckBox1: TCheckBox
+ Left = 264
+ Top = 9
+ Width = 97
+ Height = 17
+ Caption = #21435#38500#37325#22797
+ Checked = True
+ State = cbChecked
+ TabOrder = 2
+ end
+ object CheckBox3: TCheckBox
+ Left = 344
+ Top = 9
+ Width = 97
+ Height = 17
+ Hint = #21435#37325#26102#19981#26816#27979#20070#28304#21517#31216#21644#20998#32452
+ Caption = #30495#23454#21435#37325
+ Checked = True
+ State = cbChecked
+ TabOrder = 4
+ end
+ object CheckBox2: TCheckBox
+ Left = 429
+ Top = 9
+ Width = 97
+ Height = 17
+ Caption = #26657#39564#20070#28304
+ TabOrder = 3
+ end
+ object CheckBox4: TCheckBox
+ Left = 517
+ Top = 9
+ Width = 97
+ Height = 17
+ Hint = #33258#21160#32473#20070#28304#28155#21152#25110#21024#38500#21457#29616#20998#32452#26631#35782
+ Caption = #33258#21160#21457#29616
+ Checked = True
+ ParentShowHint = False
+ ShowHint = True
+ State = cbChecked
+ TabOrder = 5
+ end
+ object CheckBox5: TCheckBox
+ Left = 605
+ Top = 9
+ Width = 116
+ Height = 17
+ Hint = #36873#20013#26102#22788#29702#25152#26377#20070#28304'('#21253#25324#34987#36807#28388#25481#30340#20070#28304'),'#19981#36873#20013#21482#22788#29702#24403#21069#36807#28388#21518#30340#20070#28304
+ Caption = #22788#29702#25152#26377#20070#28304
+ Checked = True
+ ParentShowHint = False
+ ShowHint = True
+ State = cbChecked
+ TabOrder = 6
+ end
+ object CheckBox6: TCheckBox
+ Left = 711
+ Top = 9
+ Width = 97
+ Height = 17
+ Hint = #33258#21160#32473#20070#28304#35780#20998'('#19981#21246#36873#26102#25209#37327#26657#39564#20070#28304#19981#26657#39564#21457#29616')'
+ Caption = #33258#21160#35780#20998
+ Checked = True
+ ParentShowHint = False
+ ShowHint = True
+ State = cbChecked
+ TabOrder = 7
+ end
+ end
+ object EditData: TSynMemo
+ Left = 0
+ Top = 36
+ Width = 828
+ Height = 150
+ Align = alClient
+ Ctl3D = True
+ ParentCtl3D = False
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -13
+ Font.Name = 'Courier New'
+ Font.Style = []
+ PopupMenu = PopupMenu2
+ TabOrder = 0
+ CodeFolding.GutterShapeSize = 11
+ CodeFolding.CollapsedLineColor = clGrayText
+ CodeFolding.FolderBarLinesColor = clGrayText
+ CodeFolding.IndentGuidesColor = clGray
+ CodeFolding.IndentGuides = True
+ CodeFolding.ShowCollapsedLine = False
+ CodeFolding.ShowHintMark = True
+ UseCodeFolding = False
+ BorderStyle = bsNone
+ Gutter.AutoSize = True
+ Gutter.BorderStyle = gbsNone
+ Gutter.Color = cl3DLight
+ Gutter.BorderColor = clWindowFrame
+ Gutter.Font.Charset = DEFAULT_CHARSET
+ Gutter.Font.Color = clWindowText
+ Gutter.Font.Height = -11
+ Gutter.Font.Name = 'Courier New'
+ Gutter.Font.Style = []
+ Gutter.ShowLineNumbers = True
+ Highlighter = SynJSONSyn1
+ WordWrap = True
+ OnChange = EditDataChange
+ FontSmoothing = fsmNone
+ end
+ object Panel5: TPanel
+ Left = 0
+ Top = 190
+ Width = 828
+ Height = 141
+ Align = alBottom
+ BevelOuter = bvNone
+ TabOrder = 2
+ DesignSize = (
+ 828
+ 141)
+ object Label2: TLabel
+ Left = 6
+ Top = 2
+ Width = 36
+ Height = 13
+ Caption = #26085#24535#65306
+ Transparent = False
+ end
+ object SpeedButton1: TSpeedButton
+ Left = 783
+ Top = 0
+ Width = 43
+ Height = 20
+ Anchors = [akTop, akRight]
+ Caption = #28165#31354
+ Flat = True
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clTeal
+ Font.Height = -11
+ Font.Name = 'Tahoma'
+ Font.Pitch = fpFixed
+ Font.Style = []
+ ParentFont = False
+ OnClick = SpeedButton1Click
+ end
+ object edtLog: TSynMemo
+ AlignWithMargins = True
+ Left = 0
+ Top = 20
+ Width = 828
+ Height = 121
+ Margins.Left = 0
+ Margins.Top = 20
+ Margins.Right = 0
+ Margins.Bottom = 0
+ Align = alClient
+ Ctl3D = False
+ ParentCtl3D = False
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -12
+ Font.Name = 'Courier New'
+ Font.Style = []
+ TabOrder = 0
+ CodeFolding.GutterShapeSize = 11
+ CodeFolding.CollapsedLineColor = clGrayText
+ CodeFolding.FolderBarLinesColor = clGrayText
+ CodeFolding.IndentGuidesColor = clGray
+ CodeFolding.IndentGuides = False
+ CodeFolding.ShowCollapsedLine = False
+ CodeFolding.ShowHintMark = True
+ UseCodeFolding = False
+ BookMarkOptions.EnableKeys = False
+ BookMarkOptions.GlyphsVisible = False
+ BorderStyle = bsNone
+ Gutter.AutoSize = True
+ Gutter.BorderStyle = gbsNone
+ Gutter.Color = cl3DLight
+ Gutter.BorderColor = clWindowFrame
+ Gutter.Font.Charset = DEFAULT_CHARSET
+ Gutter.Font.Color = clWindowText
+ Gutter.Font.Height = -11
+ Gutter.Font.Name = 'Courier New'
+ Gutter.Font.Style = []
+ Gutter.ShowLineNumbers = True
+ Gutter.Visible = False
+ Gutter.Width = 0
+ Options = [eoScrollPastEol, eoShowScrollHint, eoSmartTabDelete, eoSmartTabs, eoTabsToSpaces]
+ ReadOnly = True
+ RightEdge = 0
+ WordWrap = True
+ OnChange = EditDataChange
+ FontSmoothing = fsmClearType
+ end
+ end
+ end
+ object StatusBar1: TStatusBar
+ Left = 0
+ Top = 337
+ Width = 1145
+ Height = 25
+ Panels = <
+ item
+ Width = 500
+ end
+ item
+ Width = 50
+ end>
+ end
+ object ProgressBar1: TProgressBar
+ Left = 0
+ Top = 331
+ Width = 1145
+ Height = 6
+ Align = alBottom
+ Position = 100
+ TabOrder = 3
+ Visible = False
+ end
+ object PopupMenu1: TPopupMenu
+ OnPopup = PopupMenu1Popup
+ Left = 136
+ Top = 192
+ object C3: TMenuItem
+ Caption = #22797#21046#26032#22686'(&A)'
+ OnClick = C3Click
+ end
+ object N7: TMenuItem
+ Caption = #26032#24314#20070#28304'(&N)...'
+ OnClick = N7Click
+ end
+ object E1: TMenuItem
+ Caption = #32534#36753#20070#28304'(&E)...'
+ OnClick = E1Click
+ end
+ object N18: TMenuItem
+ Caption = #23548#20986#36873#20013#20070#28304'...'
+ OnClick = N18Click
+ end
+ object N5: TMenuItem
+ Caption = '-'
+ end
+ object S2: TMenuItem
+ Caption = #25490#24207' - '#20070#28304#21517#31216'(&S)'
+ OnClick = S2Click
+ end
+ object G1: TMenuItem
+ Caption = #25490#24207' - '#20998#32452'(&G)'
+ OnClick = G1Click
+ end
+ object W3: TMenuItem
+ Caption = #25490#24207' - '#26435#37325'(&W)'
+ OnClick = W3Click
+ end
+ object O2: TMenuItem
+ Caption = #25490#24207' - '#35780#20998'(&O)'
+ OnClick = O2Click
+ end
+ object N9: TMenuItem
+ Caption = '-'
+ end
+ object F2: TMenuItem
+ Caption = #26597#25214#20070#28304'(&F)...'
+ ShortCut = 16454
+ OnClick = F2Click
+ end
+ object N14: TMenuItem
+ Caption = #26597#25214#19979#19968#20010
+ ShortCut = 114
+ OnClick = N14Click
+ end
+ object B1: TMenuItem
+ Caption = #20070#28304#21517#31216#26367#25442'(&B)...'
+ OnClick = B1Click
+ end
+ object B2: TMenuItem
+ Caption = #20070#28304'URL'#26367#25442'(&U)...'
+ OnClick = B2Click
+ end
+ object H2: TMenuItem
+ Caption = #20998#32452#21517#31216#26367#25442'(&H)...'
+ OnClick = H2Click
+ end
+ object N16: TMenuItem
+ Caption = '-'
+ end
+ object N15: TMenuItem
+ Caption = #25209#37327#28155#21152#20998#32452#21517#31216'...'
+ OnClick = N15Click
+ end
+ object N17: TMenuItem
+ Caption = #25209#37327#21024#38500#20998#32452#21517#31216'...'
+ OnClick = N17Click
+ end
+ object N6: TMenuItem
+ Caption = '-'
+ end
+ object C4: TMenuItem
+ Caption = #22797#21046#20070#28304'(&C)'
+ ShortCut = 16451
+ OnClick = C4Click
+ end
+ object X2: TMenuItem
+ Caption = #21098#20999#20070#28304'(&X)'
+ ShortCut = 16472
+ OnClick = X2Click
+ end
+ object P2: TMenuItem
+ Caption = #31896#36148#20070#28304'(&P)'
+ ShortCut = 16470
+ OnClick = P2Click
+ end
+ object D1: TMenuItem
+ Caption = #21024#38500#20070#28304'(&D)'
+ OnClick = D1Click
+ end
+ object N13: TMenuItem
+ Caption = '-'
+ end
+ object C1: TMenuItem
+ Caption = #28165#31354'(&L)'
+ OnClick = C1Click
+ end
+ end
+ object SynJSONSyn1: TSynJSONSyn
+ Options.AutoDetectEnabled = False
+ Options.AutoDetectLineLimit = 0
+ Options.Visible = False
+ Left = 552
+ Top = 352
+ end
+ object PopupMenu2: TPopupMenu
+ OnPopup = PopupMenu2Popup
+ Left = 632
+ Top = 352
+ object S1: TMenuItem
+ Caption = #20445#23384#20462#25913'(&S)'
+ ShortCut = 16467
+ OnClick = S1Click
+ end
+ object T1: TMenuItem
+ Caption = #27979#35797#20070#28304'(&T)'
+ OnClick = T1Click
+ end
+ object N3: TMenuItem
+ Caption = '-'
+ end
+ object U1: TMenuItem
+ Caption = #26356#26032#26102#38388#25139'(&U)'
+ OnClick = U1Click
+ end
+ object N20: TMenuItem
+ Caption = '-'
+ end
+ object R1: TMenuItem
+ Caption = #25764#28040'(&R)'
+ OnClick = R1Click
+ end
+ object Z1: TMenuItem
+ Caption = #37325#20570'(&Z)'
+ OnClick = Z1Click
+ end
+ object N1: TMenuItem
+ Caption = '-'
+ end
+ object C2: TMenuItem
+ Caption = #22797#21046'(&C)'
+ OnClick = C2Click
+ end
+ object X1: TMenuItem
+ Caption = #21098#20999'(&X)'
+ OnClick = X1Click
+ end
+ object P1: TMenuItem
+ Caption = #31896#36148'(&P)'
+ OnClick = P1Click
+ end
+ object N2: TMenuItem
+ Caption = '-'
+ end
+ object A1: TMenuItem
+ Caption = #20840#36873'(&A)'
+ OnClick = A1Click
+ end
+ object N4: TMenuItem
+ Caption = '-'
+ end
+ object W1: TMenuItem
+ Caption = #33258#21160#25442#34892'(&W)'
+ OnClick = W1Click
+ end
+ end
+ object Timer1: TTimer
+ Interval = 100
+ OnTimer = Timer1Timer
+ Left = 464
+ Top = 440
+ end
+ object MainMenu1: TMainMenu
+ Left = 448
+ Top = 136
+ object F1: TMenuItem
+ Caption = #25991#20214'(&F)'
+ object O1: TMenuItem
+ Caption = #25171#24320#20070#28304#25991#20214'(&O)...'
+ OnClick = O1Click
+ end
+ object A2: TMenuItem
+ Caption = #28155#21152#20070#28304#25991#20214'(&A)...'
+ OnClick = A2Click
+ end
+ object N12: TMenuItem
+ Caption = '-'
+ end
+ object N11: TMenuItem
+ Caption = #26032#24314#20070#28304'(&N)...'
+ OnClick = N7Click
+ end
+ object N10: TMenuItem
+ Caption = '-'
+ end
+ object E2: TMenuItem
+ Caption = #23548#20986#20070#28304#25991#20214'(&E)...'
+ OnClick = E2Click
+ end
+ object S3: TMenuItem
+ Caption = #23548#20986#36873#20013#20070#28304#21040#25991#20214'(&S)...'
+ OnClick = S3Click
+ end
+ end
+ object E3: TMenuItem
+ Caption = #32534#36753'(&E)'
+ object B3: TMenuItem
+ Caption = #20070#28304#21517#31216#26367#25442'(&B)...'
+ OnClick = B1Click
+ end
+ object URLU1: TMenuItem
+ Caption = #20070#28304'URL'#26367#25442'(&U)...'
+ OnClick = B2Click
+ end
+ object H3: TMenuItem
+ Caption = #20998#32452#21517#31216#26367#25442'(&H)...'
+ OnClick = H2Click
+ end
+ object N19: TMenuItem
+ Caption = '-'
+ end
+ object URL1: TMenuItem
+ Caption = #26597#25214#20070#28304': '#25628#32034#20070#28304'URL'
+ Checked = True
+ OnClick = URL1Click
+ end
+ end
+ object H1: TMenuItem
+ Caption = #24110#21161'(&H)'
+ object I1: TMenuItem
+ Caption = #20851#20110'(&I)'
+ OnClick = I1Click
+ end
+ object N8: TMenuItem
+ Caption = '-'
+ end
+ object W2: TMenuItem
+ Caption = #20316#32773#21338#23458'(&W)'
+ OnClick = W2Click
+ end
+ object R2: TMenuItem
+ Caption = #23567#35828#38405#35835#22120'(&R)'
+ OnClick = R2Click
+ end
+ end
+ end
+ object SaveDialog1: TSaveDialog
+ Filter = #20070#28304#25991#20214'(*.json)|*.json|'#25152#26377#25991#20214'(*.*)|*.*'
+ Options = [ofHideReadOnly, ofPathMustExist, ofNoReadOnlyReturn, ofEnableSizing]
+ Title = #23548#20986#20070#28304
+ Left = 472
+ Top = 352
+ end
+ object OpenDialog1: TOpenDialog
+ Filter = #20070#28304#25991#20214'(*.json)|*.json|'#25152#26377#25991#20214'(*.*)|*.*'
+ Options = [ofHideReadOnly, ofPathMustExist, ofFileMustExist, ofEnableSizing]
+ Left = 544
+ Top = 280
+ end
+end
diff --git a/tool/书源整理工具/uFrmMain.pas b/tool/书源整理工具/uFrmMain.pas
new file mode 100644
index 000000000..4a035dde4
--- /dev/null
+++ b/tool/书源整理工具/uFrmMain.pas
@@ -0,0 +1,1784 @@
+unit uFrmMain;
+
+interface
+
+uses
+ iocp.Http.Client, iocp.Utils.Str, DateUtils, RegularExpressions,
+ YxdJson, YxdStr, YxdHash, YxdWorker, ShellAPI, Math, StrUtils,
+ uBookSourceBean,
+ Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
+ Dialogs, StdCtrls, ExtCtrls, Menus, SynEdit, SynMemo, ComCtrls, SyncObjs,
+ SynEditHighlighter, SynHighlighterJSON, Vcl.Buttons;
+
+type
+ PProcessState = ^TProcessState;
+ TProcessState = record
+ STime: Int64;
+ NeedFree: Boolean;
+ Min: Integer;
+ Max: Integer;
+ Value: Integer;
+ end;
+
+type
+ TForm1 = class(TForm)
+ Panel1: TPanel;
+ SrcList: TListBox;
+ Splitter1: TSplitter;
+ Panel2: TPanel;
+ PopupMenu1: TPopupMenu;
+ C1: TMenuItem;
+ Panel3: TPanel;
+ EditData: TSynMemo;
+ Button1: TButton;
+ Panel4: TPanel;
+ lbCount: TLabel;
+ bookGroupList: TComboBox;
+ StatusBar1: TStatusBar;
+ ProgressBar1: TProgressBar;
+ SynJSONSyn1: TSynJSONSyn;
+ PopupMenu2: TPopupMenu;
+ S1: TMenuItem;
+ N1: TMenuItem;
+ C2: TMenuItem;
+ X1: TMenuItem;
+ P1: TMenuItem;
+ A1: TMenuItem;
+ N2: TMenuItem;
+ N3: TMenuItem;
+ R1: TMenuItem;
+ Z1: TMenuItem;
+ N4: TMenuItem;
+ W1: TMenuItem;
+ Label1: TLabel;
+ Edit1: TEdit;
+ CheckBox1: TCheckBox;
+ CheckBox2: TCheckBox;
+ D1: TMenuItem;
+ N6: TMenuItem;
+ C3: TMenuItem;
+ N5: TMenuItem;
+ S2: TMenuItem;
+ G1: TMenuItem;
+ N7: TMenuItem;
+ E1: TMenuItem;
+ Timer1: TTimer;
+ CheckBox3: TCheckBox;
+ MainMenu1: TMainMenu;
+ F1: TMenuItem;
+ H1: TMenuItem;
+ E2: TMenuItem;
+ I1: TMenuItem;
+ SaveDialog1: TSaveDialog;
+ W2: TMenuItem;
+ N8: TMenuItem;
+ R2: TMenuItem;
+ N9: TMenuItem;
+ H2: TMenuItem;
+ E3: TMenuItem;
+ H3: TMenuItem;
+ N10: TMenuItem;
+ N11: TMenuItem;
+ T1: TMenuItem;
+ Panel5: TPanel;
+ Splitter2: TSplitter;
+ Label2: TLabel;
+ edtLog: TSynMemo;
+ SpeedButton1: TSpeedButton;
+ StaticText1: TStaticText;
+ O1: TMenuItem;
+ N12: TMenuItem;
+ OpenDialog1: TOpenDialog;
+ A2: TMenuItem;
+ C4: TMenuItem;
+ X2: TMenuItem;
+ P2: TMenuItem;
+ N13: TMenuItem;
+ B1: TMenuItem;
+ F2: TMenuItem;
+ N14: TMenuItem;
+ CheckBox4: TCheckBox;
+ N15: TMenuItem;
+ N16: TMenuItem;
+ N17: TMenuItem;
+ N18: TMenuItem;
+ S3: TMenuItem;
+ B2: TMenuItem;
+ B3: TMenuItem;
+ URLU1: TMenuItem;
+ N19: TMenuItem;
+ URL1: TMenuItem;
+ RadioButton1: TRadioButton;
+ RadioButton2: TRadioButton;
+ RadioButton3: TRadioButton;
+ RadioButton4: TRadioButton;
+ CheckBox5: TCheckBox;
+ N20: TMenuItem;
+ U1: TMenuItem;
+ W3: TMenuItem;
+ Panel6: TPanel;
+ Button2: TButton;
+ Button3: TButton;
+ Button4: TButton;
+ Button5: TButton;
+ Shape1: TShape;
+ CheckBox6: TCheckBox;
+ O2: TMenuItem;
+ procedure FormCreate(Sender: TObject);
+ procedure C1Click(Sender: TObject);
+ procedure FormShow(Sender: TObject);
+ procedure SrcListKeyDown(Sender: TObject; var Key: Word;
+ Shift: TShiftState);
+ procedure FormDestroy(Sender: TObject);
+ procedure SrcListData(Control: TWinControl; Index: Integer;
+ var Data: string);
+ procedure PopupMenu2Popup(Sender: TObject);
+ procedure R1Click(Sender: TObject);
+ procedure Z1Click(Sender: TObject);
+ procedure C2Click(Sender: TObject);
+ procedure X1Click(Sender: TObject);
+ procedure P1Click(Sender: TObject);
+ procedure A1Click(Sender: TObject);
+ procedure S1Click(Sender: TObject);
+ procedure SrcListClick(Sender: TObject);
+ procedure EditDataChange(Sender: TObject);
+ procedure W1Click(Sender: TObject);
+ procedure D1Click(Sender: TObject);
+ procedure C3Click(Sender: TObject);
+ procedure Button1Click(Sender: TObject);
+ procedure Timer1Timer(Sender: TObject);
+ procedure bookGroupListChange(Sender: TObject);
+ procedure N7Click(Sender: TObject);
+ procedure E1Click(Sender: TObject);
+ procedure SrcListDblClick(Sender: TObject);
+ procedure I1Click(Sender: TObject);
+ procedure E2Click(Sender: TObject);
+ procedure W2Click(Sender: TObject);
+ procedure R2Click(Sender: TObject);
+ procedure S2Click(Sender: TObject);
+ procedure G1Click(Sender: TObject);
+ procedure H2Click(Sender: TObject);
+ procedure T1Click(Sender: TObject);
+ procedure SpeedButton1Click(Sender: TObject);
+ procedure O1Click(Sender: TObject);
+ procedure A2Click(Sender: TObject);
+ procedure PopupMenu1Popup(Sender: TObject);
+ procedure C4Click(Sender: TObject);
+ procedure X2Click(Sender: TObject);
+ procedure P2Click(Sender: TObject);
+ procedure B1Click(Sender: TObject);
+ procedure F2Click(Sender: TObject);
+ procedure N14Click(Sender: TObject);
+ procedure N15Click(Sender: TObject);
+ procedure N17Click(Sender: TObject);
+ procedure S3Click(Sender: TObject);
+ procedure N18Click(Sender: TObject);
+ procedure B2Click(Sender: TObject);
+ procedure URL1Click(Sender: TObject);
+ procedure RadioButton1Click(Sender: TObject);
+ procedure RadioButton2Click(Sender: TObject);
+ procedure RadioButton3Click(Sender: TObject);
+ procedure RadioButton4Click(Sender: TObject);
+ procedure U1Click(Sender: TObject);
+ procedure W3Click(Sender: TObject);
+ procedure Button2Click(Sender: TObject);
+ procedure Button3Click(Sender: TObject);
+ procedure Button4Click(Sender: TObject);
+ procedure Button5Click(Sender: TObject);
+ procedure O2Click(Sender: TObject);
+ procedure Button6Click(Sender: TObject);
+ private
+ { Private declarations }
+ OldListWndProc, OldTextWndProc: TWndMethod;
+ FBookSrcData: JSONArray;
+ FBookCopyData: JSONArray;
+ FBookGroups: TStringHash;
+ FIsChange, FChanging: Boolean;
+ FCurIndex: Integer;
+
+ FWaitStop: Integer;
+ FTaskRef: Integer;
+ FTaskStartTime: Int64;
+ FCheckLastTime: Int64;
+ FMaxWorkers: Integer;
+
+ FFilterList: TList;
+ FCurCheckIndex: Integer;
+ FCheckCount, FCheckFinish: Integer;
+ FAutoFind, FAutoDel, FAllSrc, FAutoScore: Boolean;
+
+ FWaitCheckBookSourceSingId: Integer;
+
+ FLocker: TCriticalSection;
+ FStateMsg: string;
+ FLastSortFlag: Integer;
+ FLastFindSource: string;
+ FBookType: string;
+ procedure SetBookType(const Value: string);
+
+ protected
+ procedure SrcListWndProc(var Message: TMessage);
+ procedure SrcListTextWndProc(var Message: TMessage);
+ procedure AddSrcFiles(ADrop: Integer);
+ procedure WMDropFiles(var Msg: TWMDropFiles); message WM_DROPFILES;
+ public
+ { Public declarations }
+ function CheckItem(Item: TBookSourceItem): Boolean;
+ function CheckSaveState(): Boolean;
+
+
+ function CheckBookSourceItem(Item: TBookSourceItem; RaiseErr: Boolean = False; OutLog: TStrings = nil): Boolean; overload;
+ function CheckBookSourceItem(Item: TBookSourceItem; Http: THttpClient; Header: THttpHeaders; RaiseErr: Boolean = False; OutLog: TStrings = nil): Boolean; overload;
+
+ procedure AddSrcFile(const FileName: string);
+ procedure UpdateBookGroup(Item: TBookSourceItem);
+
+ procedure RemoveRepeat(AJob: PJob);
+ procedure WaitCheckBookSource(AJob: PJob);
+ procedure DoCheckBookSourceItem(AJob: PJob);
+
+ procedure TaskFinish(AJob: PJob);
+ procedure DoNotifyDataChange(AJob: PJob);
+ procedure DoUpdateProcess(AJob: PJob);
+
+ procedure Log(const Msg: string);
+ procedure LogD(const Msg: string);
+ procedure DispLog();
+ procedure NotifyListChange(Flag: Integer = 0);
+
+ procedure RemoveSelected();
+ procedure CopySelected();
+ procedure Paste();
+ procedure CutSelectedt();
+ procedure FindSource(const FindStr: string);
+ procedure EditSource(Item: TBookSourceItem);
+ procedure ExportSelectedToFile();
+
+ procedure ChangeItemIndex(const CurIndex, NewIndex: Integer);
+
+ property BookType: string read FBookType write SetBookType;
+ end;
+
+var
+ Form1: TForm1;
+
+implementation
+
+{$R *.dfm}
+
+uses
+ uFrmWait, uFrmEditSource, uFrmReplaceGroup;
+
+function GetCurJavaDateTime(): Int64;
+begin
+ Result := Round(((Now - 25569) * 86400000 - 3600000 * 8) / 1000);
+end;
+
+procedure CutOrCopyFiles(FileList: AnsiString; bCopy: Boolean);
+type
+ PDropFiles = ^TDropFiles;
+
+ TDropFiles = record
+ pfiles: DWORD;
+ pt: TPoint;
+ fNC: BOOL;
+ fwide: BOOL;
+ end;
+const
+ DROPEFFECT_COPY = 1;
+ DROPEFFECT_MOVE = 2;
+var
+ hGblFileList: hGlobal;
+ pFileListDate: Pbyte;
+ HandleDropEffect: UINT;
+ hGblDropEffect: hGlobal;
+ pdwDropEffect: PDWORD;
+ iLen: Integer;
+begin
+ iLen := Length(FileList) + 2;
+ FileList := FileList + #0#0;
+ hGblFileList := GlobalAlloc(GMEM_ZEROINIT or GMEM_MOVEABLE or GMEM_SHARE,
+ SizeOf(TDropFiles) + iLen);
+ pFileListDate := GlobalLock(hGblFileList);
+ PDropFiles(pFileListDate)^.pfiles := SizeOf(TDropFiles);
+ PDropFiles(pFileListDate)^.pt.Y := 0;
+ PDropFiles(pFileListDate)^.pt.X := 0;
+ PDropFiles(pFileListDate)^.fNC := False;
+ PDropFiles(pFileListDate)^.fwide := False;
+ Inc(pFileListDate, SizeOf(TDropFiles));
+ CopyMemory(pFileListDate, @FileList[1], iLen);
+ GlobalUnlock(hGblFileList);
+ HandleDropEffect := RegisterClipboardFormat('Preferred DropEffect ');
+ hGblDropEffect := GlobalAlloc(GMEM_ZEROINIT or GMEM_MOVEABLE or GMEM_SHARE,
+ SizeOf(DWORD));
+ pdwDropEffect := GlobalLock(hGblDropEffect);
+ if (bCopy) then pdwDropEffect^ := DROPEFFECT_COPY
+ else pdwDropEffect^ := DROPEFFECT_MOVE;
+ GlobalUnlock(hGblDropEffect);
+ if OpenClipboard(0) then begin
+ EmptyClipboard();
+ SetClipboardData(HandleDropEffect, hGblDropEffect);
+ SetClipboardData(CF_HDROP, hGblFileList);
+ CloseClipboard();
+ end;
+end;
+
+// ļļ #0 ָ
+procedure CopyFileClipbrd(const FName: string);
+begin
+ CutOrCopyFiles(AnsiString(FName), True);
+end;
+
+procedure TForm1.AddSrcFiles(ADrop: Integer);
+var
+ i: Integer;
+ p: array[0..1023] of Char;
+begin
+ for i := 0 to DragQueryFile(ADrop, $FFFFFFFF, nil, 0) - 1 do begin
+ DragQueryFile(ADrop, i, p, 1024);
+ AddSrcFile(StrPas(p));
+ end;
+end;
+
+procedure TForm1.B1Click(Sender: TObject);
+var
+ FindStr, NewStr: string;
+ I, Flag: Integer;
+ Item: TBookSourceItem;
+begin
+ Flag := 1;
+ if ShowReplaceGroup(Self, '滻 - Դ', FindStr, NewStr, Flag) then begin
+ if (FindStr <> '') and (Flag = 0) then
+ Exit;
+ for I := 0 to FBookSrcData.Count - 1 do begin
+ Item := TBookSourceItem(FBookSrcData.O[I]);
+ if not Assigned(Item) then Continue;
+ Item.bookSourceName := StringReplace(Trim(Item.bookSourceName), FindStr, NewStr, [rfReplaceAll, rfIgnoreCase]);
+ end;
+ NotifyListChange();
+ end;
+end;
+
+procedure TForm1.B2Click(Sender: TObject);
+var
+ FindStr, NewStr: string;
+ I, Flag: Integer;
+ Item: TBookSourceItem;
+begin
+ Flag := 1;
+ if ShowReplaceGroup(Self, '滻 - ԴURL', FindStr, NewStr, Flag) then begin
+ if (FindStr <> '') and (Flag = 0) then
+ Exit;
+ for I := 0 to FBookSrcData.Count - 1 do begin
+ Item := TBookSourceItem(FBookSrcData.O[I]);
+ if not Assigned(Item) then Continue;
+ Item.bookSourceUrl := StringReplace(Trim(Item.bookSourceUrl), FindStr, NewStr, [rfReplaceAll, rfIgnoreCase]);
+ end;
+ NotifyListChange();
+ end;
+end;
+
+procedure TForm1.bookGroupListChange(Sender: TObject);
+begin
+ if FChanging then
+ Exit;
+ FChanging := True;
+ try
+ EditData.Text := '';
+ FIsChange := False;
+ FCurIndex := -1;
+ NotifyListChange(1);
+ finally
+ FChanging := False;
+ end;
+end;
+
+procedure TForm1.Button1Click(Sender: TObject);
+begin
+ if Button1.Tag = 0 then begin
+ Button1.Tag := 1;
+ Button1.Caption := 'ֹͣ(&S)';
+
+ FTaskRef := 0;
+ FWaitStop := 0;
+ FCheckLastTime := GetTimestamp;
+ FTaskStartTime := GetTimestamp;
+ FMaxWorkers := Max(1, StrToIntDef(Edit1.Text, 8));
+ Workers.MaxWorkers := FMaxWorkers;
+
+ edtLog.Lines.Clear;
+ Log('ڳʼ...');
+
+ Timer1.Enabled := True;
+ ProgressBar1.Min := 0;
+ ProgressBar1.Max := 100;
+ ProgressBar1.Position := 0;
+
+ FAutoFind := CheckBox4.Checked;
+ FAutoDel := CheckBox1.Checked;
+ FAllSrc := CheckBox5.Checked;
+ FAutoScore := CheckBox6.Checked;
+
+ if FAutoDel or FAutoFind then begin
+ Inc(FTaskRef);
+ Log('ڴ...');
+ Workers.Post(RemoveRePeat, Pointer(Integer(CheckBox3.Checked)));
+ end;
+ if CheckBox2.Checked then begin
+ Inc(FTaskRef);
+ if not CheckBox1.Checked then
+ Workers.SendSignal(FWaitCheckBookSourceSingId);
+ end;
+
+ ShowWaitDlg();
+ end else begin
+ AtomicIncrement(FTaskRef);
+ if AtomicDecrement(FTaskRef) <= 0 then begin
+ Button1.Tag := 0;
+ Button1.Caption := 'ʼ(&B)';
+ Timer1.Enabled := False;
+ FTaskStartTime := 0;
+ ProgressBar1.Visible := False;
+ HideWaitDlg;
+ Log('');
+ end else begin
+ Button1.Tag := 2;
+ Button1.Caption := 'ֹͣ...';
+ AtomicIncrement(FWaitStop);
+ end;
+ end;
+ //Application.ProcessMessages;
+end;
+
+procedure TForm1.Button2Click(Sender: TObject);
+begin
+ ChangeItemIndex(SrcList.ItemIndex, 0);
+end;
+
+procedure TForm1.Button3Click(Sender: TObject);
+begin
+ ChangeItemIndex(SrcList.ItemIndex, SrcList.ItemIndex - 1);
+end;
+
+procedure TForm1.Button4Click(Sender: TObject);
+begin
+ ChangeItemIndex(SrcList.ItemIndex, SrcList.ItemIndex + 1);
+end;
+
+procedure TForm1.Button5Click(Sender: TObject);
+begin
+ ChangeItemIndex(SrcList.ItemIndex, SrcList.Count - 1);
+end;
+
+procedure TForm1.Button6Click(Sender: TObject);
+var
+ url: string;
+begin
+ url := 'http://www.baidu.com/?aaa=0';
+ url := TRegEx.Replace(url, '', '{{page$1}}', [roIgnoreCase]);
+ ShowMessage(url);
+end;
+
+procedure TForm1.A1Click(Sender: TObject);
+begin
+ EditData.SelectAll;
+end;
+
+procedure TForm1.A2Click(Sender: TObject);
+begin
+ OpenDialog1.Title := 'Դļ';
+ if OpenDialog1.Execute(Handle) then
+ AddSrcFile(OpenDialog1.FileName);
+end;
+
+procedure TForm1.AddSrcFile(const FileName: string);
+var
+ Data: JSONArray;
+ Item: TBookSourceItem;
+ I: Integer;
+begin
+ Data := JSONArray.Create;
+ try
+ Data.LoadFromFile(FileName);
+ I := Data.Count;
+ while I > 0 do begin
+ try
+ Item := TBookSourceItem(Data.O[0]);
+ if Assigned(Item) and (Item.bookSourceUrl <> '') then begin
+ UpdateBookGroup(Item);
+ FBookSrcData.Add(Data.O[0]);
+ end;
+ except
+ end;
+ Dec(I);
+ end;
+ finally
+ Data.Free;
+ NotifyListChange;
+ end;
+end;
+
+procedure TForm1.C1Click(Sender: TObject);
+begin
+ FBookSrcData.Clear;
+ NotifyListChange;
+end;
+
+procedure TForm1.C2Click(Sender: TObject);
+begin
+ EditData.CopyToClipboard;
+end;
+
+procedure TForm1.C3Click(Sender: TObject);
+var
+ S: string;
+ Item, NewItem: TBookSourceItem;
+begin
+ if SrcList.ItemIndex < 0 then Exit;
+ if not CheckSaveState then Exit;
+ Item := TBookSourceItem(FFilterList[SrcList.ItemIndex]);
+ S := InputBox('Դ', 'Դ', Item.bookSourceName);
+
+ NewItem := TBookSourceItem(FBookSrcData.AddChildObject);
+ NewItem.Parse(Item.ToString);
+ NewItem.bookSourceName := S;
+
+ NotifyListChange;
+ if FFilterList.Count > 0 then
+ SrcList.ItemIndex := FFilterList.Count - 1;
+end;
+
+procedure TForm1.C4Click(Sender: TObject);
+begin
+ CopySelected;
+end;
+
+function TForm1.CheckBookSourceItem(Item: TBookSourceItem; RaiseErr: Boolean; OutLog: TStrings): Boolean;
+var
+ Http: THttpClient;
+ Header: THttpHeaders;
+begin
+ Result := False;
+ try
+ if not Assigned(Item) then Exit;
+ Http := THttpClient.Create(nil);
+ if Assigned(OutLog) then begin
+ Http.ConnectionTimeOut := 6000;
+ Http.RecvTimeOut := 30000;
+ end else begin
+ Http.ConnectionTimeOut := 30000;
+ Http.RecvTimeOut := 30000;
+ end;
+ Header := THttpHeaders.Create;
+
+ FAutoScore := True;
+ Result := CheckBookSourceItem(Item, Http, Header, RaiseErr, OutLog);
+
+ finally
+ FreeAndNil(Http);
+ FreeAndNil(Header);
+ end;
+end;
+
+procedure TForm1.ChangeItemIndex(const CurIndex, NewIndex: Integer);
+var
+ Item1, Item2: TBookSourceItem;
+ I1, I2, NIndex: Integer;
+begin
+ if CurIndex < 0 then Exit;
+ if SrcList.Count <= 1 then Exit;
+ NIndex := NewIndex;
+ if NIndex >= SrcList.Count then
+ NIndex := SrcList.Count - 1;
+ if NIndex < 0 then Exit;
+ if NIndex = CurIndex then Exit;
+
+ Item1 := TBookSourceItem(FFilterList[CurIndex]);
+ Item2 := TBookSourceItem(FFilterList[NIndex]);
+
+ I1 := FBookSrcData.IndexOfObject(Item1);
+ I2 := FBookSrcData.IndexOfObject(Item2);
+
+ if I1 < 0 then Exit;
+ if I2 < 0 then Exit;
+
+ FBookSrcData.MoveItem(I1, I2);
+ FCurIndex := NIndex;
+ NotifyListChange(1);
+end;
+
+function TForm1.CheckBookSourceItem(Item: TBookSourceItem; Http: THttpClient;
+ Header: THttpHeaders; RaiseErr: Boolean; OutLog: TStrings): Boolean;
+
+ function ValidationURL(const BaseURL, URL: string): string;
+ begin
+ if (BaseURL <> '') and (BaseURL <> '-') and (URL <> '-') and (Pos('://', URL) < 1) then begin
+ if URL = '' then
+ Result := BaseURL
+ else if URL[1] = '/' then begin
+ if BaseURL[Length(BaseURL)] = '/' then
+ Result := BaseURL + URL.Substring(2)
+ else
+ Result := BaseURL + URL
+ end else begin
+ if BaseURL[Length(BaseURL)] = '/' then
+ Result := BaseURL + URL
+ else
+ Result := BaseURL + '/' + URL;
+ end;
+ end else
+ Result := URL;
+ Result := StringReplace(Result, '=searchPage', '=1', [rfReplaceAll, rfIgnoreCase]);
+ end;
+
+ function CheckURL(const URL, Title: string; RaiseErr: Boolean = False; Try404: Boolean = False): Boolean;
+ var
+ Resp: THttpResult;
+ Msg: string;
+ begin
+ Result := (URL <> '') and (URL <> '-') and (Pos('http', LowerCase(URL)) = 1);
+ if Result then begin
+ try
+ Resp := Http.Get(UrlEncodeEx(URL), nil, Header);
+ if (Resp.StatusCode = 200) or (Try404 and (Resp.StatusCode >= 400) and (Resp.StatusCode < 500)) then begin
+ Result := True;
+ if Assigned(OutLog) then OutLog.Add(Title + 'ӳɹ.');
+ end else begin
+ Result := False;
+ Msg := Format('%sʧ(StatusCode: %d, %s).', [Title, Resp.StatusCode, URL]);
+ if Assigned(OutLog) then OutLog.Add(Msg);
+ if RaiseErr then
+ raise Exception.Create(Msg);
+ end;
+ except
+ Result := False;
+ Msg := Format('%sԳ(%s).', [Title, Exception(ExceptObject).Message]);
+ if Assigned(OutLog) then OutLog.Add(Msg);
+ if RaiseErr then
+ raise Exception.Create(Msg);
+ end;
+ end else if Assigned(OutLog) then
+ OutLog.Add('Ч' + Title + '.');
+ end;
+
+ // ⷢб
+ function CheckFindURL(AItem: TBookSourceItem; const BaseURL, Text, Title: string; RaiseErr: Boolean): Boolean;
+ var
+ I, J, L: Integer;
+ Msg, Item, SubTitle, AURL: string;
+ begin
+ Result := False;
+ if Text = '' then begin
+ Result := True;
+ Exit;
+ end;
+ try
+ J := 1;
+ while (J > 0) and (J <= Length(Text)) do begin
+ I := PosEx('&&', Text, J);
+ L := 2;
+ if I <= 0 then begin
+ I := PosEx(#$A, Text, J);
+ L := 1;
+ end;
+ if I > 0 then begin
+ Item := MidStr(Text, J, I - J);
+ J := I + L;
+ end else begin
+ Item := Trim(RightStr(Text, Length(Text) - J + 1));
+ J := Length(Text) + 1;
+ end;
+
+ if (Item = #$A) or (Item = #13) then
+ Continue;
+
+ Item := StringReplace(Item, #13, '', [rfReplaceAll]);
+ Item := StringReplace(Item, #10, '', [rfReplaceAll]);
+ Item := StringReplace(Item, '\n', '', [rfReplaceAll, rfIgnoreCase]);
+ Item := Trim(Item);
+
+ I := Pos('::', Item);
+ if (Item = '') or (I < 1) then begin
+ if FAutoScore then
+ AItem.score := AItem.score - 20;
+ if Assigned(OutLog) then
+ OutLog.Add('бʽ');
+ Continue;
+ end else begin
+ SubTitle := Trim(LeftStr(Item, I - 1));
+ AURL := Trim(RightStr(Item, Length(Item) - I - 1));
+ if not CheckURL(ValidationURL(BaseURL, AURL), 'б' + SubTitle + '') then
+ AItem.score := AItem.score - 1;
+ end;
+ end;
+ except
+ Result := False;
+ Msg := Format('%sԳ(%s).', [Title, Exception(ExceptObject).Message]);
+ if Assigned(OutLog) then OutLog.Add(Msg);
+ if RaiseErr then
+ raise Exception.Create(Msg);
+ end;
+ end;
+
+var
+ T: Int64;
+ AScore, I: Integer;
+ URL: string;
+ Json: JSONObject;
+begin
+ Result := False;
+ if not Assigned(Item) then Exit;
+ Json := JSONObject.Create;
+ try
+ if Item.bookSourceUrl <> '' then begin
+ T := GetTimestamp;
+ if FAutoScore then
+ Item.score := 0;
+ Header.Clear;
+ Header.Add('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36');
+ if Item.header <> '' then begin
+ if Json.Parse(Item.header, True) then begin
+ for I := 0 to Json.Count do
+ Header.Add(Json.Items[i].FName, Json.Items[i].AsString);
+ end;
+ end;
+
+ // ԴURL
+ Result := CheckURL(Trim(Item.bookSourceUrl), 'ԴURL', RaiseErr, True);
+
+ if Result then begin
+ if FAutoScore then
+ Item.score := Item.score + 50;
+ // URL
+ URL := Trim(Item.searchUrl);
+ if CheckURL(ValidationURL(Trim(Item.bookSourceUrl), URL), 'ַ', RaiseErr) then begin
+ if FAutoScore then
+ Item.score := Item.score + 50;
+ end;
+ // ⷢб
+ if FAutoScore or (Assigned(OutLog)) then
+ CheckFindURL(Item, Trim(Item.bookSourceUrl), Trim(Item.exploreUrl), '', RaiseErr);
+ end;
+
+ if Assigned(OutLog) then
+ OutLog.Add(Format('ʱ %d ms.', [GetTimestamp - T]));
+ end else begin
+ if Assigned(OutLog) then
+ OutLog.Add('ԴURLδ.');
+ raise Exception.Create('ԴURLЧ');
+ end;
+ finally
+ FreeAndNil(Json);
+ end;
+end;
+
+function TForm1.CheckItem(Item: TBookSourceItem): Boolean;
+begin
+ Result := Assigned(Item) and (Item.bookSourceUrl <> '');
+end;
+
+function TForm1.CheckSaveState: Boolean;
+var
+ LR: Integer;
+begin
+ if FIsChange and (FCurIndex >= 0) and (FCurIndex < SrcList.Count) then begin
+ LR := MessageBox(Handle, 'ԴѾģǷ棿', 'ʾ', 64 + MB_YESNOCANCEL);
+ if LR = IDCANCEL then begin
+ Result := False;
+ Exit;
+ end;
+ if LR = IDYES then
+ S1Click(S1);
+ end;
+ Result := True;
+end;
+
+procedure TForm1.CopySelected;
+var
+ I: Integer;
+ Item: JSONObject;
+begin
+ FBookCopyData.Clear;
+ for I := SrcList.Count - 1 downto 0 do begin
+ if SrcList.Selected[I] then begin
+ Item := FBookCopyData.AddChildObject;
+ Item.Assign(JSONObject(FFilterList[I]));
+ end;
+ end;
+end;
+
+procedure TForm1.CutSelectedt;
+begin
+ CopySelected;
+ RemoveSelected;
+end;
+
+procedure TForm1.D1Click(Sender: TObject);
+begin
+ RemoveSelected;
+end;
+
+procedure TForm1.DispLog;
+begin
+ if FTaskStartTime > 0 then begin
+ if ProgressBar1.Visible then
+ StatusBar1.Panels[1].Text := Format('%s (%d/%d, %d%%) (ʱ: %dms)',
+ [FStateMsg, ProgressBar1.Position, ProgressBar1.Max, Round(ProgressBar1.Position / ProgressBar1.Max * 100),
+ GetTimestamp - FTaskStartTime])
+ else
+ StatusBar1.Panels[1].Text := Format('%s (ʱ: %dms)', [FStateMsg, GetTimestamp - FTaskStartTime])
+ end else
+ StatusBar1.Panels[1].Text := FStateMsg;
+end;
+
+procedure TForm1.DoCheckBookSourceItem(AJob: PJob);
+var
+ Item: TBookSourceItem;
+ State: PProcessState;
+ V: Integer;
+ IsOK: Boolean;
+ Http: THttpClient;
+ Header: THttpHeaders;
+begin
+ V := 0;
+ try
+ Http := THttpClient.Create(nil);
+ Http.ConnectionTimeOut := 30000;
+ Http.RecvTimeOut := 30000;
+ Http.AllowCookies := False;
+ Header := THttpHeaders.Create;
+
+ while (not AJob.IsTerminated) and (FWaitStop = 0) do begin
+ V := AtomicIncrement(FCurCheckIndex) - 1;
+
+ FLocker.Enter;
+ if (GetTimestamp - FCheckLastTime) > 100 then begin
+ FCheckLastTime := GetTimestamp;
+ New(State);
+ State.Min := 0;
+ State.Max := FCheckCount;
+ State.Value := FCheckFinish;
+ Workers.Post(DoUpdateProcess, State, True);
+ Sleep(10);
+ end;
+ FLocker.Leave;
+
+ if V < FCheckCount then begin
+ Item := TBookSourceItem(FBookSrcData.O[V]);
+ if not Assigned(Item) then Exit;
+
+ if (FAllSrc) or (FFilterList.IndexOf(Item) >= 0) then begin
+
+ try
+ IsOK := CheckBookSourceItem(Item, Http, Header);
+ except
+ IsOK := False;
+ end;
+
+ if IsOK then
+ Item.RemoveGroup('ʧЧ')
+ else
+ Item.AddGroup('ʧЧ');
+
+ Sleep(50);
+ end;
+
+ AtomicIncrement(FCheckFinish);
+ end else
+ Break;
+
+ end;
+ finally
+
+ if (V = FCheckCount) or (FWaitStop > 0) then begin
+ Sleep(100);
+ while not AJob.IsTerminated do begin
+ if (FCheckFinish >= FCheckCount) or (FWaitStop > 0) then
+ Break;
+
+ FLocker.Enter;
+ if (GetTimestamp - FCheckLastTime) > 1000 then begin
+ FCheckLastTime := GetTimestamp;
+ New(State);
+ State.Min := 0;
+ State.Max := FCheckCount;
+ State.Value := FCheckFinish;
+ Workers.Post(DoUpdateProcess, State, True);
+ Sleep(10);
+ end;
+ FLocker.Leave;
+
+ Sleep(100);
+ end;
+ Workers.Post(TaskFinish, nil, True);
+ end;
+
+ FreeAndNil(Http);
+ FreeAndNil(Header);
+ end;
+end;
+
+procedure TForm1.DoNotifyDataChange(AJob: PJob);
+begin
+ NotifyListChange;
+end;
+
+procedure TForm1.DoUpdateProcess(AJob: PJob);
+var
+ V: PProcessState;
+begin
+ if not Assigned(Self) then Exit;
+ V := AJob.Data;
+ if V = nil then
+ ProgressBar1.Visible := False
+ else begin
+ ProgressBar1.Min := V.Min;
+ ProgressBar1.Max := V.Max;
+ ProgressBar1.Position := V.Value;
+ ProgressBar1.Visible := Button1.Tag <> 0;
+ if V.NeedFree then
+ Dispose(V);
+ end;
+end;
+
+procedure TForm1.E1Click(Sender: TObject);
+begin
+ if SrcList.ItemIndex < 0 then Exit;
+ EditSource(TBookSourceItem(FFilterList[SrcList.ItemIndex]));
+end;
+
+procedure TForm1.E2Click(Sender: TObject);
+var
+ FName: JSONString;
+begin
+ if SaveDialog1.Execute(Handle) then begin
+ FName := SaveDialog1.FileName;
+ if ExtractFileExt(FName) = '' then
+ FName := FName + '.json';
+ FBookSrcData.SaveToFile(FName, 4, YxdStr.TTextEncoding.teUTF8, False);
+ end;
+end;
+
+procedure TForm1.EditDataChange(Sender: TObject);
+begin
+ FIsChange := True;
+end;
+
+procedure TForm1.EditSource(Item: TBookSourceItem);
+begin
+ ShowEditSource(Item,
+ procedure (Item: TBookSourceItem)
+ begin
+ if FBookSrcData.IndexOfObject(Item) < 0 then
+ FBookSrcData.Add(JSONObject(Item));
+ NotifyListChange;
+ if (FCurIndex >= 0) and (FCurIndex < FFilterList.Count) then begin
+ if TObject(FFilterList[FCurIndex]) = Item then begin
+ EditData.Text := TBookSourceItem(FFilterList[FCurIndex]).ToString(4);
+ FIsChange := False;
+ end;
+ end;
+ end
+ );
+end;
+
+procedure TForm1.ExportSelectedToFile;
+var
+ FName: JSONString;
+ I: Integer;
+ Item: TBookSourceItem;
+ Items: JSONArray;
+begin
+ if SrcList.SelCount = 0 then
+ Exit;
+ if SaveDialog1.Execute(Handle) then begin
+ FName := SaveDialog1.FileName;
+ if ExtractFileExt(FName) = '' then
+ FName := FName + '.json';
+ Items := JSONArray.Create;
+ try
+ for I := 0 to SrcList.Count - 1 do begin
+ if SrcList.Selected[I] then begin
+ Item := TBookSourceItem(JSONObject(FFilterList[I]));
+ if not Assigned(Item) then Continue;
+ Items.AddChildObject.Assign(Item);
+ end;
+ end;
+ Items.SaveToFile(FName, 4, YxdStr.TTextEncoding.teUTF8, False);
+ finally
+ Items.Free;
+ end;
+ end;
+end;
+
+procedure TForm1.F2Click(Sender: TObject);
+begin
+ FindSource(InputBox('Դ', 'ҪҵĹؼ', ''));
+end;
+
+procedure TForm1.FindSource(const FindStr: string);
+var
+ I, J: Integer;
+ Item: TBookSourceItem;
+begin
+ if FindStr = '' then Exit;
+ FLastFindSource := FindStr;
+ J := SrcList.ItemIndex + 1;
+ if J < 0 then J := 0;
+ for I := J to SrcList.Count - 1 do begin
+ Item := TBookSourceItem(JSONObject(FFilterList[I]));
+ if not Assigned(Item) then Continue;
+ J := Pos(FindStr, Item.bookSourceName);
+ if (Pos(FindStr, Item.bookSourceName) > 0) or (URL1.Checked and (Pos(FindStr, Item.bookSourceUrl) > 0)) then begin
+ SrcList.ClearSelection;
+ SrcList.ItemIndex := I;
+ SrcList.Selected[I] := True;
+ SrcList.ScrollBy(0, SrcList.ItemHeight * I + 1);
+ Exit;
+ end;
+ end;
+ LogD('Ҳ "' + FindStr + '" ');
+end;
+
+procedure TForm1.FormCreate(Sender: TObject);
+begin
+ JsonNameAfterSpace := True;
+ JsonCaseSensitive := False;
+ FBookCopyData := JSONArray.Create;
+ FBookSrcData := JSONArray.Create;
+ FBookGroups := TStringHash.Create(997);
+ FFilterList := TList.Create;
+ FLocker := TCriticalSection.Create;
+
+ FWaitCheckBookSourceSingId := Workers.RegisterSignal('WaitCheckBookSource');
+ Workers.PostWait(WaitCheckBookSource, FWaitCheckBookSourceSingId);
+end;
+
+procedure TForm1.FormDestroy(Sender: TObject);
+begin
+ FreeAndNil(FBookSrcData);
+ FreeAndNil(FBookGroups);
+ FreeAndNil(FFilterList);
+ FreeAndNil(FBookCopyData);
+ FreeAndNil(FLocker);
+end;
+
+procedure TForm1.FormShow(Sender: TObject);
+begin
+ DragAcceptFiles(SrcList.Handle, True);
+ DragAcceptFiles(StaticText1.Handle, True);
+
+ OldListWndProc := SrcList.WindowProc;
+ OldTextWndProc := StaticText1.WindowProc;
+ SrcList.WindowProc := SrcListWndProc;
+ StaticText1.WindowProc := SrcListTextWndProc;
+
+ NotifyListChange;
+end;
+
+procedure TForm1.G1Click(Sender: TObject);
+var
+ IsDX: Boolean;
+begin
+ if FLastSortFlag <> 2 then begin
+ FLastSortFlag := 2;
+ IsDX := False;
+ end else begin
+ IsDX := True;
+ FLastSortFlag := 0;
+ end;
+ FBookSrcData.Sort(
+ function (A, B: Pointer): Integer
+ var
+ Item1: PJSONValue absolute A;
+ Item2: PJSONValue absolute B;
+ S1, S2: string;
+ begin
+ if (Item1.FType = Item2.FType) and (Item1.FType = jdtObject) and
+ (Item1.AsJsonObject <> nil) and (Item2.AsJsonObject <> nil)
+ then begin
+ S1 := TBookSourceItem(Item1.AsJsonObject).bookSourceGroup;
+ S2 := TBookSourceItem(Item2.AsJsonObject).bookSourceGroup;
+ if IsDX then
+ Result := CompareStr(S2, S1)
+ else
+ Result := CompareStr(S1, S2);
+ end else
+ Result := 0;
+ end
+ );
+ NotifyListChange(1);
+end;
+
+procedure TForm1.H2Click(Sender: TObject);
+var
+ FindStr, NewStr: string;
+ I, Flag: Integer;
+ Item: TBookSourceItem;
+begin
+ Flag := 0;
+ if ShowReplaceGroup(Self, '滻 - Դ', FindStr, NewStr, Flag) then begin
+ if (FindStr <> '') and (Flag = 0) then
+ Exit;
+ for I := 0 to FBookSrcData.Count - 1 do begin
+ Item := TBookSourceItem(FBookSrcData.O[I]);
+ if not Assigned(Item) then Continue;
+ if Flag = 0 then begin
+ Item.bookSourceGroup := StringReplace(Trim(Item.bookSourceGroup), FindStr, NewStr, [rfReplaceAll, rfIgnoreCase]);
+ end else begin
+ if NewStr = '' then
+ Item.RemoveGroup(FindStr)
+ else
+ Item.ReplaceGroup(FindStr, NewStr);
+ end;
+ end;
+ NotifyListChange();
+ end;
+end;
+
+procedure TForm1.I1Click(Sender: TObject);
+var
+ Msg: string;
+begin
+ Msg := Application.Title + sLineBreak + 'YangYxd Ȩ 2019';
+ MessageBox(Handle, PChar(Msg), '', 64);
+end;
+
+procedure TForm1.Log(const Msg: string);
+begin
+ LogD(Msg);
+ FStateMsg := Msg;
+ DispLog();
+end;
+
+procedure TForm1.LogD(const Msg: string);
+begin
+ edtLog.Lines.Add(Format('[%s] %s', [FormatDateTime('hh:mm:ss.zzz', Now), Msg]));
+end;
+
+procedure TForm1.N14Click(Sender: TObject);
+begin
+ FindSource(FLastFindSource);
+end;
+
+procedure TForm1.N15Click(Sender: TObject);
+var
+ I: Integer;
+ GName: string;
+ Item: TBookSourceItem;
+begin
+ GName := InputBox('ӷ', '', '');
+ if GName = '' then Exit;
+
+ for I := 0 to SrcList.Count - 1 do begin
+ if SrcList.Selected[I] then begin
+ Item := TBookSourceItem(JSONObject(FFilterList[I]));
+ if not Assigned(Item) then Continue;
+ Item.AddGroup(GName);
+ end;
+ end;
+ NotifyListChange;
+end;
+
+procedure TForm1.N17Click(Sender: TObject);
+var
+ I: Integer;
+ GName: string;
+ Item: TBookSourceItem;
+begin
+ GName := InputBox('ӷ', '', '');
+ if GName = '' then Exit;
+
+ for I := 0 to SrcList.Count - 1 do begin
+ if SrcList.Selected[I] then begin
+ Item := TBookSourceItem(JSONObject(FFilterList[I]));
+ if not Assigned(Item) then Continue;
+ Item.RemoveGroup(GName);
+ end;
+ end;
+ NotifyListChange;
+end;
+
+procedure TForm1.N18Click(Sender: TObject);
+begin
+ ExportSelectedToFile;
+end;
+
+procedure TForm1.N7Click(Sender: TObject);
+var
+ Item: TBookSourceItem;
+begin
+ Item := TBookSourceItem(JSONObject.Create);
+ EditSource(Item);
+end;
+
+procedure TForm1.NotifyListChange(Flag: Integer);
+var
+ I, J: Integer;
+ Key: string;
+ Item: TBookSourceItem;
+
+ function EqualsBookType(const ABookType: string): Boolean;
+ begin
+ Result := (FBookType = '') or (FBookType = UpperCase(ABookType)) or
+ ((FBookType = 'TEXT') and (ABookType = ''));
+ end;
+
+begin
+ J := FCurIndex;
+
+ if Flag = 0 then begin
+ for I := 0 to FBookSrcData.Count - 1 do
+ UpdateBookGroup(TBookSourceItem(FBookSrcData.O[I]));
+ bookGroupList.Items.Clear;
+ bookGroupList.Items.Add('');
+ bookGroupList.Items.Add('ʧЧ');
+ FBookGroups.GetKeyList(bookGroupList.Items);
+ end;
+
+ FFilterList.Clear;
+ Key := LowerCase(bookGroupList.Text);
+ if Key <> '' then begin
+ for I := 0 to FBookSrcData.Count - 1 do begin
+ Item := TBookSourceItem(FBookSrcData.O[I]);
+ if Item.bookSourceType = 0 then begin // EqualsBookType(Item.bookSourceType)
+ if (Pos(Key, Item.bookSourceGroup) > 0) or (Pos(Key, Item.bookSourceName) > 0) then
+ FFilterList.Add(Item);
+ end;
+ end;
+ end else begin
+ for I := 0 to FBookSrcData.Count - 1 do begin
+ Item := TBookSourceItem(FBookSrcData.O[I]);
+ if Item.bookSourceType = 0 then // EqualsBookType(Item.bookSourceType)
+ FFilterList.Add(Item);
+ end;
+ end;
+
+ SrcList.Count := FFilterList.Count;
+ StaticText1.Visible := SrcList.Count = 0;
+ if (J < SrcList.Count) and (J >= 0) then begin
+ SrcList.ClearSelection;
+ SrcList.ItemIndex := J;
+ SrcList.Selected[J] := True;
+ SrcListClick(SrcList);
+ end;
+
+ SrcList.ShowHint := SrcList.Count = 0;
+ StatusBar1.Panels[0].Text := Format('Դ%d, ǰ: %d', [FBookSrcData.Count, FFilterList.Count]);
+end;
+
+procedure TForm1.O1Click(Sender: TObject);
+begin
+ OpenDialog1.Title := 'Դļ';
+ if OpenDialog1.Execute(Handle) then begin
+ FBookSrcData.Clear;
+ AddSrcFile(OpenDialog1.FileName);
+ end;
+end;
+
+procedure TForm1.O2Click(Sender: TObject);
+var
+ IsDX: Boolean;
+begin
+ if FLastSortFlag <> 4 then begin
+ FLastSortFlag := 4;
+ IsDX := False;
+ end else begin
+ IsDX := True;
+ FLastSortFlag := 0;
+ end;
+ FBookSrcData.Sort(
+ function (A, B: Pointer): Integer
+ var
+ Item1: PJSONValue absolute A;
+ Item2: PJSONValue absolute B;
+ begin
+ if (Item1.FType = Item2.FType) and (Item1.FType = jdtObject) and
+ (Item1.AsJsonObject <> nil) and (Item2.AsJsonObject <> nil)
+ then begin
+ if IsDX then
+ Result := TBookSourceItem(Item1.AsJsonObject).score - TBookSourceItem(Item2.AsJsonObject).score
+ else
+ Result := TBookSourceItem(Item2.AsJsonObject).score - TBookSourceItem(Item1.AsJsonObject).score;
+ end else
+ Result := 0;
+ end
+ );
+ NotifyListChange(1);
+end;
+
+procedure TForm1.P1Click(Sender: TObject);
+begin
+ EditData.PasteFromClipboard;
+end;
+
+procedure TForm1.P2Click(Sender: TObject);
+begin
+ Paste;
+end;
+
+procedure TForm1.Paste;
+var
+ I: Integer;
+begin
+ for I := 0 to FBookCopyData.Count - 1 do
+ FBookSrcData.AddChildObject.Assign(FBookCopyData.O[I]);
+ NotifyListChange();
+end;
+
+procedure TForm1.PopupMenu1Popup(Sender: TObject);
+begin
+ C4.Enabled := SrcList.SelCount > 0;
+ X2.Enabled := C4.Enabled;
+ C3.Enabled := C4.Enabled;
+ E1.Enabled := C4.Enabled;
+ D1.Enabled := C4.Enabled;
+ P2.Enabled := FBookCopyData.Count > 0;
+ N14.Enabled := FLastFindSource <> '';
+end;
+
+procedure TForm1.PopupMenu2Popup(Sender: TObject);
+begin
+ S1.Enabled := SrcList.ItemIndex >= 0;
+ P1.Enabled := EditData.CanPaste;
+ X1.Enabled := EditData.SelLength > 0;
+ C2.Enabled := X1.Enabled;
+ R1.Enabled := EditData.CanUndo;
+ Z1.Enabled := EditData.CanRedo;
+ W1.Checked := EditData.WordWrap;
+end;
+
+procedure TForm1.R1Click(Sender: TObject);
+begin
+ EditData.Undo;
+end;
+
+procedure TForm1.R2Click(Sender: TObject);
+begin
+ ShellExecute(0, 'OPEN', PChar('https://github.com/yangyxd/MyBookshelf'), nil, nil, SW_SHOWMAXIMIZED)
+end;
+
+procedure TForm1.RadioButton1Click(Sender: TObject);
+begin
+ BookType := '';
+end;
+
+procedure TForm1.RadioButton2Click(Sender: TObject);
+begin
+ BookType := 'TEXT';
+end;
+
+procedure TForm1.RadioButton3Click(Sender: TObject);
+begin
+ BookType := 'AUDIO';
+end;
+
+procedure TForm1.RadioButton4Click(Sender: TObject);
+begin
+ BookType := 'IMAGE';
+end;
+
+procedure TForm1.RemoveRepeat(AJob: PJob);
+var
+ CheckName: Boolean;
+
+ function Equals(A, B: TBookSourceItem): Boolean;
+ begin
+ Result :=
+ (LowerCase(A.bookSourceUrl) = LowerCase(B.bookSourceUrl)) and
+ (LowerCase(A.loginUrl) = LowerCase(B.loginUrl)) and
+ (LowerCase(A.header) = LowerCase(B.header)) and
+ (LowerCase(A.exploreUrl) = LowerCase(B.exploreUrl)) and
+ (LowerCase(A.ruleContent_content) = LowerCase(B.ruleContent_content)) and
+ (LowerCase(A.ruleContent_nextContentUrl) = LowerCase(B.ruleContent_nextContentUrl)) and
+ (LowerCase(A.ruleSearch_kind) = LowerCase(B.ruleSearch_kind)) and
+ (LowerCase(A.ruleSearch_lastChapter) = LowerCase(B.ruleSearch_lastChapter)) and
+ (LowerCase(A.ruleSearch_name) = LowerCase(B.ruleSearch_name)) and
+ (A.bookSourceType = B.bookSourceType) and
+ (LowerCase(A.ruleSearch_bookUrl) = LowerCase(B.ruleSearch_bookUrl)) and
+ (LowerCase(A.ruleSearch_coverUrl) = LowerCase(B.ruleSearch_coverUrl)) and
+ (LowerCase(A.ruleSearch_intro) = LowerCase(B.ruleSearch_intro)) and
+ (LowerCase(A.bookUrlPattern) = LowerCase(B.bookUrlPattern)) and
+ (LowerCase(A.ruleToc_chapterName) = LowerCase(B.ruleToc_chapterName)) and
+ (LowerCase(A.ruleToc_chapterList) = LowerCase(B.ruleToc_chapterList)) and
+ (LowerCase(A.ruleToc_chapterUrl) = LowerCase(B.ruleToc_chapterUrl)) and
+ (LowerCase(A.ruleToc_nextTocUrl) = LowerCase(B.ruleToc_nextTocUrl)) and
+ (LowerCase(A.ruleContent_webJs) = LowerCase(B.ruleContent_webJs)) and
+ (LowerCase(A.ruleBookInfo_author) = LowerCase(B.ruleBookInfo_author)) and
+ (LowerCase(A.ruleBookInfo_name) = LowerCase(B.ruleBookInfo_name)) and
+ (LowerCase(A.ruleExplore_bookList) = LowerCase(B.ruleExplore_bookList)) and
+ (LowerCase(A.ruleExplore_name) = LowerCase(B.ruleExplore_name)) and
+ (LowerCase(A.ruleExplore_lastChapter) = LowerCase(B.ruleExplore_lastChapter));
+ if not CheckName then
+ Result := Result and
+ (LowerCase(A.bookSourceName) = LowerCase(B.bookSourceName)) and
+ (LowerCase(A.bookSourceGroup) = LowerCase(B.bookSourceGroup));
+ end;
+
+var
+ I, J, LastCount, ST: Integer;
+ Item: TBookSourceItem;
+ T: TProcessState;
+ State: PProcessState;
+ List: TList;
+begin
+ I := 0;
+ LastCount := FBookSrcData.Count;
+ CheckName := Boolean(Integer(AJob.Data));
+
+ T.STime := GetTimestamp;
+ T.Min := 0;
+ T.Value := 0;
+ ST := 1000;
+
+ try
+
+ while I < FBookSrcData.Count do begin
+ Item := TBookSourceItem(FBookSrcData.O[I]);
+ Inc(I);
+
+ if (not FAllSrc) and (FFilterList.IndexOf(Item) < 0) then
+ Continue;
+
+ if FAutoDel then begin
+ for J := FBookSrcData.Count - 1 downto I do begin
+ if Equals(Item, TBookSourceItem(FBookSrcData.O[J])) then
+ FBookSrcData.Remove(J);
+ end;
+ end;
+
+ if FAutoFind then begin
+ if Item.exploreUrl <> '' then
+ Item.AddGroup('')
+ else
+ Item.RemoveGroup('');
+ end;
+
+ if AJob.IsTerminated or (FWaitStop > 0) then
+ Break;
+ if GetTimestamp - T.STime > ST then begin
+ ST := 200;
+ T.Value := I;
+ T.Max := FBookSrcData.Count;
+ New(State);
+ State^ := T;
+ State.NeedFree := True;
+ Workers.Post(DoUpdateProcess, State, True);
+ end;
+ end;
+
+ finally
+ if LastCount <> FBookSrcData.Count then
+ Workers.Post(DoNotifyDataChange, nil, True);
+ Sleep(100);
+ Workers.Post(TaskFinish, nil, True);
+ end;
+end;
+
+procedure TForm1.RemoveSelected;
+var
+ I, V: Integer;
+begin
+ for I := SrcList.Count - 1 downto 0 do begin
+ if SrcList.Selected[I] then begin
+ V := FBookSrcData.IndexOfObject(JSONObject(FFilterList[I]));
+ if V >= 0 then
+ FBookSrcData.Remove(V);
+ end;
+ end;
+ NotifyListChange;
+end;
+
+procedure TForm1.S1Click(Sender: TObject);
+var
+ S: string;
+ Item: TBookSourceItem;
+begin
+ if (FCurIndex < 0) or (FCurIndex >= SrcList.Count) then Exit;
+ Item := TBookSourceItem(FFilterList[FCurIndex]);
+ if not Assigned(Item) then Exit;
+ try
+ FIsChange := False;
+ S := Item.ToString();
+ Item.Parse(EditData.Text);
+ if not CheckItem(Item) then
+ Item.Parse(S);
+ finally
+ NotifyListChange;
+ end;
+end;
+
+procedure TForm1.S2Click(Sender: TObject);
+var
+ IsDX: Boolean;
+begin
+ if FLastSortFlag <> 1 then begin
+ FLastSortFlag := 1;
+ IsDX := False;
+ end else begin
+ IsDX := True;
+ FLastSortFlag := 0;
+ end;
+ FBookSrcData.Sort(
+ function (A, B: Pointer): Integer
+ var
+ Item1: PJSONValue absolute A;
+ Item2: PJSONValue absolute B;
+ S1, S2: string;
+ begin
+ if (Item1.FType = Item2.FType) and (Item1.FType = jdtObject) and
+ (Item1.AsJsonObject <> nil) and (Item2.AsJsonObject <> nil)
+ then begin
+ S1 := TBookSourceItem(Item1.AsJsonObject).bookSourceName;
+ S2 := TBookSourceItem(Item2.AsJsonObject).bookSourceName;
+ if IsDX then
+ Result := CompareStr(S2, S1)
+ else
+ Result := CompareStr(S1, S2);
+ end else
+ Result := 0;
+ end
+ );
+ NotifyListChange(1);
+end;
+
+procedure TForm1.S3Click(Sender: TObject);
+begin
+ ExportSelectedToFile();
+end;
+
+procedure TForm1.SetBookType(const Value: string);
+begin
+ if UpperCase(FBookType) <> UpperCase(Value) then begin
+ FBookType := UpperCase(Value);
+ NotifyListChange();
+ end;
+end;
+
+procedure TForm1.SpeedButton1Click(Sender: TObject);
+begin
+ edtLog.Lines.Clear;
+end;
+
+procedure TForm1.SrcListClick(Sender: TObject);
+begin
+ if SrcList.ItemIndex < 0 then Exit;
+ if not CheckSaveState then Exit;
+ FCurIndex := SrcList.ItemIndex;
+ EditData.Text := TBookSourceItem(FFilterList[FCurIndex]).ToString(4);
+ FIsChange := False;
+end;
+
+procedure TForm1.SrcListData(Control: TWinControl; Index: Integer;
+ var Data: string);
+var
+ Item: TBookSourceItem;
+begin
+ if Index < FBookSrcData.Count then begin
+ Item := TBookSourceItem(FFilterList[index]);
+ Data := Format('%s%s', [Item.bookSourceGroup, Item.bookSourceName]);
+ end else
+ Data := '';
+end;
+
+procedure TForm1.SrcListDblClick(Sender: TObject);
+begin
+ E1Click(E1);
+end;
+
+procedure TForm1.SrcListKeyDown(Sender: TObject; var Key: Word;
+ Shift: TShiftState);
+
+ procedure PasteItems();
+ var
+ pGlobal : Thandle;
+ begin
+ OpenClipboard(Handle);
+ try
+ pGlobal := GetClipboardData(CF_HDROP); //ȡļ
+ if pGlobal > 0 then
+ AddSrcFiles(pGlobal);
+ finally
+ CloseClipboard;
+ end;
+ end;
+
+begin
+ if Key = VK_DELETE then begin
+ RemoveSelected();
+ end else if Key = Ord('V') then begin // ճ
+ PasteItems();
+ end;
+end;
+
+procedure TForm1.SrcListTextWndProc(var Message: TMessage);
+begin
+ if Message.Msg = WM_DROPFILES then
+ WMDropFiles(TWMDropFiles(Message))
+ else
+ OldTextWndProc(Message);
+end;
+
+procedure TForm1.SrcListWndProc(var Message: TMessage);
+begin
+ if Message.Msg = WM_DROPFILES then
+ WMDropFiles(TWMDropFiles(Message))
+ else
+ OldListWndProc(Message);
+end;
+
+procedure TForm1.T1Click(Sender: TObject);
+var
+ Item: TBookSourceItem;
+begin
+ Item := TBookSourceItem(JSONObject.Create);
+ try
+ Item.Parse(EditData.Text);
+ if CheckBookSourceItem(Item, True, edtLog.Lines) then
+ LogD('ϲ, ͨ!')
+ else
+ LogD('Դ쳣!');
+ EditData.Text := Item.ToString(4);
+ finally
+ FreeAndNil(Item);
+ end;
+end;
+
+procedure TForm1.TaskFinish(AJob: PJob);
+var
+ I: Integer;
+begin
+ I := AtomicDecrement(FTaskRef);
+ if (I <= 0) or (FWaitStop > 0) then begin
+ if (I = 0) and Assigned(Self) and (not (csDestroying in ComponentState)) then begin
+ NotifyListChange();
+ Button1Click(Button1);
+ end;
+ end else if not (csDestroying in ComponentState) then begin
+ Log('УԴ...');
+ Workers.SendSignal(FWaitCheckBookSourceSingId);
+ end;
+end;
+
+procedure TForm1.Timer1Timer(Sender: TObject);
+begin
+ DispLog;
+end;
+
+procedure TForm1.U1Click(Sender: TObject);
+var
+ Item: TBookSourceItem;
+begin
+ Item := TBookSourceItem(JSONObject.Create);
+ try
+ Item.Parse(EditData.Text);
+ Item.lastUpdateTime := GetCurJavaDateTime;
+ finally
+ FreeAndNil(Item);
+ end;
+end;
+
+procedure TForm1.UpdateBookGroup(Item: TBookSourceItem);
+var
+ J: Integer;
+ ARef: Number;
+ ABookGroup: TArray;
+ AGroup: string;
+begin
+ ABookGroup := Item.GetGroupList;
+
+ for J := 0 to High(ABookGroup) do begin
+ ARef := 0;
+ AGroup := Trim(ABookGroup[J]);
+ FBookGroups.TryGetValue(AGroup, ARef);
+ Inc(ARef);
+ FBookGroups.AddOrUpdate(AGroup, ARef);
+ end;
+end;
+
+procedure TForm1.URL1Click(Sender: TObject);
+begin
+ TMenuItem(Sender).Checked := not TMenuItem(Sender).Checked;
+end;
+
+procedure TForm1.W1Click(Sender: TObject);
+begin
+ EditData.WordWrap := not W1.Checked;
+end;
+
+procedure TForm1.W2Click(Sender: TObject);
+begin
+ ShellExecute(0, 'OPEN', PChar('http://www.cnblogs.com/yangyxd/'), nil, nil, SW_SHOWMAXIMIZED)
+end;
+
+procedure TForm1.W3Click(Sender: TObject);
+var
+ IsDX: Boolean;
+begin
+ if FLastSortFlag <> 3 then begin
+ FLastSortFlag := 3;
+ IsDX := False;
+ end else begin
+ IsDX := True;
+ FLastSortFlag := 0;
+ end;
+ FBookSrcData.Sort(
+ function (A, B: Pointer): Integer
+ var
+ Item1: PJSONValue absolute A;
+ Item2: PJSONValue absolute B;
+ begin
+ if (Item1.FType = Item2.FType) and (Item1.FType = jdtObject) and
+ (Item1.AsJsonObject <> nil) and (Item2.AsJsonObject <> nil)
+ then begin
+ if IsDX then
+ Result := TBookSourceItem(Item1.AsJsonObject).weight - TBookSourceItem(Item2.AsJsonObject).weight
+ else
+ Result := TBookSourceItem(Item2.AsJsonObject).weight - TBookSourceItem(Item1.AsJsonObject).weight;
+ end else
+ Result := 0;
+ end
+ );
+ NotifyListChange(1);
+end;
+
+procedure TForm1.WaitCheckBookSource(AJob: PJob);
+var
+ I, J: Integer;
+begin
+ if FBookSrcData.Count > 0 then begin
+ FCheckCount := FBookSrcData.Count;
+ FCurCheckIndex := 0;
+ FCheckFinish := 0;
+ J := Min(FBookSrcData.Count, FMaxWorkers);
+ for I := 0 to J - 1 do begin
+ if AJob.IsTerminated then
+ Break;
+ Workers.Post(DoCheckBookSourceItem, nil);
+ end;
+ end else
+ Workers.Post(TaskFinish, nil, True);
+end;
+
+procedure TForm1.WMDropFiles(var Msg: TWMDropFiles);
+begin
+ AddSrcFiles(Msg.Drop);
+end;
+
+procedure TForm1.X1Click(Sender: TObject);
+begin
+ EditData.CopyToClipboard;
+ EditData.SelText := '';
+end;
+
+procedure TForm1.X2Click(Sender: TObject);
+begin
+ CutSelectedt;
+end;
+
+procedure TForm1.Z1Click(Sender: TObject);
+begin
+ EditData.Redo;
+end;
+
+end.
diff --git a/tool/书源整理工具/uFrmReplaceGroup.dfm b/tool/书源整理工具/uFrmReplaceGroup.dfm
new file mode 100644
index 000000000..4554a0263
--- /dev/null
+++ b/tool/书源整理工具/uFrmReplaceGroup.dfm
@@ -0,0 +1,80 @@
+object frmReplaceGroup: TfrmReplaceGroup
+ Left = 0
+ Top = 0
+ BorderStyle = bsDialog
+ ClientHeight = 166
+ ClientWidth = 345
+ Color = clWindow
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -11
+ Font.Name = 'Tahoma'
+ Font.Style = []
+ OldCreateOrder = False
+ Position = poMainFormCenter
+ DesignSize = (
+ 345
+ 166)
+ PixelsPerInch = 96
+ TextHeight = 13
+ object Label1: TLabel
+ Left = 24
+ Top = 16
+ Width = 60
+ Height = 13
+ Caption = #26597#25214#20869#23481#65306
+ end
+ object Label2: TLabel
+ Left = 24
+ Top = 63
+ Width = 60
+ Height = 13
+ Caption = #26367#25442#20869#23481#65306
+ end
+ object Edit1: TEdit
+ Left = 24
+ Top = 35
+ Width = 289
+ Height = 21
+ TabOrder = 0
+ end
+ object Edit2: TEdit
+ Left = 24
+ Top = 82
+ Width = 289
+ Height = 21
+ TabOrder = 1
+ end
+ object CheckBox1: TCheckBox
+ Left = 24
+ Top = 128
+ Width = 97
+ Height = 17
+ Caption = #20840#23383#21305#37197
+ Checked = True
+ State = cbChecked
+ TabOrder = 2
+ end
+ object Button1: TButton
+ Left = 239
+ Top = 125
+ Width = 75
+ Height = 25
+ Anchors = [akRight, akBottom]
+ Caption = #30830#23450'(&O)'
+ Default = True
+ TabOrder = 3
+ OnClick = Button1Click
+ end
+ object Button2: TButton
+ Left = 151
+ Top = 125
+ Width = 75
+ Height = 25
+ Anchors = [akRight, akBottom]
+ Cancel = True
+ Caption = #21462#28040'(&C)'
+ ModalResult = 2
+ TabOrder = 4
+ end
+end
diff --git a/tool/书源整理工具/uFrmReplaceGroup.pas b/tool/书源整理工具/uFrmReplaceGroup.pas
new file mode 100644
index 000000000..28878c9f6
--- /dev/null
+++ b/tool/书源整理工具/uFrmReplaceGroup.pas
@@ -0,0 +1,62 @@
+unit uFrmReplaceGroup;
+
+interface
+
+uses
+ Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
+ Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
+
+type
+ TfrmReplaceGroup = class(TForm)
+ Label1: TLabel;
+ Edit1: TEdit;
+ Label2: TLabel;
+ Edit2: TEdit;
+ CheckBox1: TCheckBox;
+ Button1: TButton;
+ Button2: TButton;
+ procedure Button1Click(Sender: TObject);
+ private
+ { Private declarations }
+ public
+ { Public declarations }
+ end;
+
+var
+ frmReplaceGroup: TfrmReplaceGroup;
+
+function ShowReplaceGroup(Sender: TComponent; const Title: string; var FindStr, NewStr: string; var Flag: Integer): Boolean;
+
+implementation
+
+{$R *.dfm}
+
+function ShowReplaceGroup(Sender: TComponent; const Title: string; var FindStr, NewStr: string; var Flag: Integer): Boolean;
+var
+ F: TfrmReplaceGroup;
+begin
+ F := TfrmReplaceGroup.Create(Sender);
+ try
+ F.Caption := Title;
+ F.CheckBox1.Enabled := Flag = 0;
+ Result := F.ShowModal = mrOk;
+ if Result then begin
+ FindStr := Trim(F.Edit1.Text);
+ NewStr := Trim(F.Edit2.Text);
+ Flag := Ord(F.CheckBox1.Checked);
+ end;
+ finally
+ F.Free;
+ end;
+end;
+
+procedure TfrmReplaceGroup.Button1Click(Sender: TObject);
+begin
+// if Trim(Edit1.Text) = '' then begin
+// ShowMessage('Ҫҵ');
+// Exit;
+// end;
+ ModalResult := mrOk;
+end;
+
+end.
diff --git a/tool/书源整理工具/uFrmWait.dfm b/tool/书源整理工具/uFrmWait.dfm
new file mode 100644
index 000000000..1f755de5c
--- /dev/null
+++ b/tool/书源整理工具/uFrmWait.dfm
@@ -0,0 +1,62 @@
+object Form2: TForm2
+ Left = 0
+ Top = 0
+ BorderStyle = bsNone
+ BorderWidth = 1
+ Caption = 'Form2'
+ ClientHeight = 173
+ ClientWidth = 251
+ Color = clSilver
+ Font.Charset = DEFAULT_CHARSET
+ Font.Color = clWindowText
+ Font.Height = -11
+ Font.Name = 'Tahoma'
+ Font.Style = []
+ OldCreateOrder = False
+ Position = poOwnerFormCenter
+ OnClose = FormClose
+ PixelsPerInch = 96
+ TextHeight = 13
+ object Panel1: TPanel
+ Left = 0
+ Top = 0
+ Width = 251
+ Height = 173
+ Align = alClient
+ BevelOuter = bvNone
+ Color = clWindow
+ ParentBackground = False
+ TabOrder = 0
+ object Label1: TLabel
+ Left = 73
+ Top = 88
+ Width = 103
+ Height = 13
+ Alignment = taCenter
+ Caption = #27491#22312#22788#29702', '#35831#31561#24453'...'
+ end
+ object ActivityIndicator1: TActivityIndicator
+ AlignWithMargins = True
+ Left = 112
+ Top = 32
+ Animate = True
+ end
+ object Button1: TButton
+ Left = 71
+ Top = 120
+ Width = 106
+ Height = 25
+ Cancel = True
+ Caption = #21462#28040'(&C)'
+ TabOrder = 1
+ OnClick = Button1Click
+ end
+ end
+ object Timer1: TTimer
+ Enabled = False
+ Interval = 100
+ OnTimer = Timer1Timer
+ Left = 184
+ Top = 56
+ end
+end
diff --git a/tool/书源整理工具/uFrmWait.pas b/tool/书源整理工具/uFrmWait.pas
new file mode 100644
index 000000000..e32f1f8c2
--- /dev/null
+++ b/tool/书源整理工具/uFrmWait.pas
@@ -0,0 +1,77 @@
+unit uFrmWait;
+
+interface
+
+uses
+ Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
+ Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.WinXCtrls,
+ Vcl.ExtCtrls;
+
+type
+ TForm2 = class(TForm)
+ Panel1: TPanel;
+ ActivityIndicator1: TActivityIndicator;
+ Label1: TLabel;
+ Button1: TButton;
+ Timer1: TTimer;
+ procedure Button1Click(Sender: TObject);
+ procedure FormClose(Sender: TObject; var Action: TCloseAction);
+ procedure Timer1Timer(Sender: TObject);
+ private
+ { Private declarations }
+ public
+ { Public declarations }
+ end;
+
+var
+ Form2: TForm2;
+
+procedure ShowWaitDlg();
+procedure HideWaitDlg();
+
+implementation
+
+{$R *.dfm}
+
+uses
+ uFrmMain;
+
+var
+ fWait: TForm2;
+
+procedure ShowWaitDlg();
+begin
+ if Assigned(fWait) then
+ Exit;
+ fWait := TForm2.Create(Application);
+ fWait.ShowModal;
+end;
+
+procedure HideWaitDlg();
+begin
+ if Assigned(fWait) then
+ fWait.Timer1.Enabled := True;
+end;
+
+procedure TForm2.Button1Click(Sender: TObject);
+begin
+ if Form1.Button1.Tag <> 0 then begin
+ Timer1.Enabled := True;
+ Form1.Button1Click(Button1);
+ end;
+end;
+
+procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
+begin
+ fWait := nil;
+ Action := caFree;
+end;
+
+procedure TForm2.Timer1Timer(Sender: TObject);
+begin
+ if Form1.Button1.Tag = 0 then begin
+ ModalResult := mrCancel;
+ end;
+end;
+
+end.