-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathFaDownloader.cs
More file actions
639 lines (561 loc) · 21.2 KB
/
Copy pathFaDownloader.cs
File metadata and controls
639 lines (561 loc) · 21.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using System.Collections.Generic;
namespace FurryDownloader
{
public partial class mainFrom : Form
{
//基本参数
private string userName; // 作者名
private string filePath; // 下载存放目录
private string cookie = ""; // cookie
//高级参数
private int startPageNum = 1; // 下载起始页的页号,默认1
private int startPicNum = 1; // 从当前页的第几张开始下载,默认1
private int totalDownloadNum = 0; // 已下载的图片个数
private int skipDownloadNum = 0; // 已跳过下载的图片个数
private int maxDownloadNum = 0; // 最大下载量
private int currentDownloadTaskNum = 0; // 当前已添加的下载任务数量
private Thread workerThread; // 主循环线程
private DateTime startTime; // 开始下载前的时间
private bool isStop = false; // 是否停止下载
private HttpHelper http = new HttpHelper();
private string homeDir = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + "\\FaDownloader\\";
public mainFrom()
{
InitializeComponent();
LoadCookie();
LoadSetting();
}
/// <summary>
/// 创建文件夹
/// </summary>
/// <param name="dir"></param>
private void CreateDir(string dir)
{
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
}
/// <summary>
/// 创建文件
/// </summary>
/// <param name="path"></param>
private void CreateFile(string path)
{
if (!File.Exists(path))
{
File.Create(path).Close();
}
}
#region cookie文件函数
/// <summary>
/// 读取cookie
/// </summary>
private void LoadCookie()
{
string cookiePath = homeDir + "cookie.txt";
CreateDir(homeDir);
//如果已经存在cookie文件则读取
if (File.Exists(cookiePath))
{
cookie = File.ReadAllText(cookiePath, Encoding.UTF8);
}
}
/// <summary>
/// 保存cookie
/// </summary>
private void SaveCookie(string cookie)
{
//为空则不存储
if (cookie == "")
return;
// 创建并写入
string path = homeDir + "cookie.txt";
CreateDir(homeDir);
CreateFile(path);
File.WriteAllText(path, cookie, Encoding.UTF8);
}
#endregion
#region 配置文件
private void LoadSetting()
{
string settingPath = homeDir + "setting.txt";
CreateDir(homeDir);
// 如果已经存在设置文件则读取
// 暂时只存储路径
if (File.Exists(settingPath))
{
filePath = File.ReadAllText(settingPath, Encoding.UTF8);
FilePath.Text = filePath;
}
}
private void SaveSetting(string setting)
{
if (filePath == "")
{
return;
}
// 创建并写入
string path = homeDir + "setting.txt";
CreateDir(homeDir);
CreateFile(path);
File.WriteAllText(path, setting, Encoding.UTF8);
}
#endregion
#region 操作控件的工具类
delegate void TextBoxDelegate(string str);
/// <summary>在textBox中追加信息,因为在其他线程无法操作主线程的控件,所以需要用这种方法<</summary>
/// <param name="str">要追加的信息</param>
public void AddItemToTextBox(string str)
{
try
{
if (textBox.InvokeRequired)
{
TextBoxDelegate d = AddItemToTextBox;
textBox.Invoke(d, str);
}
else
{
textBox.AppendText(str + "\r\n");
}
}
catch
{
}
}
delegate void FreezeDelegate();
/// <summary>
/// 冻结
/// </summary>
public void Freeze()
{
try
{
if (ButtonEnter.InvokeRequired)
{
FreezeDelegate d = Freeze;
ButtonEnter.Invoke(d);
}
else
{
//冻结所有控件
ButtonEnter.Enabled = false;
ButtonCancle.Enabled = true;
input_name.Enabled = false;
RadioButtonGallery.Enabled = RadioButtonScraps.Enabled = false;
Browse.Enabled = false;
InputPageNum.Enabled = InputStartPicNum.Enabled = InputMaxDownloadNum.Enabled = false;
}
}
catch
{
}
}
delegate void UnFreezeDelegate();
/// <summary>
/// 解冻
/// </summary>
public void UnFreeze()
{
try
{
if (ButtonEnter.InvokeRequired)
{
UnFreezeDelegate d = UnFreeze;
ButtonEnter.Invoke(d);
}
else
{
//解冻所有控件
ButtonEnter.Enabled = true;
ButtonCancle.Enabled = false;
input_name.Enabled = true;
RadioButtonGallery.Enabled = RadioButtonScraps.Enabled = true;
Browse.Enabled = true;
InputPageNum.Enabled = InputStartPicNum.Enabled = InputMaxDownloadNum.Enabled = true;
}
}
catch
{
}
}
#endregion
#region 按钮回调
/// <summary>
/// 确定按钮触发事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ButtonStart_Click(object sender, EventArgs e)
{
isStop = false;
//开始下载
workerThread = new Thread(DoDownload);
workerThread.Start();
}
/// <summary>
/// 取消按钮触发事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ButtonCancle_Click(object sender, EventArgs e)
{
isStop = true;
}
/// <summary>
/// 浏览按钮触发事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ButtonFolder_Click(object sender, EventArgs e)
{
FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
{
filePath = folderBrowserDialog.SelectedPath;
FilePath.Text = filePath;
// 保存设置
SaveSetting(filePath);
}
}
/// <summary>
/// 登录fa
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ButtonLogin_Click(object sender, EventArgs e)
{
FaLogin faLogin = new FaLogin();
faLogin.ShowDialog();
if (faLogin.Cookie != null && faLogin.Cookie != "")
{
SaveCookie(faLogin.Cookie);
LoadCookie();
MessageBox.Show("登录成功,已保存身份信息以供下次使用");
}
faLogin.Dispose();
}
/// <summary>
/// 帮助按钮触发事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Help_Click(object sender, EventArgs e)
{
string help = "本软件用于批量下载fa站图片\r\n" +
"请保证网络连接通畅\r\n" +
"必要时请给于软件管理员权限\r\n" +
"下载图片不成功时,请开启vpn后重新下载\r\n" +
"本软件仅供交流,请勿用于商业用途\r\n";
MessageBox.Show(help, "注意事项");
}
#endregion
#region 初始化函数和收尾函数
/// <summary>
/// 获取用户提供的数据
/// </summary>
private void GetUserInput()
{
// 获取画师名
userName = input_name.Text.Trim().Replace("_", "");
// 获取下载目录
filePath = FilePath.Text.Length == 0
? String.Format(".\\{0}\\", userName)
: String.Format("{0}\\{1}\\", FilePath.Text, userName);
// 处理高级选项参数
string inputPage = InputPageNum.Text.Trim();
string inputStartPic = InputStartPicNum.Text.Trim();
string inputMaxDownloadNum = InputMaxDownloadNum.Text.Trim();
// 起始页数,默认值为1
if (!int.TryParse(inputPage, out startPageNum) || startPageNum < 1)
this.startPageNum = 1;
// 从第几张开始下载,默认值为1
if (!int.TryParse(inputStartPic, out startPicNum) || startPicNum < 1)
this.startPicNum = 1;
// 最大下载量,默认为0,即不限量
if (!int.TryParse(inputMaxDownloadNum, out maxDownloadNum) || maxDownloadNum < 0)
this.maxDownloadNum = 0;
// 输出高级参数下载信息
AddItemToTextBox(string.Format("从第{0}页第{1}张开始下载\r\n", this.startPageNum, this.startPicNum));
}
/// <summary>
/// 下载前执行
/// </summary>
private void BeforeDownload()
{
// 冻结所有控件
Freeze();
// 修改回车焦点
this.AcceptButton = this.ButtonCancle;
// 下载总数和跳过总数清零
totalDownloadNum = 0;
skipDownloadNum = 0;
currentDownloadTaskNum = 0;
// 初始化开始时间
startTime = DateTime.Now;
// 设置最大线程数
DownloadManager.SetMaxThreads(16);
}
/// <summary>
/// 结束下载时执行
/// </summary>
private void AfterDownload()
{
// 解冻所有控件
UnFreeze();
DateTime now = DateTime.Now;// 下载结束后的时间
TimeSpan time = now - startTime;// 总下载时间
AddItemToTextBox(String.Format("共{0}小时{1}分钟{2}秒", time.Hours, time.Minutes, time.Seconds + "秒"));
AddItemToTextBox(String.Format("下载了{0}张,跳过了{1}张\r\n", totalDownloadNum, skipDownloadNum));
// 修改回车焦点
this.AcceptButton = this.ButtonEnter;
}
#endregion
#region 下载相关
/// <summary>
/// 下载勾选上的图集
/// </summary>
private void DoDownload()
{
// 检查必须的信息
if (input_name.Text == "")
{
MessageBox.Show("作者名不能为空!");
return;
}
BeforeDownload();
try
{
if (RadioButtonGallery.Checked)
{
download("gallery");
}
else if (RadioButtonScraps.Checked)
{
download("scraps");
}
DownloadManager.WaitForAllFinished();
}
catch (Exception e)
{
AddItemToTextBox(e.Message);
DownloadManager.StopAll();
DownloadManager.WaitForAllFinished();
}
AfterDownload();
}
/// <summary>
/// 下载图集
/// </summary>
/// <param name="type">gallery或者scraps</param>
private void download(string type)
{
AddItemToTextBox(type + "下载开始");
GetUserInput();
// 循环下载第pageNum页
while (true)
{
try
{
if (isStop)
{
throw new Exception("正在结束下载");
}
// 存储当前页面地址
string nowUrl = string.Format("https://www.furaffinity.net/{0}/{1}/{2}",
type,
userName,
startPageNum);
// 下载当前页
string html = GetHtml(nowUrl);
// 检查页面是否下载成功
if (html == null || html == "")
throw new NetworkException("请检查网络");
// 检查此用户是否存在
if (!Analyze.HasUser(html))
throw new NoSuchArtistException("未查询到该作者,请检查作者名称");
// 检查此页是否需要登录才能访问
if (!Analyze.NeedLogin(html))
throw new NeedLoginException("需要登陆后才能下载此作者的作品");
//--------开始详情页下载循环------------
// 获得所有详情页
List<String> pages = Analyze.GetPages(html);
// 检查此页是否还有图片,没有的话就结束并等待所有任务完成
AddItemToTextBox(String.Format("第{0}页共{1}张图片", startPageNum, pages.Count));
if (pages.Count == 0)
return;
for (int i = startPicNum - 1; i < pages.Count; ++i)
{
// 停止时不再添加新的任务
if (isStop)
{
throw new InterruptException("正在结束下载");
}
// 在到达最大下载量时停止
if (maxDownloadNum != 0 && currentDownloadTaskNum >= maxDownloadNum)
{
AddItemToTextBox("已到达最大任务量,正在等待下载完成");
return;
}
currentDownloadTaskNum++;
downloadPicture(pages[i], type);
}
startPicNum = 1;
startPageNum++;
}
catch (NetworkException)
{
if (checkBoxRetry.Checked)
{
AddItemToTextBox("网络不稳定,将在一秒后重试");
Thread.Sleep(1000);
}
else
{
throw;
}
}
}
}
/// <summary>
/// 根据详情页url下载此页面,解析其中的图片地址并下载
/// </summary>
/// <param name="pageUrl"></param>
/// <param name="type"></param>
private void downloadPicture(string pageUrl, string type)
{
// 获取缓存路径
string[] arr = pageUrl.Split('/');
string pageId = arr[arr.Length - 2];
string cacheDir = homeDir + "cache\\";
CreateDir(cacheDir);
string cacheFile = cacheDir + pageId;
string pictureUrl = null;
// 关闭缓存的话就顺便清理掉对应的缓存
if (checkBoxNoCache.Checked)
{
File.Delete(cacheFile);
}
// 从缓存中获取地址
else if (File.Exists(cacheFile))
{
pictureUrl = File.ReadAllText(cacheFile);
if (pictureUrl == "")
{
pictureUrl = null;
}
}
if (pictureUrl == null)
{
//获取详情页面信息
string html = GetHtml(pageUrl);
// 检查页面是否下载成功
if (html == null || html == "")
throw new Exception("请检查网络");
//获取图片下载地址
pictureUrl = Analyze.GetPictureUrl(html);
if (pictureUrl == null)
throw new Exception("无法分析出图片地址:" + pageUrl);
// 缓存下来图片地址
CreateFile(cacheFile);
File.WriteAllText(cacheFile, pictureUrl);
}
// 获取文件名字
string pictureName = Analyze.GetFileName(pictureUrl);
// 文件完整路径
string fullFilePath = filePath + type + "\\" + pictureName;
// 判断文件是否已经存在
if (File.Exists(fullFilePath))
{
AddItemToTextBox(String.Format("第{0}页第{1}张已存在,跳过", startPageNum, startPicNum));
startPicNum++;
skipDownloadNum++;
return;
}
// 使用多线程下载
Task task = new Task()
{
pageNum = startPageNum,
picNum = startPicNum,
Url = pictureUrl,
// gallery路径扁平化
FilePath = filePath + (type == "gallery" ? "" : type + "\\"),
FileName = pictureName,
TaskStart = new Task.TaskStartDelegate(delegate (int id, Task t)
{
// 下载开始
}),
TaskStop = new Task.TaskStopDelegate(delegate (int id, Task t)
{
// AddItemToTextBox("下载被终止" + t.FileName);
}),
TaskFinish = new Task.TaskFinishDelegate(delegate (int id, Task t)
{
totalDownloadNum++;
// 计算下载所用时间
DateTime now = DateTime.Now;
TimeSpan time = now - startTime;
AddItemToTextBox(String.Format("第{0}页第{1}张下载完成", t.pageNum, t.picNum));
}),
TaskFail = new Task.TaskFailDelegate(delegate (int id, Task t)
{
AddItemToTextBox(String.Format("第{0}页第{1}张下载失败,文件名:{2},url:{3}", t.pageNum, t.picNum, t.FileName, t.Url));
}),
};
DownloadManager.Add(task);
startPicNum++;
}
/// <summary>
/// 下载所给网址指向的页面信息
/// </summary>
/// <param name="url">网址</param>
/// <returns>页面信息</returns>
public string GetHtml(string url)
{
HttpItem item = new HttpItem()
{
URL = url,//URL 必需项
Encoding = Encoding.UTF8,//编码格式(utf-8,gb2312,gbk) 可选项 默认类会自动识别
//Encoding = Encoding.Default,
Method = "get",//URL 可选项 默认为Get
Timeout = 10000,//连接超时时间 可选项默认为100000
IsToLower = false,//得到的HTML代码是否转成小写 可选项默认转小写
Cookie = cookie,//字符串Cookie 可选项
UserAgent = "PostmanRuntime/7.17.1",//用户的浏览器类型,版本,操作系统 可选项有默认值
Accept = "*/*",// 可选项有默认值
Referer = "https://www.furaffinity.net",//来源URL 可选项
Host = "www.furaffinity.net",
ResultType = ResultType.String,//返回数据类型,是Byte还是String
Connectionlimit = 16,
// ProxyIp = "127.0.0.1:1081",
};
try
{
var response = http.GetHtml(item);
if (response.StatusCode == System.Net.HttpStatusCode.ServiceUnavailable)
{
throw new NetworkException("访问量过大,将稍后重试");
}
// 返回html
return response.Html;
}
catch (NetworkException)
{
throw;
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
throw new NetworkException("网络异常:" + e.Message);
}
}
#endregion
}
}