├── .classpath
├── .gitignore
├── .project
├── .settings
├── org.eclipse.core.resources.prefs
└── org.eclipse.jdt.core.prefs
├── ReadMe.md
└── src
├── Comic.png
├── ComicCore
├── Chapter.java
├── Comic.java
├── ComicInfo.java
└── comicfactory.java
├── DownLoad
├── DLControl.java
└── DLThread.java
├── Start
├── LOG.java
└── Start.java
├── UI
├── ButtonAnaly.java
├── ButtonDownLoad.java
├── ButtonFilePath.java
├── CheckBoxAll.java
├── FrameComic.java
├── PanelControl.java
├── SelectThread.java
├── SelectWebSite.java
├── TextComicID.java
├── TextFileText.java
└── UIConfig.java
├── config.js
└── manhuagui
├── DlManhuagui.java
└── manhuaguiCfg.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /bin/
2 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | GetComic
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.jdt.core.javanature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | encoding//src/ComicCore/Chapter.java=UTF-8
3 | encoding//src/DownLoad/DLControl.java=UTF-8
4 | encoding//src/DownLoad/DLThread.java=UTF-8
5 | encoding//src/Start/LOG.java=UTF-8
6 | encoding//src/Start/Start.java=UTF-8
7 | encoding//src/UI/ButtonAnaly.java=UTF-8
8 | encoding//src/UI/ButtonDownLoad.java=UTF-8
9 | encoding//src/UI/ButtonFilePath.java=UTF-8
10 | encoding//src/UI/CheckBoxAll.java=UTF-8
11 | encoding//src/UI/FrameComic.java=UTF-8
12 | encoding//src/UI/PanelControl.java=UTF-8
13 | encoding//src/UI/SelectThread.java=UTF-8
14 | encoding//src/UI/TextComicID.java=UTF-8
15 | encoding//src/UI/TextFileText.java=UTF-8
16 | encoding//src/UI/UIConfig.java=UTF-8
17 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
5 | org.eclipse.jdt.core.compiler.compliance=1.8
6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate
7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate
8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate
9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
11 | org.eclipse.jdt.core.compiler.source=1.8
12 |
--------------------------------------------------------------------------------
/ReadMe.md:
--------------------------------------------------------------------------------
1 | #增加网站的方法
2 | 在使用工厂模式重构代码后,软件已经基本具备可扩展性,如果需要增加一个新的下载漫画的网址,需要做如下的事情:
3 | 1)实现ComicCore/Comic接口中的三个方法。可以参考已有实现DlManhuagui;
4 | 2)在UI/UIConfig中依照注释样例增加一个网址名;
5 | 当然,你也可以使用LOG.log(String)来给自己的类添加打印信息以方便调试,这个打印信息会自动保存到当前程序下的Comiclog.txt文本中。
6 |
--------------------------------------------------------------------------------
/src/Comic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feiguiwoxin/GetComic/c38bdf9e28ca1c7f0b20586b7fe4dd55da29b8a5/src/Comic.png
--------------------------------------------------------------------------------
/src/ComicCore/Chapter.java:
--------------------------------------------------------------------------------
1 | package ComicCore;
2 |
3 | public class Chapter{
4 | private String Title = null;
5 | private String Html = null;
6 |
7 | public Chapter(String Title, String Html)
8 | {
9 | this.Title = Title;
10 | this.Html = Html;
11 | }
12 |
13 | public String getTitle() {
14 | return Title;
15 | }
16 |
17 | public String getHtml() {
18 | return Html;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/ComicCore/Comic.java:
--------------------------------------------------------------------------------
1 | package ComicCore;
2 |
3 | import java.util.ArrayList;
4 | /****************************
5 | * 对于漫画下载软件来说:
6 | * 1)获取到漫画的章节信息,可以用于显示在界面上让用户选择哪些章节
7 | * 2)获取每个章节中的漫画图片地址,按照地址进行下载即可
8 | * 3) 获取漫画书名,用于保存时用作保存文件夹名字
9 | * 因此抽象出这两个方法作为接口,可以认为任何类只要实现了这两个方法,就都可以进行漫画下载了
10 | ****************************/
11 | public interface Comic{
12 | public ArrayList GetChapetr(String ComicId);
13 | public ArrayList GetPicUrlByChapter(String ChapterUrl);
14 | public String GetBookName();
15 | }
--------------------------------------------------------------------------------
/src/ComicCore/ComicInfo.java:
--------------------------------------------------------------------------------
1 | package ComicCore;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.ByteArrayOutputStream;
5 | import java.io.File;
6 | import java.io.FileOutputStream;
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.io.InputStreamReader;
10 | import java.net.URL;
11 | import java.net.URLConnection;
12 | import java.net.URLEncoder;
13 | import java.security.SecureRandom;
14 | import java.security.cert.CertificateException;
15 | import java.security.cert.X509Certificate;
16 | import java.util.regex.Matcher;
17 | import java.util.regex.Pattern;
18 |
19 | import javax.net.ssl.HttpsURLConnection;
20 | import javax.net.ssl.SSLContext;
21 | import javax.net.ssl.TrustManager;
22 | import javax.net.ssl.X509TrustManager;
23 |
24 | import Start.LOG;
25 | /******************************
26 | * 在抽象类中增加2个公共方法:
27 | * 1)获取网页信息的方法,这在获取章节和图片地址时必然会用到
28 | * 2)保存图片的方法,这在下载图片的时候也必然会用到
29 | ******************************/
30 | public abstract class ComicInfo implements Comic{
31 | protected String BookName = null;
32 |
33 | public String GetBookName()
34 | {
35 | return BookName;
36 | }
37 |
38 | public String GetHtmlInfo(String UrlAdd)
39 | {
40 | String HttpInfo = null;
41 | if(null == UrlAdd) return null;
42 | String line;
43 | URL url;
44 | int trytime = 5;
45 |
46 | while(trytime > 0)
47 | {
48 | try {
49 | url = new URL(UrlAdd);
50 | URLConnection urlcon = url.openConnection();
51 | if(UrlAdd.startsWith("https"))
52 | {
53 | trustall((HttpsURLConnection) urlcon);
54 | }
55 |
56 | urlcon.setRequestProperty("accept", "*/*");
57 | urlcon.setRequestProperty("connection", "Keep-Alive");
58 | urlcon.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0");
59 | urlcon.setConnectTimeout(10 * 1000);
60 | urlcon.setReadTimeout(10 * 1000);
61 | urlcon.connect();
62 | BufferedReader urlRead = new BufferedReader(new InputStreamReader(urlcon.getInputStream(), "utf-8"));
63 | while((line = urlRead.readLine()) != null)
64 | {
65 | HttpInfo += line;
66 | }
67 | urlRead.close();
68 | return HttpInfo;
69 | } catch (Exception e) {
70 | trytime --;
71 | LOG.log(String.format("%s倒数第%d次重连,信息%s", UrlAdd, trytime, e.getMessage()));
72 | continue;
73 | }
74 | }
75 |
76 | if(HttpInfo == null)
77 | {
78 | LOG.log("获取网址信息错误:" + UrlAdd);
79 | }
80 |
81 | return HttpInfo;
82 | }
83 |
84 | private static void trustall(HttpsURLConnection con)
85 | {
86 | TrustManager[] trustallcert = {new X509TrustManager()
87 | {
88 | @Override
89 | public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
90 | }
91 |
92 | @Override
93 | public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
94 | }
95 |
96 | @Override
97 | public X509Certificate[] getAcceptedIssuers() {
98 | return null;
99 | }}};
100 | SSLContext ssl;
101 | try {
102 | ssl = SSLContext.getInstance("SSL");
103 | ssl.init(null, trustallcert, new SecureRandom());
104 | con.setSSLSocketFactory(ssl.getSocketFactory());
105 | } catch (Exception e) {
106 | e.printStackTrace();
107 | }
108 |
109 | }
110 |
111 | public static int SavePicture(String UrlAdd, String Path, String FileName, String refer)
112 | {
113 | if(null == Path || null == FileName) return 0;
114 | //提前判断文件是否存在,如果存在直接跳过下载,加快续传速度
115 | File file = new File(Path + FileName);
116 | if(file.exists())
117 | {
118 | return 1;
119 | }
120 |
121 | byte[] imgData = getImgData(UrlAdd, refer);
122 | if(null == imgData) return 0;
123 |
124 | FileOutputStream fop = null;
125 |
126 | try {
127 | try {
128 | fop = new FileOutputStream(file);
129 | fop.write(imgData);
130 | fop.flush();
131 | } catch (Exception e) {
132 | e.printStackTrace();
133 | LOG.log(e.getMessage());
134 | return 0;
135 | }
136 | finally
137 | {
138 | fop.close();
139 | }
140 | }
141 | catch(Exception e)
142 | {
143 | LOG.log(e.getMessage());
144 | e.printStackTrace();
145 | }
146 |
147 | return 1;
148 | }
149 | //获取图片的二进制流
150 | private static byte[] getImgData(String UrlAdd, String refer)
151 | {
152 | byte[] ImgData = null;
153 | //如果连接失败,有5次重连机会
154 | int ConnectTime = 5;
155 | if(null == UrlAdd) return null;
156 |
157 | while(ConnectTime > 0)
158 | {
159 | try {
160 | UrlAdd = ConvertURLAddToChar(UrlAdd, "utf-8");
161 | URL url = new URL(UrlAdd);
162 | URLConnection urlcon = url.openConnection();
163 | if(UrlAdd.startsWith("https"))
164 | {
165 | trustall((HttpsURLConnection)urlcon);
166 | }
167 | urlcon.setRequestProperty("Referer", refer);
168 | urlcon.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0");
169 | urlcon.setConnectTimeout(15 * 1000);
170 | urlcon.setReadTimeout(15 * 1000);
171 |
172 | ImgData = GetbyteFromStream(urlcon.getInputStream());
173 | return ImgData;
174 | } catch (Exception e) {
175 | e.printStackTrace();
176 | LOG.log(String.format("%s倒数第%d次重连,信息%s", UrlAdd, ConnectTime, e.getMessage()));
177 | ConnectTime --;
178 | continue;
179 | }
180 | }
181 |
182 | return null;
183 | }
184 | //将图片的Http流转化为二进制流
185 | private static byte[] GetbyteFromStream(InputStream in) throws IOException
186 | {
187 | int len;
188 |
189 | byte[] buffer = new byte[3 * 100 * 1024];
190 | ByteArrayOutputStream out = new ByteArrayOutputStream();
191 | while((len = in.read(buffer)) != -1)
192 | {
193 | out.write(buffer, 0 ,len);
194 | }
195 |
196 | byte[] imgData = out.toByteArray();
197 | in.close();
198 | out.close();
199 |
200 | return imgData;
201 | }
202 | //将链接中可能的中文进行编码
203 | private static String ConvertURLAddToChar(String UrlAdd,String Charset)
204 | {
205 | try
206 | {
207 | Matcher matcher = Pattern.compile("[\\u4e00-\\u9fa5]").matcher(UrlAdd);
208 | while (matcher.find())
209 | {
210 | String CHNWord=matcher.group();
211 | UrlAdd=UrlAdd.replaceAll(CHNWord, URLEncoder.encode(CHNWord,Charset));
212 | }
213 | }
214 | catch (Exception e)
215 | {
216 | e.printStackTrace();
217 | LOG.log(String.format("encode string[%s] fail with[%s]", UrlAdd, Charset));
218 | }
219 |
220 | return UrlAdd;
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/src/ComicCore/comicfactory.java:
--------------------------------------------------------------------------------
1 | package ComicCore;
2 |
3 | import Start.LOG;
4 |
5 | public class comicfactory {
6 | @SuppressWarnings("unchecked")
7 | public Comic create(String package_addr)
8 | {
9 | Comic getcomic = null;
10 | Class extends Comic> cls = null;
11 | try {
12 | cls = (Class extends Comic>) Class.forName(package_addr);
13 | getcomic = (Comic)cls.newInstance();
14 | } catch (ClassNotFoundException e) {
15 | LOG.log("comicfactory:找不到指定的类:" + package_addr);
16 | } catch (InstantiationException e) {
17 | LOG.log("comicfactory:没有默认构造器:" + package_addr);
18 | } catch (IllegalAccessException e) {
19 | LOG.log("comicfactory:没有默认构造器:" + package_addr);
20 | }
21 | return getcomic;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/DownLoad/DLControl.java:
--------------------------------------------------------------------------------
1 | package DownLoad;
2 |
3 | import java.io.File;
4 | import java.util.ArrayList;
5 | import java.util.concurrent.ExecutorService;
6 | import java.util.concurrent.Executors;
7 |
8 | import javax.swing.JOptionPane;
9 |
10 | import ComicCore.Chapter;
11 | import ComicCore.Comic;
12 | import ComicCore.comicfactory;
13 | import UI.FrameComic;
14 | //这个类为下载控制中心,主要用于与面板的通信和下载线程的控制
15 | public class DLControl {
16 | public static volatile boolean RunThread = true;
17 |
18 | private String ComicId = null;
19 | private String package_addr = null;
20 | private int PoolSize = 0;
21 | private String FilePath = null;
22 | private FrameComic fc = null;
23 | private ExecutorService fixpool = null;
24 | private String BookName = null;
25 | private Comic getComic = null;
26 |
27 | //获取网页的章节
28 | public boolean AnalyChapter()
29 | {
30 | if(null == ComicId) return false;
31 | getComic = new comicfactory().create(package_addr);
32 | if(null == getComic) return false;
33 | ArrayList Chapters = getComic.GetChapetr(ComicId);
34 | if(null == Chapters) return false;
35 | BookName = getComic.GetBookName();
36 | if (BookName == null) BookName = "default";
37 |
38 | if(Chapters.isEmpty())
39 | {
40 | return false;
41 | }
42 | fc.addChapters(Chapters);
43 | return true;
44 | }
45 |
46 | public void InterrputDL()
47 | {
48 | if(null != fixpool)
49 | {
50 | fixpool.shutdownNow();
51 | DLControl.RunThread = false;
52 | }
53 | fc.InterrputDL();
54 | }
55 |
56 | public DLControl(FrameComic fc)
57 | {
58 | this.fc = fc;
59 | }
60 |
61 | public boolean StartDL(int poolsize)
62 | {
63 | ArrayList Chapters = fc.getDLInfo();
64 | if(fixpool == null)
65 | {
66 | PoolSize = poolsize;
67 | fixpool = Executors.newFixedThreadPool(PoolSize);
68 | }
69 | else if(fixpool != null)
70 | {
71 | PoolSize = poolsize;
72 | fixpool.shutdownNow();
73 | fixpool = Executors.newFixedThreadPool(PoolSize);
74 | }
75 |
76 | if(Chapters.isEmpty())
77 | {
78 | JOptionPane.showMessageDialog(null, "没有任何选择章节", "错误说明", JOptionPane.CLOSED_OPTION);
79 | return false;
80 | }
81 |
82 | if(ThreadControl(Chapters))
83 | {
84 | fc.disableAllbox();
85 | return true;
86 | }
87 |
88 | return false;
89 | }
90 |
91 | public boolean ThreadControl(ArrayList Chapters)
92 | {
93 | File HeadDir = new File(FilePath + "/" + BookName + "(" + ComicId + ")");
94 |
95 | if(!(HeadDir.exists() && HeadDir.isDirectory()))
96 | {
97 | if(!HeadDir.mkdirs())
98 | {
99 | JOptionPane.showMessageDialog(null, "创建初始文件夹失败,请检查磁盘是否已满/地址错误:"+ HeadDir.getAbsolutePath(), "错误说明", JOptionPane.CLOSED_OPTION);
100 | return false;
101 | }
102 | }
103 |
104 | for(Chapter c : Chapters)
105 | {
106 | DLThread dl = new DLThread(c, HeadDir.getAbsolutePath(), getComic);
107 | dl.setFC(fc);
108 | fixpool.execute(dl);
109 | }
110 |
111 | return true;
112 | }
113 |
114 | public void setComicId(String ComicId) {
115 | this.ComicId = ComicId;
116 | }
117 |
118 | public void setFilePath(String filePath) {
119 | FilePath = filePath;
120 | }
121 |
122 | public void setpackage_addr(String package_addr) {
123 | this.package_addr = package_addr;
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/DownLoad/DLThread.java:
--------------------------------------------------------------------------------
1 | package DownLoad;
2 |
3 | import java.io.File;
4 | import java.util.ArrayList;
5 |
6 | import ComicCore.Chapter;
7 | import ComicCore.Comic;
8 | import ComicCore.ComicInfo;
9 | import Start.LOG;
10 | import UI.FrameComic;
11 |
12 | public class DLThread implements Runnable{
13 | private String html = null;
14 | private String title = null;
15 | private String path = null;
16 | private FrameComic fc = null;
17 | private int len = 0;
18 | private Chapter chapter = null;
19 | private Comic getComic = null;
20 |
21 | public DLThread(Chapter chapter,String path, Comic getComic)
22 | {
23 | this.chapter = chapter;
24 | this.html = chapter.getHtml();
25 | this.title = chapter.getTitle();
26 | this.path = path;
27 | this.getComic = getComic;
28 | processTitle();
29 | }
30 |
31 | public void setFC(FrameComic fc)
32 | {
33 | this.fc = fc;
34 | }
35 |
36 | @Override
37 | public void run() {
38 |
39 | if(!DLControl.RunThread)
40 | {
41 | return;
42 | }
43 |
44 | File dir = new File(path + "/" + title);
45 | if(!(dir.exists() && dir.isDirectory()))
46 | {
47 | if(!dir.mkdir())
48 | {
49 | LOG.log("创建章节文件夹失败,请检查磁盘是否已满/地址错误:" + dir.getAbsolutePath());
50 | return;
51 | }
52 | }
53 |
54 | ArrayList PicPath = getComic.GetPicUrlByChapter(html);
55 | if(PicPath == null)
56 | {
57 | fc.FinishDl(chapter, 0);
58 | return;
59 | }
60 | else if(PicPath.isEmpty())
61 | {
62 | LOG.log("解析图片地址失败:" + html);
63 | fc.FinishDl(chapter, 0);
64 | return;
65 | }
66 |
67 | len = PicPath.size();
68 | int numlen = getNumLen(len);
69 | int index = 1;
70 | int result = 0;
71 |
72 | for(String path : PicPath)
73 | {
74 | fc.UpdateDLinfo(chapter, index, len);
75 | result = ComicInfo.SavePicture(path, dir.getAbsolutePath() + "/", String.format("%0" + numlen + "d", index) + ".jpg", html);
76 |
77 | if(!DLControl.RunThread)
78 | {
79 | return;
80 | }
81 |
82 | if(0 == result)
83 | {
84 | LOG.log("下载图片地址失败:" + path);
85 | fc.FinishDl(chapter, 0);
86 | return;
87 | }
88 |
89 | index ++;
90 | }
91 |
92 | fc.FinishDl(chapter, 1);
93 | }
94 | //一些带奇怪符号的名称可能会导致创建文件夹失败,所以需要进行预处理
95 | private void processTitle()
96 | {
97 | title = title.replaceAll("[\\^%&',.;=?$]+", "");
98 | }
99 |
100 | private int getNumLen(int num)
101 | {
102 | int len = 0;
103 |
104 | do
105 | {
106 | num = num /10;
107 | len ++;
108 | }
109 | while(num != 0);
110 |
111 | return len;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/Start/LOG.java:
--------------------------------------------------------------------------------
1 | package Start;
2 |
3 | import java.io.File;
4 | import java.io.FileWriter;
5 | import java.io.IOException;
6 | import java.util.Date;
7 |
8 | import javax.swing.JOptionPane;
9 |
10 | public class LOG {
11 |
12 | private static StringBuffer logbuff = new StringBuffer();
13 | private static int lognum = 0;
14 |
15 | static
16 | {
17 | loginDate();
18 | }
19 |
20 | public static synchronized void log(String str)
21 | {
22 | lognum ++;
23 | logbuff = logbuff.append(String.format("%tT:%s\r\n", new Date(), str));
24 |
25 | if(lognum > 20 || str.length() > 1024)
26 | {
27 | logintofile();
28 | }
29 | }
30 |
31 | private static void loginDate()
32 | {
33 | logbuff = logbuff.append(String.format("%1$tF %1$tT:START!!!\r\n", new Date()));
34 | logintofile();
35 | }
36 |
37 | public static synchronized void logintofile()
38 | {
39 | if(logbuff.length() <= 0) return;
40 | File file = new File("./Comiclog.txt");
41 | try {
42 | FileWriter fw = new FileWriter(file, true);
43 | fw.write(logbuff.toString());
44 | logbuff.setLength(0);;
45 | lognum = 0;
46 | fw.close();
47 | } catch (IOException e) {
48 | JOptionPane.showMessageDialog(null, "写入日志错误,请关闭日志文件", "错误", JOptionPane.CLOSED_OPTION);
49 | return;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Start/Start.java:
--------------------------------------------------------------------------------
1 | package Start;
2 |
3 | import java.util.Timer;
4 | import java.util.TimerTask;
5 |
6 | import DownLoad.DLControl;
7 | import UI.FrameComic;
8 | import UI.PanelControl;
9 |
10 | public class Start {
11 |
12 | public static void main(String[] args) throws InterruptedException
13 | {
14 | PanelControl pc = new PanelControl();
15 | FrameComic fc = new FrameComic(pc);
16 | DLControl dlc = new DLControl(fc);
17 | pc.setDlc(dlc);
18 | fc.setVisible(true);
19 | Timer logtime = new Timer();
20 | logtime.schedule(new TimerTask()
21 | {
22 | @Override
23 | public void run() {
24 | LOG.logintofile();;
25 | }
26 | },
27 | 1, 2 * 1000);
28 | }
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/src/UI/ButtonAnaly.java:
--------------------------------------------------------------------------------
1 | package UI;
2 |
3 | import java.awt.event.MouseAdapter;
4 | import java.awt.event.MouseEvent;
5 |
6 | import javax.swing.JButton;
7 |
8 | @SuppressWarnings("serial")
9 | public class ButtonAnaly extends JButton{
10 | private PanelControl pc= null;
11 |
12 | public void setControl(PanelControl pc)
13 | {
14 | this.pc = pc;
15 | }
16 |
17 | public ButtonAnaly()
18 | {
19 | this.setText("获取章节");
20 | this.addMouseListener(new MouseAdapter()
21 | {
22 | @Override
23 | public void mousePressed(MouseEvent e)
24 | {
25 | if(ButtonAnaly.this.isEnabled())
26 | {
27 | ButtonAnaly.this.setText("获取中....");
28 | ButtonAnaly.this.setEnabled(false);
29 | ButtonAnaly.this.paintImmediately(0, 0, ButtonAnaly.this.getWidth(), ButtonAnaly.this.getHeight());
30 | pc.StartAnaly(ButtonAnaly.this);
31 | }
32 | }
33 | });
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/UI/ButtonDownLoad.java:
--------------------------------------------------------------------------------
1 | package UI;
2 |
3 | import java.awt.event.MouseAdapter;
4 | import java.awt.event.MouseEvent;
5 |
6 | import javax.swing.JButton;
7 | import javax.swing.JOptionPane;
8 |
9 | import DownLoad.DLControl;
10 |
11 | @SuppressWarnings("serial")
12 | public class ButtonDownLoad extends JButton{
13 | private boolean DLState = false;
14 | private PanelControl pc= null;
15 |
16 | public void setControl(PanelControl pc)
17 | {
18 | this.pc = pc;
19 | }
20 |
21 | public ButtonDownLoad()
22 | {
23 | this.setText("下载");
24 | this.setEnabled(false);
25 | this.addMouseListener(new MouseAdapter()
26 | {
27 | @Override
28 | public void mousePressed(MouseEvent e)
29 | {
30 | if(ButtonDownLoad.this.isEnabled() && ButtonDownLoad.this.getText().equals("下载"))
31 | {
32 | ButtonDownLoad.this.setText("中断");
33 | DLState = true;
34 | DLControl.RunThread = true;
35 | boolean result = pc.StartDL(ButtonDownLoad.this);
36 | if(false == result)
37 | {
38 | ButtonDownLoad.this.setEnabled(true);
39 | ButtonDownLoad.this.setText("下载");
40 | DLState = false;
41 | }
42 | }
43 | else if(!ButtonDownLoad.this.isEnabled() && ButtonDownLoad.this.getText().equals("下载"))
44 | {
45 | JOptionPane.showMessageDialog(null, "请先分析以获取章节信息", "错误说明", JOptionPane.CLOSED_OPTION);
46 | }
47 | else if(!ButtonDownLoad.this.isEnabled() && ButtonDownLoad.this.getText().equals("下载中"))
48 | {
49 | JOptionPane.showMessageDialog(null, "就算你再怎么点,也没办法加快下载速度呀-_-!", "错误说明", JOptionPane.CLOSED_OPTION);
50 | }
51 | else if(ButtonDownLoad.this.getText().equals("中断"))
52 | {
53 | pc.InterrputDL();
54 | }
55 | }
56 | });
57 | }
58 |
59 | public boolean getDLState() {
60 | return DLState;
61 | }
62 |
63 | public void resetDLStat()
64 | {
65 | this.DLState = false;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/UI/ButtonFilePath.java:
--------------------------------------------------------------------------------
1 | package UI;
2 |
3 | import java.awt.event.MouseAdapter;
4 | import java.awt.event.MouseEvent;
5 | import java.io.File;
6 |
7 | import javax.swing.JButton;
8 | import javax.swing.JFileChooser;
9 |
10 | @SuppressWarnings("serial")
11 | public class ButtonFilePath extends JButton{
12 | private PanelControl pc= null;
13 |
14 | public void setControl(PanelControl pc)
15 | {
16 | this.pc = pc;
17 | }
18 |
19 | public ButtonFilePath()
20 | {
21 | this.setText("选择");
22 | this.setBounds(392, 0, 93, 30);
23 | this.addMouseListener(new MouseAdapter()
24 | {
25 | @Override
26 | public void mousePressed(MouseEvent e)
27 | {
28 | if (!ButtonFilePath.this.isEnabled()) return;
29 | JFileChooser chooser = new JFileChooser();
30 | chooser.setCurrentDirectory(new File(pc.getFilePath()));
31 | chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
32 | chooser.showOpenDialog(null);
33 | if(chooser.getSelectedFile() != null)
34 | {
35 | String SelectedFile = chooser.getSelectedFile().getAbsolutePath();
36 | pc.setFilePath(SelectedFile);
37 | }
38 | }
39 | });
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/UI/CheckBoxAll.java:
--------------------------------------------------------------------------------
1 | package UI;
2 |
3 | import java.awt.event.ItemEvent;
4 | import java.awt.event.ItemListener;
5 |
6 | import javax.swing.JCheckBox;
7 |
8 | @SuppressWarnings("serial")
9 | public class CheckBoxAll extends JCheckBox{
10 | public CheckBoxAll(String text, FrameComic fc)
11 | {
12 | this.setText(text);
13 | this.setSelected(true);
14 |
15 | this.addItemListener(new ItemListener()
16 | {
17 | @Override
18 | public void itemStateChanged(ItemEvent e) {
19 | if(CheckBoxAll.this.getText().equals("全选"))
20 | {
21 | if(ItemEvent.SELECTED == e.getStateChange())
22 | {
23 | fc.SelectAll();
24 | }
25 | else
26 | {
27 | fc.DeSelectAll();
28 | }
29 | }
30 | }
31 |
32 | });
33 | }
34 |
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/UI/FrameComic.java:
--------------------------------------------------------------------------------
1 | package UI;
2 |
3 | import Start.LOG;
4 |
5 | import java.awt.Color;
6 | import java.awt.Container;
7 | import java.awt.Font;
8 | import java.awt.Toolkit;
9 | import java.awt.event.WindowAdapter;
10 | import java.awt.event.WindowEvent;
11 | import java.util.ArrayList;
12 | import java.util.LinkedHashMap;
13 | import java.util.Set;
14 |
15 | import javax.swing.BoxLayout;
16 | import javax.swing.JCheckBox;
17 | import javax.swing.JFrame;
18 | import javax.swing.JOptionPane;
19 | import javax.swing.JPanel;
20 | import javax.swing.JScrollPane;
21 | import javax.swing.JTextArea;
22 |
23 | import ComicCore.Chapter;
24 |
25 | @SuppressWarnings("serial")
26 | public class FrameComic extends JFrame{
27 | private JPanel JpScroll = null;
28 | private LinkedHashMap Boxs = new LinkedHashMap();
29 | CheckBoxAll CheckAll = null;
30 | private int TaskNum = 0;
31 | PanelControl pc = null;
32 | JScrollPane scrollpane = null;
33 |
34 | public FrameComic(PanelControl pc)
35 | {
36 | this.pc = pc;
37 | initLayout();
38 | }
39 |
40 | private void initLayout()
41 | {
42 | this.setTitle("一键获取漫画工具");
43 | this.setResizable(false);
44 | //采取如此奇怪的读取方式是为了在JAR中能读取到img
45 | this.setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getClassLoader().getResource("Comic.png")));
46 | this.setSize(UIConfig.ComicW, UIConfig.ComicH);
47 | this.setLocation((UIConfig.WinW - UIConfig.ComicW) / 2, (UIConfig.WinH - UIConfig.ComicH) / 2);
48 | this.addWindowListener(new WindowAdapter()
49 | {
50 | @Override
51 | public void windowClosing(WindowEvent e)
52 | {
53 | LOG.logintofile();;
54 | if(CheckAll != null && CheckAll.isEnabled() == false)
55 | {
56 | int ExitRst = JOptionPane.showConfirmDialog(null, "正在下载中,确认退出?", "确认", JOptionPane.YES_NO_OPTION);
57 | if(JOptionPane.NO_OPTION == ExitRst)
58 | {
59 | return;
60 | }
61 | }
62 | System.exit(0);
63 | }
64 | });
65 | this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
66 | Container con = this.getContentPane();
67 | con.setBackground(new Color(144, 238, 144));
68 | con.setLayout(null);
69 | con.add(pc);
70 |
71 | setScrollUI(con);
72 | setFileUI(con);
73 | setHelpUI(con);
74 | }
75 |
76 | private void setHelpUI(Container con)
77 | {
78 | JTextArea ta = new JTextArea();
79 | ta.setEditable(false);
80 | ta.setLineWrap(true);
81 | ta.setFont(new Font("宋体", Font.PLAIN, 15));
82 | ta.setDisabledTextColor(Color.BLACK);
83 | ta.setText("本软件只能抓取到http://www.manhuagui.com/该网站的漫画数据(此文本框内可以用CTRL+C进行复制)。\n" +
84 | "1.漫画ID是什么?\n" +
85 | "进入http://www.manhuagui.com/网站后,搜索到你想要的漫画,点击进入漫画页面。比如http://www.manhuagui.com/comic/1626/这个漫画,则漫画ID就是1626。\n"
86 | + "另外即使是网页显示因版权而被屏蔽的漫画也可以试一下哦,但切记不要一天之内下载过多此类被禁漫画,很容易被网站后台发现而封IP。\n" +
87 | "2.下载失败后,如何判断是不是被封IP了?如何解决?\n" +
88 | "可以尝试在浏览器上打开http://www.manhuagui.com/,如果发现无法进入,则可以使用你的手机(注意请不要使用和电脑一样的wifi网络,使用你的无线网络或其他网络)尝试登录该网站,如果手机可以进入而电脑不行,则说明确实被封IP了。\n" +
89 | "封IP不要紧张,如果你是拨号上网的用户,那么重新拨号就可以获取到新IP。如果你是光纤用户,则需要断电重启一下光钎盒子。\n" +
90 | "3.其他说明\n" +
91 | "1)如果出现下载不动的情况(进度一直没动),可以中断一下然后继续点下载,这并不会重新下载已下载文件,而是会重新建立连接下载(就像用迅雷等软件下载没速度重连一下可能会好一样的道理),可能会加快下载速度\n" +
92 | "2)如果中断后发现选择章节界面是灰色的,这时候可以直接关闭程序,然后重新打开重新分析下载,只要你不改动保存位置也不删除已下载文件,那么对于已下载内容同样会直接跳过,不会重新下载。");
93 |
94 | JScrollPane sp = new JScrollPane(ta);
95 | sp.setBounds(5, 450, 485, 115);
96 | con.add(sp);
97 | }
98 |
99 | private void setFileUI(Container con)
100 | {
101 | JPanel filepanel = new JPanel();
102 | filepanel.setBounds(5, 45, 485, 30);
103 | filepanel.setBackground(new Color(144, 238, 144));
104 | filepanel.setLayout(null);
105 |
106 | filepanel.add(pc.getTextFileText());
107 | filepanel.add(pc.getButtonFilePath());
108 |
109 | con.add(filepanel);
110 |
111 | }
112 |
113 | private void setScrollUI(Container con)
114 | {
115 | JpScroll = new JPanel();
116 | JpScroll.setLayout(new BoxLayout(JpScroll, BoxLayout.Y_AXIS));
117 | scrollpane = new JScrollPane(JpScroll);
118 | scrollpane.getVerticalScrollBar().setUnitIncrement(15);
119 | scrollpane.setBounds(5, 80, 485, 365);
120 | con.add(scrollpane);
121 | }
122 |
123 | public void addChapters(ArrayList Chapters)
124 | {
125 | JpScroll.removeAll();
126 | scrollpane.getVerticalScrollBar().setValue(0);
127 | Boxs.clear();
128 | CheckAll = new CheckBoxAll("全选", this);
129 | JpScroll.add(CheckAll);
130 | for(Chapter chapter : Chapters)
131 | {
132 | JCheckBox tmp = new JCheckBox(chapter.getTitle());
133 | tmp.setSelected(true);
134 | JpScroll.add(tmp);
135 | Boxs.put(chapter, tmp);
136 | }
137 | JpScroll.updateUI();
138 | }
139 |
140 | public ArrayList getDLInfo()
141 | {
142 | this.TaskNum = 0;
143 | ArrayList info = new ArrayList();
144 | Set keys = Boxs.keySet();
145 | for(Chapter key : keys)
146 | {
147 | JCheckBox cb = Boxs.get(key);
148 | if(cb.isSelected() && cb.isEnabled())
149 | {
150 | info.add(key);
151 | TaskNum ++;
152 | }
153 | }
154 |
155 | return info;
156 | }
157 |
158 | public void SelectAll()
159 | {
160 | Set keys = Boxs.keySet();
161 | for(Chapter key : keys)
162 | {
163 | Boxs.get(key).setSelected(true);
164 | }
165 |
166 | JpScroll.updateUI();
167 | return;
168 | }
169 |
170 | public void DeSelectAll()
171 | {
172 | Set keys = Boxs.keySet();
173 | for(Chapter key : keys)
174 | {
175 | Boxs.get(key).setSelected(false);
176 | }
177 |
178 | JpScroll.updateUI();
179 | return;
180 | }
181 |
182 | public void disableAllbox()
183 | {
184 | Set keys = Boxs.keySet();
185 | CheckAll.setEnabled(false);
186 | for(Chapter key : keys)
187 | {
188 | Boxs.get(key).setEnabled(false);;
189 | }
190 |
191 | JpScroll.updateUI();
192 | return;
193 | }
194 |
195 | public void UpdateDLinfo(Chapter chapter,int curr,int all)
196 | {
197 | JCheckBox box = Boxs.get(chapter);
198 | String currtext = box.getText();
199 | int index = currtext.indexOf("任务进展:");
200 | if(-1 != index)
201 | {
202 | currtext = currtext.substring(0, index - 1);
203 | }
204 |
205 | box.setText(currtext + " 任务进展:" + curr + "/" + all);
206 | box.updateUI();
207 | }
208 |
209 | public void FinishDl(Chapter chapter, int type)
210 | {
211 | TaskNum --;
212 | JCheckBox box = Boxs.get(chapter);
213 | String currtext = box.getText();
214 | int index = currtext.indexOf("任务进展:");
215 | if(-1 != index)
216 | {
217 | currtext = currtext.substring(0, index - 1);
218 | }
219 |
220 | if(type == 1)
221 | {
222 | box.setText(currtext + " 下载完成喽~");
223 | box.setForeground(Color.RED);
224 | }
225 | else if(type == 0)
226 | {
227 | box.setText(currtext + " 任务进展:任务失败,可以尝试再次下载,不会影响已下图片" );
228 | }
229 |
230 | box.updateUI();
231 | if(TaskNum == 0)
232 | {
233 | pc.ResetCompnent();
234 | ActiveAllbox();
235 | Toolkit.getDefaultToolkit().beep();
236 | JOptionPane.showMessageDialog(null, "下载完成^_^", "说明", JOptionPane.CLOSED_OPTION);
237 | }
238 | }
239 |
240 | public void ActiveAllbox()
241 | {
242 | Set keys = Boxs.keySet();
243 | CheckAll.setEnabled(true);
244 | for(Chapter key : keys)
245 | {
246 | if(Boxs.get(key).getForeground() == Color.RED)
247 | {
248 | continue;
249 | }
250 |
251 | Boxs.get(key).setEnabled(true);
252 | }
253 |
254 | JpScroll.updateUI();
255 | return;
256 | }
257 |
258 | public void InterrputDL()
259 | {
260 | ActiveAllbox();
261 | this.TaskNum = 0;
262 | }
263 | }
264 |
265 |
--------------------------------------------------------------------------------
/src/UI/PanelControl.java:
--------------------------------------------------------------------------------
1 | package UI;
2 |
3 | import java.awt.Color;
4 | import java.awt.GridLayout;
5 |
6 | import javax.swing.JOptionPane;
7 | import javax.swing.JPanel;
8 | import DownLoad.DLControl;
9 |
10 | @SuppressWarnings("serial")
11 | public class PanelControl extends JPanel{
12 | private TextComicID ComicId = new TextComicID();
13 | private ButtonDownLoad buttonDl = new ButtonDownLoad();
14 | private ButtonAnaly buttonanaly = new ButtonAnaly();
15 | private SelectThread select = new SelectThread();
16 | private DLControl dlc = null;
17 | private ButtonFilePath filepath = new ButtonFilePath();
18 | private TextFileText filetext = new TextFileText();
19 | private SelectWebSite website = new SelectWebSite();
20 |
21 | public PanelControl()
22 | {
23 | initComponent();
24 | initLayout();
25 | }
26 |
27 | public void setDlc(DLControl dlc)
28 | {
29 | this.dlc = dlc;
30 | }
31 |
32 | private void initComponent()
33 | {
34 | buttonDl.setControl(this);
35 | buttonanaly.setControl(this);
36 | filepath.setControl(this);
37 | }
38 |
39 | private void initLayout()
40 | {
41 | this.add(ComicId);
42 | this.add(website);
43 | this.add(select);
44 | this.add(buttonanaly);
45 | this.add(buttonDl);
46 | this.setBounds(5, 10, 485, 30);
47 | this.setBackground(new Color(144, 238, 144));
48 | this.setLayout(new GridLayout(1, 4, 5, 0));
49 | }
50 |
51 | public void StartAnaly(ButtonAnaly buttonanaly)
52 | {
53 | //安全性考虑,这样不是由这个PanelControl本身创建的ButtonAnaly对象无法调用此方法。
54 | if(buttonanaly != this.buttonanaly) return;
55 | if(ComicId.getText().equals(""))
56 | {
57 | JOptionPane.showMessageDialog(null, "请输入漫画ID", "错误说明", JOptionPane.CLOSED_OPTION);
58 | return;
59 | }
60 |
61 | if(buttonDl.getDLState())
62 | {
63 | JOptionPane.showMessageDialog(null, "正在下载中,暂时无法操作", "错误说明", JOptionPane.CLOSED_OPTION);
64 | return;
65 | }
66 |
67 | dlc.setComicId(ComicId.getText());
68 | String package_addr = UIConfig.ComicWebList.get(website.getSelectedIndex()).getpackage_addr();
69 | dlc.setpackage_addr(package_addr);
70 |
71 | if(dlc.AnalyChapter())
72 | {
73 | buttonDl.setEnabled(true);
74 | }
75 | else
76 | {
77 | JOptionPane.showMessageDialog(null, "可能的错误说明:\n1.漫画ID错误或网站已禁止该漫画,请检查。\n2.有可能是因为你下载过多导致被禁止IP了哟(尤其是网站上的版权受限漫画,下载过多特别容易被察觉而禁止),\n可以尝试ping www.manhuagui.com来确认。如果ping不通而通过手机无线网络却可以访问就基本确实是被封IP了。\n(可以重新拨号或重启光钎盒子来换IP)",
78 | "错误说明", JOptionPane.CLOSED_OPTION);
79 | }
80 |
81 | buttonanaly.setText("获取章节");
82 | buttonanaly.setEnabled(true);
83 | return;
84 | }
85 |
86 | public boolean StartDL(ButtonDownLoad buttonDl)
87 | {
88 | if(buttonDl != this.buttonDl) return false;
89 |
90 | buttonanaly.setEnabled(false);
91 | select.setEnabled(false);
92 | website.setEnabled(false);
93 | filepath.setEnabled(false);
94 | filetext.setEditable(false);
95 | dlc.setFilePath(filetext.getText());
96 | boolean result = dlc.StartDL((int)Math.pow(2, select.getSelectedIndex()) * 4);
97 | if(false == result)
98 | {
99 | buttonanaly.setEnabled(true);
100 | select.setEnabled(true);
101 | website.setEnabled(true);
102 | filepath.setEnabled(true);
103 | filetext.setEditable(true);
104 | }
105 |
106 | return result;
107 | }
108 |
109 | public void ResetCompnent()
110 | {
111 | buttonDl.setEnabled(true);
112 | buttonDl.setText("下载");
113 | buttonDl.resetDLStat();
114 | buttonanaly.setEnabled(true);
115 | select.setEnabled(true);
116 | website.setEnabled(true);
117 | filepath.setEnabled(true);
118 | }
119 |
120 | public void InterrputDL()
121 | {
122 | ResetCompnent();
123 | dlc.InterrputDL();
124 | }
125 |
126 | public String getFilePath()
127 | {
128 | return filetext.getText();
129 | }
130 |
131 | public void setFilePath(String filepath)
132 | {
133 | filetext.setText(filepath);
134 | }
135 |
136 | public ButtonFilePath getButtonFilePath() {
137 | return filepath;
138 | }
139 |
140 | public TextFileText getTextFileText() {
141 | return filetext;
142 | }
143 |
144 | public SelectWebSite getWebsite() {
145 | return website;
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/src/UI/SelectThread.java:
--------------------------------------------------------------------------------
1 | package UI;
2 |
3 | import javax.swing.JComboBox;
4 |
5 | @SuppressWarnings("serial")
6 | public class SelectThread extends JComboBox{
7 |
8 | public SelectThread()
9 | {
10 | this.addItem("4线程");
11 | this.addItem("8线程");
12 | this.setSelectedIndex(0);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/UI/SelectWebSite.java:
--------------------------------------------------------------------------------
1 | package UI;
2 |
3 | import javax.swing.JComboBox;
4 |
5 | @SuppressWarnings("serial")
6 | public class SelectWebSite extends JComboBox{
7 | public SelectWebSite()
8 | {
9 | for(ComicWebInfo cw : UIConfig.ComicWebList)
10 | {
11 | this.addItem(cw.getname());
12 | }
13 | this.setSelectedIndex(0);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/UI/TextComicID.java:
--------------------------------------------------------------------------------
1 | package UI;
2 |
3 | import java.awt.Color;
4 | import java.awt.event.KeyAdapter;
5 | import java.awt.event.KeyEvent;
6 | import java.awt.event.MouseAdapter;
7 | import java.awt.event.MouseEvent;
8 |
9 | import javax.swing.JTextField;
10 |
11 | @SuppressWarnings("serial")
12 | public class TextComicID extends JTextField{
13 |
14 | public TextComicID()
15 | {
16 | this.setText("请输入漫画ID");
17 | this.setForeground(Color.BLUE);
18 | this.addMouseListener(new MouseAdapter()
19 | {
20 | @Override
21 | public void mousePressed(MouseEvent e)
22 | {
23 | if(TextComicID.this.getText().equals("请输入漫画ID"))
24 | {
25 | TextComicID.this.setText("");
26 | TextComicID.this.setForeground(Color.BLACK);
27 | }
28 | }
29 | });
30 |
31 | this.addKeyListener(new KeyAdapter()
32 | {
33 | @Override
34 | public void keyTyped(KeyEvent e)
35 | {
36 | if(TextComicID.this.getText().equals("请输入漫画ID"))
37 | {
38 | TextComicID.this.setText("");
39 | TextComicID.this.setForeground(Color.BLACK);
40 | }
41 | int keychar = e.getKeyChar();
42 | if(!(keychar >= KeyEvent.VK_0 && keychar <= KeyEvent.VK_9))
43 | {
44 | e.consume();
45 | }
46 | }
47 | });
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/UI/TextFileText.java:
--------------------------------------------------------------------------------
1 | package UI;
2 |
3 | import java.io.File;
4 |
5 | import javax.swing.JTextField;
6 |
7 | @SuppressWarnings("serial")
8 | public class TextFileText extends JTextField{
9 |
10 | public TextFileText()
11 | {
12 | this.setBounds(0, 0, 387, 30);
13 | this.setText((new File(".")).getAbsolutePath());
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/UI/UIConfig.java:
--------------------------------------------------------------------------------
1 | package UI;
2 |
3 | import java.awt.Toolkit;
4 | import java.util.ArrayList;
5 |
6 | //这个类用于记录一些常用的变量
7 | public class UIConfig {
8 | public static final int WinH = (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight();
9 | public static final int WinW = (int)Toolkit.getDefaultToolkit().getScreenSize().getWidth();
10 | public static final int ComicH = 600;
11 | public static final int ComicW = 500;
12 | public static ArrayList ComicWebList = new ArrayList();
13 |
14 | static
15 | {
16 | ComicWebList.add(new ComicWebInfo("manhuagui.DlManhuagui", "漫画柜"));
17 | /*第一个参数表明你新增Comic子类的地址,需要包含包名
18 | * 第二个参数表明你想给这个网站取的名字,会显示在主界面的选择框中
19 | * */
20 | //ComicWebList.add(new ComicWebInfo("类地址", "漫画名"));
21 | }
22 | }
23 |
24 | class ComicWebInfo
25 | {
26 | private String package_addr = null;
27 | private String name = null;
28 |
29 | public ComicWebInfo(String package_addr,String name)
30 | {
31 | this.package_addr = package_addr;
32 | this.name = name;
33 | }
34 |
35 | public String getpackage_addr()
36 | {
37 | return package_addr;
38 | }
39 |
40 | public String getname()
41 | {
42 | return name;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | var LZString = (function () {
2 | var f = String.fromCharCode;
3 | var keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
4 | var baseReverseDic = {};
5 |
6 | function getBaseValue(alphabet, character) {
7 | if (!baseReverseDic[alphabet]) {
8 | baseReverseDic[alphabet] = {};
9 | for (var i = 0; i < alphabet.length; i++) {
10 | baseReverseDic[alphabet][alphabet.charAt(i)] = i
11 | }
12 | }
13 | return baseReverseDic[alphabet][character]
14 | }
15 | var LZString = {
16 | decompressFromBase64: function (input) {
17 | if (input == null) return "";
18 | if (input == "") return null;
19 | return LZString._0(input.length, 32, function (index) {
20 | return getBaseValue(keyStrBase64, input.charAt(index))
21 | })
22 | }, _0: function (length, resetValue, getNextValue) {
23 | var dictionary = [],
24 | next, enlargeIn = 4,
25 | dictSize = 4,
26 | numBits = 3,
27 | entry = "",
28 | result = [],
29 | i, w, bits, resb, maxpower, power, c, data = {
30 | val: getNextValue(0),
31 | position: resetValue,
32 | index: 1
33 | };
34 | for (i = 0; i < 3; i += 1) {
35 | dictionary[i] = i
36 | }
37 | bits = 0;
38 | maxpower = Math.pow(2, 2);
39 | power = 1;
40 | while (power != maxpower) {
41 | resb = data.val & data.position;
42 | data.position >>= 1;
43 | if (data.position == 0) {
44 | data.position = resetValue;
45 | data.val = getNextValue(data.index++)
46 | }
47 | bits |= (resb > 0 ? 1 : 0) * power;
48 | power <<= 1
49 | }
50 | switch (next = bits) {
51 | case 0:
52 | bits = 0;
53 | maxpower = Math.pow(2, 8);
54 | power = 1;
55 | while (power != maxpower) {
56 | resb = data.val & data.position;
57 | data.position >>= 1;
58 | if (data.position == 0) {
59 | data.position = resetValue;
60 | data.val = getNextValue(data.index++)
61 | }
62 | bits |= (resb > 0 ? 1 : 0) * power;
63 | power <<= 1
64 | }
65 | c = f(bits);
66 | break;
67 | case 1:
68 | bits = 0;
69 | maxpower = Math.pow(2, 16);
70 | power = 1;
71 | while (power != maxpower) {
72 | resb = data.val & data.position;
73 | data.position >>= 1;
74 | if (data.position == 0) {
75 | data.position = resetValue;
76 | data.val = getNextValue(data.index++)
77 | }
78 | bits |= (resb > 0 ? 1 : 0) * power;
79 | power <<= 1
80 | }
81 | c = f(bits);
82 | break;
83 | case 2:
84 | return ""
85 | }
86 | dictionary[3] = c;
87 | w = c;
88 | result.push(c);
89 | while (true) {
90 | if (data.index > length) {
91 | return ""
92 | }
93 | bits = 0;
94 | maxpower = Math.pow(2, numBits);
95 | power = 1;
96 | while (power != maxpower) {
97 | resb = data.val & data.position;
98 | data.position >>= 1;
99 | if (data.position == 0) {
100 | data.position = resetValue;
101 | data.val = getNextValue(data.index++)
102 | }
103 | bits |= (resb > 0 ? 1 : 0) * power;
104 | power <<= 1
105 | }
106 | switch (c = bits) {
107 | case 0:
108 | bits = 0;
109 | maxpower = Math.pow(2, 8);
110 | power = 1;
111 | while (power != maxpower) {
112 | resb = data.val & data.position;
113 | data.position >>= 1;
114 | if (data.position == 0) {
115 | data.position = resetValue;
116 | data.val = getNextValue(data.index++)
117 | }
118 | bits |= (resb > 0 ? 1 : 0) * power;
119 | power <<= 1
120 | }
121 | dictionary[dictSize++] = f(bits);
122 | c = dictSize - 1;
123 | enlargeIn--;
124 | break;
125 | case 1:
126 | bits = 0;
127 | maxpower = Math.pow(2, 16);
128 | power = 1;
129 | while (power != maxpower) {
130 | resb = data.val & data.position;
131 | data.position >>= 1;
132 | if (data.position == 0) {
133 | data.position = resetValue;
134 | data.val = getNextValue(data.index++)
135 | }
136 | bits |= (resb > 0 ? 1 : 0) * power;
137 | power <<= 1
138 | }
139 | dictionary[dictSize++] = f(bits);
140 | c = dictSize - 1;
141 | enlargeIn--;
142 | break;
143 | case 2:
144 | return result.join('')
145 | }
146 | if (enlargeIn == 0) {
147 | enlargeIn = Math.pow(2, numBits);
148 | numBits++
149 | }
150 | if (dictionary[c]) {
151 | entry = dictionary[c]
152 | } else {
153 | if (c === dictSize) {
154 | entry = w + w.charAt(0)
155 | } else {
156 | return null
157 | }
158 | }
159 | result.push(entry);
160 | dictionary[dictSize++] = w + entry.charAt(0);
161 | enlargeIn--;
162 | w = entry;
163 | if (enlargeIn == 0) {
164 | enlargeIn = Math.pow(2, numBits);
165 | numBits++
166 | }
167 | }
168 | }
169 | };
170 | return LZString
171 | })();
172 |
173 | function depressBase64(str,f)
174 | {
175 | var x = LZString.decompressFromBase64(str);
176 | return x.split(f);
177 | }
178 |
179 | function getcinfo(p, a, c, y, e, d)
180 | {
181 | e = function (c)
182 | {
183 | return (c < a ? "" : e(parseInt(c / a))) + ((c = c % a) > 35 ? String.fromCharCode(c + 29) : c.toString(36))
184 | };
185 | if (!''.replace(/^/, String))
186 | {
187 | while (c--) d[e(c)] = y[c] || e(c);
188 | y = [
189 | function (e) {
190 | return d[e]
191 | }
192 | ];
193 | e = function () {
194 | return '\\w+'
195 | };
196 | c = 1;
197 | };
198 | while (c--)
199 | if (y[c]) p = p.replace(new RegExp('\\b' + e(c) + '\\b', 'g'), y[c]);
200 | return p;
201 | }
202 |
203 | function parase(p, a, c, y, e)
204 | {
205 | return getcinfo(p, a, c, depressBase64(y, "|"), e, {});
206 | }
207 |
208 | function getChapter(str)
209 | {
210 | return LZString.decompressFromBase64(str);
211 | }
212 |
213 |
214 |
--------------------------------------------------------------------------------
/src/manhuagui/DlManhuagui.java:
--------------------------------------------------------------------------------
1 | package manhuagui;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Date;
5 | import java.util.regex.Matcher;
6 | import java.util.regex.Pattern;
7 |
8 | import javax.script.Invocable;
9 | import javax.script.ScriptEngine;
10 | import javax.script.ScriptEngineManager;
11 | import javax.swing.JOptionPane;
12 |
13 | import ComicCore.Chapter;
14 | import ComicCore.ComicInfo;
15 | import Start.LOG;
16 |
17 | public class DlManhuagui extends ComicInfo{
18 |
19 | @Override
20 | public ArrayList GetChapetr(String ComicId) {
21 | String UrlAddr = "https://www.manhuagui.com/comic/" + ComicId + "/?" +(new Date()).getTime();
22 |
23 | String HtmlInfo = this.GetHtmlInfo(UrlAddr.toString());
24 | if(null == HtmlInfo) return null;
25 |
26 | String Title = null;
27 | String Html = null;
28 | ArrayList Chapters = new ArrayList();
29 |
30 | String BookNameRex = "name: '(.+?)'";
31 | Pattern pattern = Pattern.compile(BookNameRex);
32 | Matcher matcher = pattern.matcher(HtmlInfo);
33 |
34 |
35 | while(matcher.find())
36 | {
37 | BookName = matcher.group(1);
38 | }
39 |
40 | //漫画柜在一部分网站上对于章节信息也采用了base64压缩,需要先进行解压缩
41 | if(HtmlInfo.indexOf("__VIEWSTATE") != -1)
42 | {
43 | HtmlInfo = adjustHtmlInfo(HtmlInfo);
44 | }
45 |
46 | //使用正则表达式进行匹配
47 | String Rex = " 0)
85 | {
86 | JOptionPane.showMessageDialog(null, "当前漫画为网站标定为版权受限的漫画,请不要下载过多,可能被封IP,详细见下面的说明", "提示", JOptionPane.CLOSED_OPTION);
87 | HtmlInfo = tmpHtmlInfo;
88 | }
89 |
90 | return HtmlInfo;
91 | }
92 |
93 | @Override
94 | public ArrayList GetPicUrlByChapter(String ChapterUrl) {
95 | String HtmlInfo = this.GetHtmlInfo(ChapterUrl);
96 | if(null == HtmlInfo) return null;
97 |
98 | String PicInfo = GetJsInfo(HtmlInfo);
99 | if(null == PicInfo)
100 | {
101 | LOG.log("JS解析失败");
102 | return null;
103 | }
104 |
105 | ArrayList PicturePath = new ArrayList();
106 | String path = null;
107 | String[] files = null;
108 | String cid = null;
109 | String md5 = null;
110 | String MatchString = "cid\":(.+?),\"cname(.+?)files\":\\[(.+?)\\],(.+?)path\":\"(.+?)\"(.+?)md5\":\"(.+?)\"";
111 | Pattern pattern = Pattern.compile(MatchString);
112 | Matcher match = pattern.matcher(PicInfo);
113 |
114 | while(match.find())
115 | {
116 | cid = match.group(1);
117 | path = match.group(5);
118 | files = match.group(3).replaceAll("\"","").replaceAll(".webp", "").split(",");
119 | md5 = match.group(7);
120 | }
121 | for(String file:files)
122 | {
123 | PicturePath.add("https://i.hamreus.com" + path + file + "?cid="+ cid + "&md5=" + md5);
124 | }
125 |
126 | return PicturePath;
127 | }
128 |
129 | /*从网页内容中获取我们需要用于解码的JS,这里漫画柜使用了2层压缩:
130 | * 1.eval函数本身执行的就是一个parker压缩;
131 | * 2.对于parker压缩的第四个入参也就是k,需要先进行base64解压缩;
132 | * 这个函数的目的就是为了获取解码需要的相关参数*/
133 | private String GetJsInfo(String HtmlInfo)
134 | {
135 | if(null == HtmlInfo) return null;
136 |
137 | String Para1 = null,Para2 = null,Para3 = null,Para4 = null,Para5 = null;
138 | String Base64StringMatch = "\\(function\\(p,a,c,k,e,d\\)\\{(.+?)\\}\\('(.+?)',(.+?),(.+?),'(.+?)'(.+?),(.+?),";
139 |
140 | Pattern pattern = Pattern.compile(Base64StringMatch);
141 | Matcher match = pattern.matcher(HtmlInfo);
142 |
143 | while(match.find())
144 | {
145 | Para1 = match.group(2);
146 | Para2 = match.group(3);
147 | Para3 = match.group(4);
148 | Para4 = match.group(5);
149 | Para5 = match.group(7);
150 | }
151 |
152 | if(null == Para1)
153 | {
154 | LOG.log("Parked压缩解码失败");
155 | return null;
156 | }
157 |
158 | return paraseInfo(Para1,Integer.parseInt(Para2), Integer.parseInt(Para3), Para4,Integer.parseInt(Para5));
159 | }
160 | //此处直接调用修改自源HTML的脚本来进行解码,另外,此处采用eval(String)而不用eval(FileReader)是因为打包成JAR后无法使用后者
161 | private String paraseInfo(String para1, int para2, int para3, String para4, int para5)
162 | {
163 | ScriptEngineManager engineManager = new ScriptEngineManager();
164 | ScriptEngine engine = engineManager.getEngineByName("js");
165 | try {
166 | engine.eval(manhuaguiCfg.JSFile);
167 | Invocable inv = (Invocable)engine;
168 | String ParkerInfo = (String)inv.invokeFunction("parase", para1,para2,para3,para4,para5);
169 | return ParkerInfo;
170 | } catch (Exception e) {
171 | LOG.log(e.getMessage());
172 | e.printStackTrace();
173 | return null;
174 | }
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/src/manhuagui/manhuaguiCfg.java:
--------------------------------------------------------------------------------
1 | package manhuagui;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.InputStreamReader;
5 |
6 | import Start.LOG;
7 |
8 | public class manhuaguiCfg {
9 | public static String JSFile = "";
10 |
11 | static
12 | {
13 | BufferedReader br = new BufferedReader(new InputStreamReader(Start.Start.class.getResourceAsStream("/config.js")));
14 | String line = null;
15 |
16 | try {
17 | try{
18 | while((line = br.readLine()) != null)
19 | {
20 | JSFile = JSFile.concat(line);
21 | }
22 | }
23 | catch(Exception e)
24 | {
25 | LOG.log(e.getMessage());
26 | e.printStackTrace();
27 | }
28 | finally
29 | {
30 | br.close();
31 | }
32 | } catch (Exception e) {
33 | LOG.log(e.getMessage());
34 | e.printStackTrace();
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------