Skip to content

Commit 222126f

Browse files
committed
Merge branch 'dev'
2 parents c8ee7f5 + 607c854 commit 222126f

File tree

12 files changed

+134
-90
lines changed

12 files changed

+134
-90
lines changed

README.md

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
<p align="center" width="40%">
2-
<img alt="Lezhin Comics Downloader" src="./src/main/resources/assets/main-image.png">
1+
<p align="center">
2+
<img alt="Lezhin Comics Downloader" src="./src/main/resources/assets/lezhin-comics-downloader-logo.png" width="20%">
33
</p>
44

5+
<h1 align="center">Lezhin Comics Downloader</h1>
56

7+
<p align="center">Downloader for lezhin comics</p>
68

79
<p align="center">
810
<a href="https://lgtm.com/projects/g/ImSejin/lezhin-comics-downloader/context:java"><img alt="Language grade: Java" src="https://img.shields.io/lgtm/grade/java/g/ImSejin/lezhin-comics-downloader.svg?logo=lgtm&logoWidth=18"/></a>
@@ -12,21 +14,17 @@
1214

1315
<p align="center">
1416
<img alt="GitHub All Releases" src="https://img.shields.io/github/downloads/imsejin/lezhin-comics-downloader/total">
15-
<img alt="GitHub Releases" src="https://img.shields.io/github/downloads/imsejin/lezhin-comics-downloader/latest/total">
17+
<img alt="GitHub Releases" src="https://img.shields.io/github/downloads/imsejin/lezhin-comics-downloader/latest/total">
1618
<img alt="jdk8" src="https://img.shields.io/badge/jdk-8-orange">
17-
<img alt="GitHub" src="https://img.shields.io/github/license/imsejin/lezhin-comics-downloader">
19+
<img alt="GitHub" src="https://img.shields.io/github/license/imsejin/lezhin-comics-downloader">
1820
</p>
1921

20-
This is downloader that helps you to login and downloads the specified comic for all lezhin-comics even adults.
21-
22-
*The user is responsible for everything that happens using this program.*
23-
24-
<br><br>
25-
2622
# Preview
2723

2824
<img alt="preview" src="./src/main/resources/assets/preview.gif">
2925

26+
<p align="center">This is downloader that helps you to login and downloads the specified comic for all lezhin-comics even adults.</p>
27+
<p align="center">※ <i>The user is responsible for everything that happens using this program.</i></p>
3028
<br><br>
3129

3230
# Getting started
@@ -56,7 +54,7 @@ This is downloader that helps you to login and downloads the specified comic for
5654
## Usage
5755

5856
```cmd
59-
java -jar {JAR filename} -l=<language> -n=<comic name> [-r=<episode range> -d]
57+
java -jar {JAR filename} -l=<language> -n=<comic name> [-r=<episode range> -j -d]
6058
```
6159

6260
- *<ins>language</ins> (required)*: language of lezhin platform you want to see.
@@ -76,6 +74,7 @@ java -jar {JAR filename} -l=<language> -n=<comic name> [-r=<episode range> -d]
7674
- __n~__ : from ep.N to the last episode
7775
- __~n__ : from the first episode to ep.N
7876
- __m~n__ : from ep.M to ep.N
77+
- <ins>jpg</ins> (optional): save images as JPEG format (default: WEBP format).
7978
- <ins>debug</ins> (optional): enables debugging mode.
8079

8180
<br><br>

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>io.github.imsejin</groupId>
88
<artifactId>lezhin-comics-downloader</artifactId>
9-
<version>2.7.1</version>
9+
<version>2.8.0</version>
1010
<packaging>jar</packaging>
1111

1212
<name>Lezhin Comics Downloader</name>
@@ -40,7 +40,7 @@
4040
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
4141
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
4242

43-
<common.utils.version>0.3.4</common.utils.version>
43+
<common.utils.version>0.4.7</common.utils.version>
4444
<lombok.version>1.18.20</lombok.version>
4545
</properties>
4646

src/main/java/io/github/imsejin/lzcodl/Application.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public static void main(String[] arguments) {
6262
.language(cmd.getOptionValue('l'))
6363
.comicName(cmd.getOptionValue('n'))
6464
.episodeRange(cmd.getOptionValue('r', null))
65+
.jpg(cmd.hasOption('j'))
6566
.debugging(cmd.hasOption('d'))
6667
.build();
6768

@@ -76,6 +77,7 @@ public static void main(String[] arguments) {
7677
Model pom = mavenReader.read(reader);
7778
Loggers.getLogger().info("{} v{}", pom.getName(), pom.getVersion());
7879
Loggers.getLogger().info("If you have any questions, contact me by '{}/issues'", pom.getUrl());
80+
Loggers.getLogger().debug("Argument: {}", args);
7981

8082
// Login with username and password and gets a token.
8183
args.setAccessToken(LoginHelper.login(args));
@@ -93,7 +95,7 @@ public static void main(String[] arguments) {
9395
args.setComicPath(comicDir);
9496

9597
// Downloads images.
96-
Downloader.download(args);
98+
new Downloader(args).download();
9799

98100
// Terminates the application.
99101
ChromeBrowser.getDriver().quit();

src/main/java/io/github/imsejin/lzcodl/common/CommandParser.java

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,40 +25,44 @@
2525
*/
2626
public final class CommandParser {
2727

28-
private static final Options options;
28+
private static final Option lang = Option.builder("l")
29+
.longOpt("lang")
30+
.desc("language of lezhin platform you want to see")
31+
.valueSeparator()
32+
.hasArg()
33+
.required()
34+
.build();
2935

30-
static {
31-
// Option: language
32-
Option lang = Option.builder("l")
33-
.longOpt("lang")
34-
.desc("language of lezhin platform you want to see")
35-
.valueSeparator()
36-
.hasArg()
37-
.required()
38-
.build();
39-
// Option: comicName
40-
Option name = Option.builder("n")
41-
.longOpt("name")
42-
.desc("webtoon name you want to download")
43-
.valueSeparator()
44-
.hasArg()
45-
.required()
46-
.build();
47-
// Option: episodeRange
48-
Option range = Option.builder("r")
49-
.longOpt("range")
50-
.desc("range of episodes you want to download")
51-
.hasArg()
52-
.valueSeparator()
53-
.build();
54-
// Option: debugging
55-
Option debug = Option.builder("d")
56-
.longOpt("debug")
57-
.desc("debug mode")
58-
.build();
36+
private static final Option name = Option.builder("n")
37+
.longOpt("name")
38+
.desc("webtoon name you want to download")
39+
.valueSeparator()
40+
.hasArg()
41+
.required()
42+
.build();
5943

60-
options = new Options().addOption(lang).addOption(name).addOption(range).addOption(debug);
61-
}
44+
private static final Option range = Option.builder("r")
45+
.longOpt("range")
46+
.desc("range of episodes you want to download")
47+
.hasArg()
48+
.valueSeparator()
49+
.build();
50+
51+
/**
52+
* @since 2.8.0
53+
*/
54+
private static final Option jpg = Option.builder("j")
55+
.longOpt("jpg")
56+
.desc("save as JPEG format")
57+
.build();
58+
59+
private static final Option debug = Option.builder("d")
60+
.longOpt("debug")
61+
.desc("debug mode")
62+
.build();
63+
64+
private static final Options options = new Options()
65+
.addOption(lang).addOption(name).addOption(range).addOption(jpg).addOption(debug);
6266

6367
private CommandParser() {
6468
}

src/main/java/io/github/imsejin/lzcodl/common/URLFactory.java

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,42 +31,42 @@ public final class URLFactory {
3131
/**
3232
* CDN 서버의 origin URL
3333
*
34-
* <pre>{@code
35-
* http://cdn.lezhin.com
36-
* }</pre>
34+
* @see <a href="http://cdn.lezhin.com">CDN URL</a>
3735
*/
3836
private static final String cdnUrl = "http://cdn.lezhin.com";
3937

4038
/**
4139
* 각 회차의 정보를 얻을 수 있는 URI의 접두사<br>
4240
* The prefix of URI to obtain information for each episode
4341
*
44-
* <pre>{@code
45-
* http://cdn.lezhin.com/episodes/
46-
* }</pre>
42+
* @see <a href="http://cdn.lezhin.com/episodes/">Episode Info URL</a>
4743
*/
4844
private static final String episodeInfoUrl = cdnUrl + "/episodes/";
4945

5046
/**
5147
* 이미지 URI의 접두사<br>
5248
* The prefix of image URI
5349
*
54-
* <pre>{@code
55-
* http://cdn.lezhin.com/v2/comics/
56-
* }</pre>
50+
* @see <a href="http://cdn.lezhin.com/v2/comics/">Image Prefix URL</a>
5751
*/
5852
private static final String imgUrl = cdnUrl + "/v2/comics/";
5953

60-
private URLFactory() {
54+
/**
55+
* @since 2.8.0
56+
*/
57+
private final Arguments args;
58+
59+
public URLFactory(Arguments args) {
60+
this.args = args;
6161
}
6262

6363
/**
64-
* <pre>{@code
65-
* https://cdn.lezhin.com/v2/comics/5651768999542784/episodes/6393378955722752/contents/scrolls/1.webp?access_token=5be30a25-a044-410c-88b0-19a1da968a64&purchased=false
66-
* }</pre>
64+
* @see <a href="https://cdn.lezhin.com/v2/comics/5651768999542784/episodes/6393378955722752/contents/scrolls/1.webp?access_token=5be30a25-a044-410c-88b0-19a1da968a64&purchased=false">
65+
* Image URL
66+
* </a>
6767
*/
6868
@SneakyThrows(MalformedURLException.class)
69-
public static synchronized URL image(long comicId, long episodeId, int filename, String accessToken, boolean purchased) {
69+
public static URL image(long comicId, long episodeId, int filename, String imageFormat, String accessToken, boolean purchased) {
7070
StringBuilder sb = new StringBuilder();
7171

7272
sb.append(imgUrl);
@@ -75,7 +75,9 @@ public static synchronized URL image(long comicId, long episodeId, int filename,
7575
sb.append(episodeId);
7676
sb.append("/contents/scrolls/");
7777
sb.append(filename);
78-
sb.append(".webp?access_token=");
78+
sb.append('.');
79+
sb.append(imageFormat);
80+
sb.append("?access_token=");
7981
sb.append(accessToken);
8082
sb.append("&purchased=").append(purchased); // If not append though you paid this episode, width of image decreases. (1080px => 720px)
8183
sb.append("&q=30"); // I don't know what this means, but if not append, width of image decreases. (1080px => 1024px)
@@ -84,16 +86,40 @@ public static synchronized URL image(long comicId, long episodeId, int filename,
8486
}
8587

8688
/**
87-
* @since 2.6.2
89+
* @see <a href="https://cdn.lezhin.com/v2/comics/5651768999542784/episodes/6393378955722752/contents/scrolls/1.webp?access_token=5be30a25-a044-410c-88b0-19a1da968a64&purchased=false">
90+
* Image URL
91+
* </a>
92+
* @since 2.8.0
93+
*/
94+
@SneakyThrows(MalformedURLException.class)
95+
public URL image(long episodeId, int filename, boolean purchased) {
96+
StringBuilder sb = new StringBuilder();
97+
98+
sb.append(imgUrl);
99+
sb.append(this.args.getProduct().getId());
100+
sb.append("/episodes/");
101+
sb.append(episodeId);
102+
sb.append("/contents/scrolls/");
103+
sb.append(filename);
104+
sb.append('.');
105+
sb.append(this.args.getImageFormat());
106+
sb.append("?access_token=");
107+
sb.append(this.args.getAccessToken());
108+
sb.append("&purchased=").append(purchased); // If not append though you paid this episode, width of image decreases. (1080px => 720px)
109+
sb.append("&q=30"); // I don't know what this means, but if not append, width of image decreases. (1080px => 1024px)
110+
111+
return new URL(sb.toString());
112+
}
113+
114+
/**
115+
* @since 2.8.0
88116
*/
89-
public static URL image(Arguments args, Episode episode, int filename, boolean purchased) {
90-
return image(args.getProduct().getId(), episode.getId(), filename, args.getAccessToken(), purchased);
117+
public URL image(Episode episode, int filename, boolean purchased) {
118+
return image(episode.getId(), filename, purchased);
91119
}
92120

93121
/**
94-
* <pre>{@code
95-
* http://cdn.lezhin.com/episodes/snail/1.json?access_token=5be30a25-a044-410c-88b0-19a1da968a64
96-
* }</pre>
122+
* @see <a href="http://cdn.lezhin.com/episodes/snail/1.json?access_token=5be30a25-a044-410c-88b0-19a1da968a64">A episode API</a>
97123
*/
98124
@SneakyThrows(MalformedURLException.class)
99125
public static URL oneEpisodeAPI(String comicName, String episodeName, String accessToken) {
@@ -117,9 +143,7 @@ public static URL oneEpisodeAPI(Arguments args, Episode episode) {
117143
}
118144

119145
/**
120-
* <pre>{@code
121-
* http://cdn.lezhin.com/episodes/snail?access_token=5be30a25-a044-410c-88b0-19a1da968a64
122-
* }</pre>
146+
* @see <a href="http://cdn.lezhin.com/episodes/snail?access_token=5be30a25-a044-410c-88b0-19a1da968a64">All episode API</a>
123147
*/
124148
@SneakyThrows(MalformedURLException.class)
125149
public static URL allEpisodeAPI(String comicName, String accessToken) {

src/main/java/io/github/imsejin/lzcodl/core/Downloader.java

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,32 @@
4545
*/
4646
public final class Downloader {
4747

48-
private static final String IMG_FORMAT_EXTENSION = "webp"; // or jpg
48+
/**
49+
* @since 2.8.0
50+
*/
51+
private final Arguments args;
52+
53+
/**
54+
* @since 2.8.0
55+
*/
56+
private final URLFactory urlFactory;
4957

50-
private Downloader() {
58+
public Downloader(Arguments args) {
59+
this.args = args;
60+
this.urlFactory = new URLFactory(args);
5161
}
5262

53-
public static void download(Arguments args) throws IOException {
54-
EpisodeRange episodeRange = EpisodeRange.of(args.getEpisodeRange());
63+
public void download() throws IOException {
64+
EpisodeRange episodeRange = EpisodeRange.of(this.args.getEpisodeRange());
5565

56-
List<Episode> episodes = args.getProduct().getEpisodes();
57-
for (int i : episodeRange.getArray(args)) {
66+
List<Episode> episodes = this.args.getProduct().getEpisodes();
67+
for (int i : episodeRange.getArray(this.args)) {
5868
Episode episode = episodes.get(i);
59-
downloadEpisode(args, episode, i + 1);
69+
downloadEpisode(this.args, episode, i + 1);
6070
}
6171
}
6272

63-
private static void downloadEpisode(Arguments arguments, Episode episode, int num) throws IOException {
73+
private void downloadEpisode(Arguments arguments, Episode episode, int num) throws IOException {
6474
// Cannot download paid episode.
6575
if (!episode.isFree()) return;
6676

@@ -80,16 +90,16 @@ private static void downloadEpisode(Arguments arguments, Episode episode, int nu
8090
try (ProgressBar progressBar = getDefaultProgressBar(arguments.getProduct().getAlias(), num, numOfImages)) {
8191
// Downloads all images of the episode.
8292
IntStream.rangeClosed(1, numOfImages).parallel().forEach(i -> {
83-
String filename = String.format("%03d.%s", i, IMG_FORMAT_EXTENSION);
93+
String filename = String.format("%03d.%s", i, this.args.getImageFormat());
8494
File file = new File(episodeDir.toFile(), filename);
8595

8696
// Tries to download high-resolution image for only paid users.
87-
URL url = URLFactory.image(arguments, episode, i, true);
97+
URL url = this.urlFactory.image(episode, i, true);
8898
boolean success = downloadImage(url, file);
8999

90100
// Try to download low-resolution image for all users.
91101
if (!success) {
92-
url = URLFactory.image(arguments, episode, i, false);
102+
url = this.urlFactory.image(episode, i, false);
93103
success = downloadImage(url, file);
94104

95105
// If failed to download, skips this image.

0 commit comments

Comments
 (0)