D wrapper around (some) of the pixiv web API
Révision | b92948ac0f252dcebc91ac0482b6556acec037ec (tree) |
---|---|
l'heure | 2023-08-13 11:15:44 |
Auteur | supercell <stigma@disr...> |
Commiter | supercell |
Start implementation of partial downloads
Currently it's limited to single-paged illustrations; Some cleanup and
refactoring is needed first.
@@ -18,6 +18,8 @@ import pixivd.types; | ||
18 | 18 | public enum PixivDVersion = 0.8; |
19 | 19 | public enum PixivDVersionString = "0.8"; |
20 | 20 | |
21 | +enum kUserAgent = "Mozilla/5.0 (Windows NT 10.0; rv:108.0) Gecko/20100101 Firefox/108.0"; | |
22 | + | |
21 | 23 | struct DownloadProgressEvent |
22 | 24 | { |
23 | 25 | size_t total; |
@@ -44,17 +46,24 @@ class Client | ||
44 | 46 | private: |
45 | 47 | |
46 | 48 | string m_phpsessid; |
49 | + | |
47 | 50 | HTTP m_client; |
48 | 51 | |
49 | 52 | bool m_isGMInitialized = false; |
50 | 53 | |
51 | 54 | public: |
52 | 55 | |
56 | + /** | |
57 | + * Return the PHPSESSID that is being used. | |
58 | + */ | |
53 | 59 | @property string phpsessid() const |
54 | 60 | { |
55 | 61 | return m_phpsessid; |
56 | 62 | } |
57 | 63 | |
64 | + /** | |
65 | + * Set a new PHPSESSID to be used for new requests. | |
66 | + */ | |
58 | 67 | @property void phpsessid(string sessionID) |
59 | 68 | { |
60 | 69 | m_phpsessid = sessionID; |
@@ -72,17 +81,19 @@ public: | ||
72 | 81 | { |
73 | 82 | m_phpsessid = phpsessid; |
74 | 83 | m_client = HTTP(); |
75 | - m_client.addRequestHeader("Accept", "application/json, */*"); | |
76 | - m_client.addRequestHeader("Host", "www.pixiv.net"); | |
77 | - m_client.addRequestHeader("Referer", "https://www.pixiv.net/"); | |
78 | - m_client.setUserAgent( | |
79 | - "Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefix/91.0"); | |
80 | - if ("" != m_phpsessid) | |
81 | - { | |
82 | - m_client.setCookie("PHPSESSID=" ~ m_phpsessid); | |
83 | - } | |
84 | + this._resetHeaders(); | |
84 | 85 | } |
85 | 86 | |
87 | + /** | |
88 | + * Create a new instance of Client. | |
89 | + * | |
90 | + * This will allow you to interact with the pixiv API without an | |
91 | + * account. However, your access will be limited to what is | |
92 | + * publically available. | |
93 | + * | |
94 | + * Use `phpsessid(string)` to set a PHPSESSID, meaning you will | |
95 | + * be logged in. | |
96 | + */ | |
86 | 97 | this() |
87 | 98 | { |
88 | 99 | this(""); |
@@ -710,18 +721,36 @@ private: | ||
710 | 721 | { |
711 | 722 | import std.datetime.systime; |
712 | 723 | |
713 | - import std.file : FileException, exists, setTimes; | |
724 | + import std.file : FileException, exists, getSize, rename, setTimes; | |
714 | 725 | import std.path : extension; |
715 | 726 | import std.stdio : File; |
716 | 727 | |
717 | 728 | const baseFileName = illust.id ~ illust.urls["original"].extension; |
729 | + const partFileName = baseFileName ~ ".part"; | |
718 | 730 | |
719 | 731 | if (true == exists(baseFileName) && false == overwrite) |
720 | 732 | { |
721 | 733 | throw new FileException(baseFileName, "File already exists"); |
722 | 734 | } |
723 | 735 | |
724 | - File imageFile = File(baseFileName, "w+"); | |
736 | + string mode = "w+"; | |
737 | + | |
738 | + if (exists(partFileName)) { | |
739 | + // Resume previous attempt at downloading. | |
740 | + m_client.url = illust.urls["original"]; | |
741 | + m_client.method = HTTP.Method.head; | |
742 | + m_client.perform(); | |
743 | + if ("accept-ranges" in m_client.responseHeaders()) { | |
744 | + ulong bytesRead = getSize(partFileName); | |
745 | + m_client.addRequestHeader("Range", format!"bytes=%d-"(bytesRead)); | |
746 | + mode = "a+"; | |
747 | + } | |
748 | + // If the "accept-ranges" header was not present, | |
749 | + // we'll restart the entire download. | |
750 | + m_client.method = HTTP.Method.get; | |
751 | + } | |
752 | + | |
753 | + File imageFile = File(partFileName, mode); | |
725 | 754 | |
726 | 755 | m_client.url = illust.urls["original"]; |
727 | 756 | m_client.onReceive = (ubyte[] data) { |
@@ -735,11 +764,13 @@ private: | ||
735 | 764 | |
736 | 765 | m_client.perform(); |
737 | 766 | imageFile.close(); |
767 | + rename(partFileName, baseFileName); | |
738 | 768 | emit(DownloadCompleteEvent()); |
739 | 769 | |
740 | 770 | SysTime createDate = SysTime.fromISOExtString(illust.createDate); |
741 | - | |
742 | 771 | setTimes(baseFileName, createDate, createDate); |
772 | + | |
773 | + this._resetHeaders(); | |
743 | 774 | } |
744 | 775 | |
745 | 776 | void _downloadPagedIllust(Illustration illust, bool overwrite) |
@@ -1014,4 +1045,18 @@ private: | ||
1014 | 1045 | DestroyExceptionInfo(&exception); |
1015 | 1046 | emit(DownloadCompleteEvent()); |
1016 | 1047 | } |
1048 | + | |
1049 | + void _resetHeaders() | |
1050 | + { | |
1051 | + m_client.clearRequestHeaders(); | |
1052 | + | |
1053 | + m_client.addRequestHeader("Accept", "application/json, */*"); | |
1054 | + m_client.addRequestHeader("Host", "www.pixiv.net"); | |
1055 | + m_client.addRequestHeader("Referer", "https://www.pixiv.net/"); | |
1056 | + m_client.setUserAgent(kUserAgent); | |
1057 | + if ("" != m_phpsessid) | |
1058 | + { | |
1059 | + m_client.setCookie("PHPSESSID=" ~ m_phpsessid); | |
1060 | + } | |
1061 | + } | |
1017 | 1062 | } |