utils.d 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /*
  2. * pixiv_down - CLI-based downloading tool for https://www.pixiv.net.
  3. * Copyright (C) 2024 Mio
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, version 3 of the License.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. */
  17. module pd.utils;
  18. import std.net.curl : HTTP;
  19. /// User Agent header that is used for requests to www.pixiv.net.
  20. enum UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Safari/605.1.1";
  21. /// Create a `std.net.curl.HTTP` client prepared for requests to pixiv.
  22. ///
  23. /// Params:
  24. /// phpsessid = The PHPSESSID cookie value.
  25. /// headers = Custom headers to use for this request.
  26. ///
  27. /// There are some default headers that will be used if they're not
  28. /// present in `headers`:
  29. ///
  30. /// * `accept: application/json, */*`
  31. /// * `host: www.pixiv.net`
  32. /// * `referer: https://www.pixiv.net`
  33. ///
  34. /// Returns: A HTTP client with the appropriate headers set for making
  35. /// requests to the pixiv servers.
  36. HTTP makeHTTPClient(string phpsessid, string[string] headers = string[string].init)
  37. {
  38. import std.experimental.logger: warningf;
  39. auto client = HTTP();
  40. headers.require("accept", "application/json, */*");
  41. headers.require("host", "www.pixiv.net");
  42. headers.require("referer", "https://www.pixiv.net");
  43. foreach(const ref header, const ref value; headers) {
  44. client.addRequestHeader(header, value);
  45. }
  46. client.onReceiveStatusLine = (HTTP.StatusLine statusLine) {
  47. if (statusLine.code < 200 || statusLine.code > 299) {
  48. // Shame you can't access the URL for the client...
  49. warningf("Received non-200 status code %d (%s)", statusLine.code, statusLine.reason);
  50. }
  51. };
  52. client.setCookie("PHPSESSID=" ~ phpsessid);
  53. client.setUserAgent(UserAgent);
  54. return client;
  55. }
  56. ///
  57. /// Attempt to make *path* safe for different operating systems.
  58. ///
  59. /// This will replace anything that is not: a hyphen ('-'), a period ('.'),
  60. /// an underscore ('_'), or a "word character" (includes numbers). Any
  61. /// characters found will be replaced by a hyphen.
  62. ///
  63. /// Only tested on Linux and Android.
  64. ///
  65. /// Params:
  66. /// path = The input path to replace characters with.
  67. ///
  68. /// Returns: A new string with the replacements made.
  69. ///
  70. string makeSafe(string path)
  71. {
  72. import std.regex : regex, replaceAll;
  73. import std.string : chomp, replace, strip;
  74. enum kReplacement = "-";
  75. // 2.076 compat: reg must not be 'const'. Doesn't work with replaceAll.
  76. auto reg = regex(`[^-\w._]`);
  77. const noSpaces = strip(path).replace(" ", kReplacement);
  78. const almostSafe = replaceAll(noSpaces, reg, kReplacement);
  79. return chomp(almostSafe, "._-");
  80. }
  81. /**
  82. * Suspend the current thread for a random value between *lowest* and
  83. * *largest*.
  84. *
  85. * Params:
  86. * lowest = The lowest value to use for the random sleep duration.
  87. * largest = The largest value to use for the random sleep duration.
  88. * print = Print a message to `stderr` with sleep duration.
  89. */
  90. void sleep(int lowest, int largest, bool print = true)
  91. {
  92. import core.thread : Thread;
  93. import core.time : seconds;
  94. import std.random : Random, uniform, unpredictableSeed;
  95. auto seed = Random(unpredictableSeed);
  96. const secs = uniform(lowest, largest + 1, seed);
  97. if (print) {
  98. import std.stdio : stderr;
  99. stderr.writefln("Sleeping for %d seconds...", secs);
  100. }
  101. Thread.sleep(secs.seconds);
  102. }
  103. struct StopSpinnerMessage {}
  104. void runSpinner(string prefix)
  105. {
  106. import core.time : msecs;
  107. import core.thread : Thread;
  108. import std.concurrency : receiveTimeout;
  109. import std.stdio : stderr;
  110. import mlib.term;
  111. const delay = 100.msecs;
  112. const segments = ['|', '/', '-', '\\'];
  113. bool running = true;
  114. while (running) {
  115. for (auto i = 0; i < segments.length; ++i) {
  116. receiveTimeout(1.msecs,
  117. (StopSpinnerMessage _) {
  118. running = false;
  119. return;
  120. }
  121. );
  122. if (!running) {
  123. break;
  124. }
  125. stderr.writef("\r\033[2K%s %c", prefix, segments[i]);
  126. stderr.flush();
  127. Thread.sleep(delay);
  128. }
  129. }
  130. }