123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- /*
- * pixiv_down - CLI-based downloading tool for https://www.pixiv.net.
- * Copyright (C) 2024 Mio
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- module app.cmds.artist;
- import pd.configuration;
- import pd.pixiv;
- int artistHandle(string[] args, in Config config)
- {
- import std.algorithm.searching : all, find;
- import std.getopt : getopt, GetoptOptions = config;
- import std.stdio : stderr, stdout;
- import app.util : sleep;
- /* artist <id> */
- if (args.length < 2) {
- displayArtistHelp();
- return 3;
- }
- string enteredType;
- int mangaOffset;
- int illustOffset;
- int novelOffset;
- const helpInformation = getopt(args,
- GetoptOptions.passThrough,
- "type|t", &enteredType,
- "skip-illust", &illustOffset,
- "skip-manga", &mangaOffset,
- "skip-novel", &novelOffset);
- if (helpInformation.helpWanted) {
- displayArtistHelp();
- return 0;
- }
- bool pred(string arg) { return arg.all!("a >= '0' && a <= '9'"); }
- string[] ids = find!(pred)(args);
- if (enteredType != "illust" && enteredType != "manga" &&
- enteredType != "novel" && enteredType != "") {
- stderr.writefln("%s: Invalid Content Type: %s", args[0], enteredType);
- stderr.writeln("Valid Content Types are: 'illust', 'manga', and 'novel'.");
- stderr.writeln("To download all content, do not use the --type option.");
- return 3;
- }
- // --skip-manga and --skip-illust are only valid when there is one
- // account ID provided.
- if (ids.length > 1 && (mangaOffset != 0 || illustOffset != 0 || novelOffset != 0)) {
- stderr.writefln("%s: Cannot provide an offset for more than one artist.",
- args[0]);
- stderr.writefln("Run '%s artist --help' for more information.", args[0]);
- return 1;
- }
- foreach (id; ids) {
- switch (enteredType) {
- case "illust":
- downloadArtist(id, "illust", illustOffset, config);
- break;
- case "manga":
- downloadArtist(id, "manga", mangaOffset, config);
- break;
- case "novel":
- downloadArtist(id, "novel", novelOffset, config);
- break;
- default:
- downloadArtist(id, "novel", novelOffset, config);
- sleep(5, 10);
- downloadArtist(id, "manga", mangaOffset, config);
- sleep(5, 10);
- downloadArtist(id, "illust", illustOffset, config);
- break;
- }
- sleep(10, 20);
- }
- return 0;
- }
- ///
- /// Display the help message for the "artist" command.
- ///
- void displayArtistHelp()
- {
- import std.stdio : stderr;
- stderr.writeln("pixiv_down artist - Download content from specified artists\n" ~
- "\nUsage:\tpixiv_down artist [options] <IDs...>\n" ~
- "\nBy default, running the command with no options will result in\n" ~
- "downloading all content from each supported content type. You\n" ~
- "can change this by using the \"--type\" option.\n" ~
- "\nOptions:\n" ~
- " -h, --help \tDisplay this help message and exit.\n" ~
- " -t, --type \tLimit the type of content to download.\n" ~
- " \t Valid values are: illust, manga, novel\n" ~
- " --skip-manga \tThe number of manga to skip when\n" ~
- " \tdownloading the specified artist.\n" ~
- " --skip-illust\tThe number of illustrations to skip when\n" ~
- " \tdownloading the specified artist.\n" ~
- " --skip-novel \tThe number of novels to skip when\n" ~
- " \tdownloading the specified artist.\n" ~
- "\nExamples:\n" ~
- "\n Download all content from an artist:\n" ~
- " pixiv_down artist 10109777\n" ~
- "\n Download all manga from an artist:\n" ~
- " pixiv_down artist --type manga 10109777\n" ~
- "\n Download all illustrations from multiple artists:\n" ~
- " pixiv_down artist --type illust 10109777 4938312\n" ~
- "\n Skip the first 10 illustrations from an artist.\n" ~
- " pixiv_down artist --skip-illust 10 10109777\n" ~
- "\nNotes:\n" ~
- "The --skip-manga, --skip-illust, and --skip-novel options only work\n" ~
- "when a single artist ID has been provided. They will NOT restrict\n" ~
- "the content type downloaded.");
- }
- private:
- void downloadArtist(string id, string type, int offset, in Config config)
- {
- import std.experimental.logger;
- import std.net.curl : CurlException;
- import std.stdio : stdout, stderr;
- import pd.pixiv;
- import pd.pixiv_downloader;
- import mlib.term;
- import app.util : sleep;
- const user = fetchUser(id, config);
- const userProfile = fetchUserProfie(id, config);
- if ("illust" == type) {
- infof("number of illustrations to download: %d", userProfile.illusts.length);
- if (0 == userProfile.illusts.length) {
- stdout.writefln("No illustrations to download from %s.", user.userName);
- return;
- } else if (offset >= userProfile.illusts.length) {
- stdout.writefln("No illustrations to download from %s - they were all skipped!", user.userName);
- return;
- }
- stdout.writefln("Downloading illustrations by %s.", user.userName);
- foreach (elemNum, illustId; userProfile.illusts[offset..$]) {
- auto info = fetchArtworkInfo(illustId, config);
- try {
- downloadArtwork(info, config);
- } catch (CurlException ce) {
- errorf("exception in curl: %s", ce.msg);
- Term.clearCurrentLine(Yes.useStderr);
- stderr.writefln("Failed to download %s. Will retry.", info.type);
- sleep(20, 30);
- downloadArtwork(info, config);
- }
- if (elemNum == 0 || elemNum % 10 != 0) {
- sleep(4, 10);
- } else {
- sleep(10, 20);
- }
- Term.goUpAndClearLine(1, Yes.useStderr);
- }
- stdout.writefln("Downloaded illustrations by %s.", user.userName);
- }
- else if ("manga" == type) {
- infof("Number of manga to download: %d", userProfile.manga.length);
- if (0 == userProfile.manga.length) {
- stdout.writefln("No manga to download from %s.", user.userName);
- return;
- } else if (offset >= userProfile.manga.length) {
- stdout.writefln("No manga to download from %s - they were all skipped!", user.userName);
- return;
- }
- stdout.writefln("Downloading manga by %s.", user.userName);
- foreach (elemNum, illustId; userProfile.manga[offset..$]) {
- ArtworkInfo info = fetchArtworkInfo(illustId, config);
- try {
- downloadArtwork(info, config);
- } catch (CurlException ce) {
- errorf("exception in curl: %s", ce.msg);
- Term.clearCurrentLine(Yes.useStderr);
- stderr.writefln("Failed to download %s. Will retry.", info.type);
- sleep(20, 30);
- downloadArtwork(info, config);
- }
- if (elemNum == 0 || elemNum % 10 != 0) {
- sleep(4, 10);
- } else {
- sleep(10, 20);
- }
- Term.goUpAndClearLine(1, Yes.useStderr);
- }
- stdout.writefln("Downloaded manga by %s.", user.userName);
- }
- else if ("novel" == type) {
- infof("Number of novels to download: %d", userProfile.novels.length);
- if (0 == userProfile.novels.length) {
- stdout.writefln("No novels to download from %s.", user.userName);
- return;
- }
- if (offset >= userProfile.novels.length) {
- stdout.writefln("No novels to download from %s -- they were all skipped!", user.userName);
- return;
- }
- stdout.writefln("Downloading novels by %s", user.userName);
- foreach(elemNum, novelId; userProfile.novels[offset..$]) {
- auto info = fetchNovelInfo(novelId, config);
- try {
- downloadNovel(info, config);
- } catch (CurlException ce) {
- errorf("exception in curl: %s", ce.msg);
- Term.clearCurrentLine(Yes.useStderr);
- stderr.writeln("Failed to download novel. Will retry.");
- sleep(20, 30);
- downloadNovel(info, config);
- }
- if (elemNum == 0 || elemNum % 10 != 0) {
- sleep(4, 10);
- } else {
- sleep(10, 20);
- }
- Term.goUpAndClearLine(1, Yes.useStderr);
- }
- stdout.writefln("Downloaded novels by %s.", user.userName);
- }
- else
- {
- import std.format : format;
- string msg = format!"Unsupported operation (DOWNLOADING %s)"(type);
- throw new Exception(msg);
- }
- }
|