Skip to content

Commit 3256339

Browse files
authored
Merge branch 'master' into master
2 parents 77a02cc + c05c3fd commit 3256339

104 files changed

Lines changed: 4435 additions & 1250 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
<div align="center">
2-
<img src="mangadownloader/md.ico" alt="Logo" style="width: 100px" />
3-
<h1>Free Manga Downloader 2 (FMD2)</h1>
2+
<img src="mangadownloader/md.ico" alt="Logo" width="80"/>
3+
4+
# Free Manga Downloader 2 (FMD2)
5+
6+
This is an active fork of the Free Manga Downloader which is a free open source application written in Object Pascal for managing and downloading manga from various websites. The source code was released under the GPLv2 license.
47

58
[![Supported Websites](https://img.shields.io/badge/Supported%20Websites-Blue?style=for-the-badge&color=purple)](https://github.com/dazedcat19/FMD2/blob/master/docs/SUPPORTED_WEBSITES.md)
69
[![Discord](https://img.shields.io/badge/Discord-FMD-Blue?style=for-the-badge&color=blue&logo=discord&logoColor=white)](https://discord.gg/cXKKgw3)
@@ -10,9 +13,7 @@
1013
[![Latest release](https://img.shields.io/github/release/dazedcat19/FMD2?style=for-the-badge)](https://github.com/dazedcat19/FMD2/releases/latest)
1114
[![Download latest release (Win32)](https://img.shields.io/github/downloads/dazedcat19/FMD2/latest/fmd_2.0.34.5_i386-win32.7z?style=for-the-badge&label=Win32)](https://github.com/dazedcat19/FMD2/releases/download/2.0.34.5/fmd_2.0.34.5_i386-win32.7z)
1215
[![Download latest release (Win64)](https://img.shields.io/github/downloads/dazedcat19/FMD2/latest/fmd_2.0.34.5_x86_64-win64.7z?style=for-the-badge&label=Win64)](https://github.com/dazedcat19/FMD2/releases/download/2.0.34.5/fmd_2.0.34.5_x86_64-win64.7z)
13-
</div>
1416

15-
<div align="center">
1617
<details>
1718
<summary>
1819
<h2>Project Samples</h2>
@@ -23,14 +24,11 @@
2324
![image](https://github.com/user-attachments/assets/001af028-5d25-48fe-a88c-5b03ab62982f)
2425

2526
</details>
26-
</div>
27-
28-
## About FMD2
29-
30-
This is an active fork of the Free Manga Downloader which is a free open source application written in Object Pascal for managing and downloading manga from various websites. The source code was released under the GPLv2 license.
3127

3228
## Features
3329

30+
<div align="left">
31+
3432
- Pure native code - No need to install .NET framework or Java VM.
3533
- Supported hundreds of manga websites.
3634
- Download all mangas at once.
@@ -41,8 +39,12 @@ This is an active fork of the Free Manga Downloader which is a free open source
4139
- Compress/convert downloaded chapters to ZIP/CBZ/PDF/EPUB.
4240
- Proxy support (HTTP/SOCK4/SOCK5).
4341

42+
</div>
43+
4444
## Build instructions
4545

46+
<div align="left">
47+
4648
In order to build FMD2 from the source code, you must install the latest stable version of Lazarus and Free Pascal Compiler:
4749
[![Lazarus](https://img.shields.io/badge/Lazarus%20IDE-Blue?style=for-the-badge&color=blue)](https://sourceforge.net/projects/lazarus/files/Lazarus%20Windows%2064%20bits/)
4850

@@ -58,7 +60,11 @@ The following packages and components are used for building FMD2:
5860
- [![MetaDarkStyle](https://img.shields.io/badge/MetaDarkStyle-GitHub-Blue?style=plastic&color=blue)](https://github.com/zamtmn/metadarkstyle)
5961
- [![Lazarus_CustomControls](https://img.shields.io/badge/Lazarus_CustomControls-GitHub-Blue?style=plastic&color=blue)](https://github.com/NhKPaNdA/Lazarus_CustomControls)
6062

61-
> [!NOTE]
63+
</div>
64+
65+
</div>
66+
67+
> [!IMPORTANT]
6268
> By default `InternetTools` uses [FLRE](https://github.com/BeRo1985/flre) and [PUCU](https://github.com/BeRo1985/PUCU) for its regex engine. Just copy the `FLRE.pas` and `PUCU.pas` to `InternetTools\data` folder. You can use Sorokin's RegExpr engine that comes with lazarus by adjusting the defines. But it's not recommended since the author of `InternetTools` prefer FLRE and doesn't always check the Sorokin's RegExpr compatibility when making an update.
6369
6470
> [!TIP]
@@ -85,11 +91,15 @@ These tools and libraries are not part of the source. You have to either downloa
8591
- `--max-flush-queue=256` override max number of update before commiting to database engine.
8692
- `--max-big-flush-queue=16384` override max number of update before commiting to database. Internally used when making large update to databases in one go. Be careful when reducing the number it might slowing down FMD2 significantly.
8793
- `--backup-interval=10` override backup databases interval (minutes).
88-
94+
95+
<div align="center">
96+
8997
## Localization
9098

99+
</div>
100+
91101
Translations are stored inside `languages` folder with `.po` extension.
92102
In order to translate FMD2 to your native language, you can copy `fmd.po` and rename it to `fmd.xx.po` where `xx` stand for two-letter language code.
93103
Additionally you can add country code at the end of language code. For reference you can look at [List of ISO 639 language codes](https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes) and [ISO 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1). For example `id_ID` will be recognized as `Bahasa Indonesia (Indonesia)`.
94-
To translate the content of the file you need to use translation tools like [Poedit](https://poedit.net).
104+
To translate the content of the file, you need to use translation tools like [Poedit](https://poedit.net).
95105
Once you have finished translating all of its content, you can launch FMD2 and it will automatically detect your new languages upon startup.

baseunits/StatusBarDownload.pas

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ procedure TStatusBarDownload.SyncCreate;
128128
StatusBarResize(FStatusBar);
129129

130130
FTimerRepaint := TTimer.Create(FStatusBar);
131-
FTimerRepaint.Interval := 100;
131+
FTimerRepaint.Interval := 50;
132132
FTimerRepaint.OnTimer := @TimerRepaintTimer;
133133
FTimerRepaint.Enabled := True;
134134
FNeedRepaint := True;

baseunits/uBaseUnit.pas

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ TMangaInfo = class
339339
destructor Destroy; override;
340340
function ModuleID: String; inline;
341341
function Website: String; inline;
342+
function FullLink: String; inline;
342343
procedure Clear;
343344
function Clone: TMangaInfo;
344345
end;
@@ -357,6 +358,7 @@ TMangaInfo = class
357358
DateAdded,
358359
DateLastDownloaded: TDateTime;
359360
iProgress: Integer;
361+
MangaPtr: TMangaInfo;
360362
private
361363
FModule: Pointer;
362364
FModuleID: String;
@@ -2973,6 +2975,11 @@ function TMangaInfo.Website: String;
29732975
Result := TModuleContainer(Module).Name;
29742976
end;
29752977

2978+
function TMangaInfo.FullLink: String;
2979+
begin
2980+
Result := FillHost(TModuleContainer(Module).RootURL, Link);
2981+
end;
2982+
29762983
procedure TMangaInfo.Clear;
29772984
begin
29782985
URL := '';

baseunits/uComicInfo.pas

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
{
2+
File: uComicInfo.pas
3+
License: GPLv2
4+
This unit is a part of Free Manga Downloader
5+
}
6+
7+
unit uComicInfo;
8+
9+
{$mode objfpc}{$H+}
10+
11+
interface
12+
13+
uses
14+
Classes, SysUtils, uBaseUnit;
15+
16+
type
17+
18+
{ TComicInfo }
19+
20+
TComicInfo = class
21+
private
22+
class function EscapeXML(const AText: String): String; static;
23+
class function FormatDate(ADate: TDateTime): String; static;
24+
public
25+
class function GenerateComicInfoXML(
26+
const AFilePath: String;
27+
const ATitle: String;
28+
const ANumber: Integer;
29+
const AMangaInfo: TMangaInfo;
30+
const APageCount: Integer
31+
): Boolean; static;
32+
33+
class function GenerateExtraEPUBMetadata(
34+
const AFilePath: String;
35+
const ATitle: String;
36+
const ANumber: Integer;
37+
const AMangaInfo: TMangaInfo
38+
): String; static;
39+
end;
40+
41+
implementation
42+
43+
{ TComicInfo }
44+
45+
class function TComicInfo.EscapeXML(const AText: String): String;
46+
begin
47+
Result := AText;
48+
Result := StringReplace(Result, '&', '&amp;', [rfReplaceAll]);
49+
Result := StringReplace(Result, '<', '&lt;', [rfReplaceAll]);
50+
Result := StringReplace(Result, '>', '&gt;', [rfReplaceAll]);
51+
Result := StringReplace(Result, '"', '&quot;', [rfReplaceAll]);
52+
Result := StringReplace(Result, '''', '&apos;', [rfReplaceAll]);
53+
Result := Trim(Result)
54+
end;
55+
56+
class function TComicInfo.FormatDate(ADate: TDateTime): String;
57+
begin
58+
if ADate = 0 then
59+
Result := ''
60+
else
61+
Result := FormatDateTime('yyyy-MM-dd', ADate);
62+
end;
63+
64+
class function TComicInfo.GenerateComicInfoXML(
65+
const AFilePath: String;
66+
const ATitle: String;
67+
const ANumber: Integer;
68+
const AMangaInfo: TMangaInfo;
69+
const APageCount: Integer
70+
): Boolean;
71+
var
72+
XML: TStringList;
73+
L: TStringList;
74+
begin
75+
Result := False;
76+
if not Assigned(AMangaInfo) then Exit;
77+
78+
L := nil;
79+
XML := TStringList.Create;
80+
try
81+
XML.Add('<?xml version="1.0" encoding="UTF-8"?>');
82+
XML.Add('<ComicInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">');
83+
84+
if AMangaInfo.Title <> '' then
85+
XML.Add(' <Series>' + EscapeXML(AMangaInfo.Title) + '</Series>');
86+
87+
if ATitle <> '' then
88+
XML.Add(' <Title>' + EscapeXML(ATitle) + '</Title>')
89+
else if AMangaInfo.Title <> '' then
90+
XML.Add(' <Title>' + EscapeXML(AMangaInfo.Title) + '</Title>');
91+
92+
XML.Add(' <Number>' + IntToStr(ANumber + 1) + '</Number>');
93+
94+
// If the Count matches the Volume count or Chapter count, then Kavita will assume the Series is Completed (you own all items of the series)
95+
if (AMangaInfo.Status = '0') then // completed
96+
XML.Add(' <Count>' + EscapeXML(IntToStr(AMangaInfo.ChapterLinks.Count)) + '</Count>');
97+
98+
if AMangaInfo.Summary <> '' then
99+
XML.Add(' <Summary>' + EscapeXML(AMangaInfo.Summary) + '</Summary>');
100+
101+
if AMangaInfo.Authors <> '' then
102+
XML.Add(' <Writer>' + EscapeXML(AMangaInfo.Authors) + '</Writer>');
103+
104+
if AMangaInfo.Artists <> '' then
105+
XML.Add(' <Penciller>' + EscapeXML(AMangaInfo.Artists) + '</Penciller>');
106+
107+
if AMangaInfo.Genres <> '' then
108+
XML.Add(' <Genre>' + EscapeXML(AMangaInfo.Genres) + '</Genre>');
109+
110+
case AMangaInfo.Status of
111+
'0': XML.Add(' <Notes>Series completed</Notes>');
112+
'1': XML.Add(' <Notes>Series ongoing</Notes>');
113+
'2': XML.Add(' <Notes>Series on hold</Notes>');
114+
'3': XML.Add(' <Notes>Series cancelled</Notes>');
115+
end;
116+
117+
XML.Add(' <Web>' + EscapeXML(AMangaInfo.FullLink) + '</Web>');
118+
119+
if APageCount > 0 then
120+
XML.Add(' <PageCount>' + IntToStr(APageCount) + '</PageCount>');
121+
122+
XML.Add('</ComicInfo>');
123+
124+
if Trim(XML.Text) <> '' then
125+
begin
126+
L := TStringList.Create;
127+
L.Text := XML.Text;
128+
L.SaveToFile(AFilePath);
129+
Result := FileExists(AFilePath);
130+
end;
131+
finally
132+
L.Free;
133+
XML.Free;
134+
end;
135+
end;
136+
137+
class function TComicInfo.GenerateExtraEPUBMetadata(
138+
const AFilePath: String;
139+
const ATitle: String;
140+
const ANumber: Integer;
141+
const AMangaInfo: TMangaInfo
142+
): String;
143+
var
144+
XML: TStringList;
145+
StatusText: String;
146+
begin
147+
Result := '';
148+
if not Assigned(AMangaInfo) then Exit;
149+
150+
XML := TStringList.Create;
151+
try
152+
if AMangaInfo.Authors <> '' then
153+
XML.Add(' <dc:creator opf:role="aut">' + EscapeXML(AMangaInfo.Authors) + '</dc:creator>');
154+
155+
if AMangaInfo.Artists <> '' then
156+
XML.Add(' <dc:creator opf:role="art">' + EscapeXML(AMangaInfo.Artists) + '</dc:creator>');
157+
158+
if AMangaInfo.Summary <> '' then
159+
XML.Add(' <dc:description>' + EscapeXML(AMangaInfo.Summary) + '</dc:description>');
160+
161+
if AMangaInfo.Title <> '' then
162+
XML.Add(' <meta name="series" content="' + EscapeXML(AMangaInfo.Title) + '"/>');
163+
164+
if ANumber >= 0 then
165+
XML.Add(' <meta name="chapter" content="' + IntToStr(ANumber + 1) + '"/>');
166+
167+
if AMangaInfo.Genres <> '' then
168+
XML.Add(' <meta name="genre" content="' + EscapeXML(AMangaInfo.Genres) + '"/>');
169+
170+
case AMangaInfo.Status of
171+
'0': StatusText := 'completed';
172+
'1': StatusText := 'ongoing';
173+
'2': StatusText := 'on_hold';
174+
'3': StatusText := 'cancelled';
175+
else
176+
StatusText := '';
177+
end;
178+
179+
if StatusText <> '' then
180+
XML.Add(' <meta name="status" content="' + StatusText + '"/>');
181+
182+
if AMangaInfo.Website <> '' then
183+
XML.Add(' <meta name="publisher" content="' + EscapeXML(AMangaInfo.Website) + '"/>');
184+
185+
XML.Add(' <meta property="dcterms:modified">' +
186+
FormatDateTime('yyyy-mm-dd"T"hh:nn:ss"Z"', Now) +
187+
'</meta>');
188+
189+
if Trim(XML.Text) <> '' then
190+
Result := XML.Text;
191+
finally
192+
XML.Free;
193+
end;
194+
end;
195+
196+
end.

baseunits/uDownloadsManager.pas

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ interface
1313
uses
1414
LazFileUtils, Classes, SysUtils, ExtCtrls, typinfo, fgl, FileUtil, synautil,
1515
blcksock, MultiLog, uBaseUnit, uPacker, uMisc, DownloadedChaptersDB, FMDOptions, ImgInfos,
16-
httpsendthread, DownloadsDB, BaseThread, SQLiteData, dateutils, strutils, ImageMagickManager;
16+
httpsendthread, DownloadsDB, BaseThread, SQLiteData, dateutils, strutils, ImageMagickManager,
17+
uComicInfo;
1718

1819
type
1920
TDownloadStatusType = (
@@ -553,6 +554,7 @@ function TTaskThread.GetFileName(const AWorkId: Integer): String;
553554
function TTaskThread.Compress: Boolean;
554555
var
555556
uPacker: TPacker;
557+
ComicInfoPath: String;
556558
i: Integer;
557559
FilePath, FileExt: String;
558560
begin
@@ -594,11 +596,38 @@ function TTaskThread.Compress: Boolean;
594596
end;
595597
end;
596598

599+
// Add ComicInfo.xml to the ZIP or CBZ archive
600+
if (Container.Manager.CompressType = 1) or (Container.Manager.CompressType = 2) then
601+
begin
602+
ComicInfoPath := IncludeTrailingPathDelimiter(CurrentWorkingDir) + 'ComicInfo.xml';
603+
TComicInfo.GenerateComicInfoXML(
604+
ComicInfoPath, // ComicInfo.xml
605+
Container.ChapterNames[Container.CurrentDownloadChapterPtr], // Title (Chapter Title)
606+
Container.CurrentDownloadChapterPtr, // Number (Issue/Chapter number)
607+
Container.DownloadInfo.MangaPtr, // MangaInfo (Series, Writer, Penciller, Genre, Summary, Web)
608+
Container.PageLinks.Count // PageCount
609+
);
610+
if FileExists(ComicInfoPath) then uPacker.FileList.Add(ComicInfoPath);
611+
end;
612+
613+
// Add extra metadata to content.opf file in the EPUB archive
614+
if (Container.Manager.CompressType = 4) then
615+
uPacker.MetaData := TComicInfo.GenerateExtraEPUBMetadata(
616+
ComicInfoPath,
617+
Container.ChapterNames[Container.CurrentDownloadChapterPtr],
618+
Container.CurrentDownloadChapterPtr,
619+
Container.DownloadInfo.MangaPtr
620+
);
621+
597622
Result := uPacker.Execute;
598623
if not Result then
599624
begin
600625
Logger.SendWarning(Self.ClassName + ', failed to compress. ' + uPacker.SavedFileName);
601626
end;
627+
628+
// Clean up ComicInfo.xml/content.opf file after compression
629+
if FileExists(ComicInfoPath) then DeleteFile(ComicInfoPath);
630+
602631
except
603632
on E: Exception do
604633
begin

0 commit comments

Comments
 (0)