123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- /*
- * 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.following;
- import pd.configuration;
- import std.experimental.logger;
- public int followingHandle(string[] args, in Config config)
- {
- import std.getopt : getopt, GetOptException;
- import std.stdio : stdout, stderr;
- import mlib.term;
- import pd.pixiv;
- /* following (--private | --public) */
- if (args.length < 2) {
- stderr.writeln("pixiv_down: Must provide one of --public or --private.");
- stderr.writeln("Run 'pixiv_down following --help' for more information.");
- return 1;
- }
- bool fromPrivate = false;
- bool fromPublic = false;
- int skip;
- try {
- auto helpInformation = getopt(args,
- "private", &fromPrivate,
- "public", &fromPublic,
- "skip|s", &skip);
- if (helpInformation.helpWanted) {
- displayFollowingHelp();
- return 0;
- }
- } catch (GetOptException e) {
- stderr.writefln("pixiv_down following: %s", e.msg);
- stderr.writefln("Run 'pixiv_down help following' for more information.");
- }
- if (fromPublic && fromPrivate) {
- stderr.writefln("pixiv_down: Cannot download both publicly and privately followed accounts.");
- displayFollowingHelp();
- return 1;
- }
- if (!fromPublic && !fromPrivate) {
- stderr.writeln("pixiv_down: Must provide one of --public or --private.");
- stderr.writeln("Run 'pixiv_down following --help' for more information.");
- return 1;
- }
- const visibility = fromPrivate ? "privately" : "publicly";
- infof("Downloading %s followed accounts", visibility);
- stdout.writefln("Fetching %s followed accounts...", visibility);
- string[] userIds = fetchFollowingIDs(fromPrivate, skip, config);
- const total = userIds.length;
- infof("following: received total of %d accounts", total);
- Term.goUpAndClearLine(1);
- stdout.writefln("Fetched %s followed accounts.", visibility);
- foreach(index, id; userIds) {
- import app.cmds.artist;
- artistHandle(["artist", id], config);
- }
- return 0;
- }
- public void displayFollowingHelp()
- {
- import std.stdio : stderr;
- stderr.writefln(
- "pixiv_down following - Bulk download all creators you follow.\n" ~
- "\nUsage:\tpixiv_down following [--public|--private] [options]\n" ~
- "\nDownload all content from every creator that you follow, starting\n" ~
- "from your most recently followed creator down to your least recently\n" ~
- "followed creator.\n" ~
- "\nYou must specify whether to download your publicaly or privately\n" ~
- "followed creators by using the --public and --private options. They\n" ~
- "cannot be combined.\n" ~
- "\nOptions:\n" ~
- " -h, --help \tPrint this help message and exit.\n" ~
- " -s, --skip NUMBER\tSkip downloading the first NUMBER creators.\n" ~
- " --private \tDownload from privately followed creators.\n" ~
- " --public \tDownload from publicaly followed creators.\n" ~
- "\nExamples:\n" ~
- "\n Download all publicaly followed accounts:\n" ~
- " pixiv_down following --public\n" ~
- "\n Download all privately followed accounts, skipping the first 2:\n" ~
- " pixiv_down following --private --skip 2\n");
- }
- private:
- string[] fetchFollowingIDs(bool private_, in int skip, in Config config)
- {
- import std.array : array;
- import std.algorithm.iteration : map;
- import mlib.term;
- import pd.pixiv;
- import app.util : sleep;
- User[] users;
- long total;
- long offset = skip;
- do {
- User[] page = fetchFollowing(private_, offset, total, config);
- tracef("following page %s", page);
- users ~= page;
- offset += page.length;
- reportProgress(total - skip, offset - skip);
- sleep(1, 2, false);
- } while (offset < total);
- Term.clearCurrentLine();
- return cast(string[])users.map!(u => u.id).array;
- }
- void reportProgress(long total, long current)
- {
- import std.format: format;
- import std.stdio;
- import mlib.term;
- Term.clearCurrentLine();
- const ratioCompleted = cast(double)current / total;
- const prefix = format!"%d ["(current);
- const suffix = format!"] %3.0f%% "(ratioCompleted * 100);
- const barLength = Term.getColumnCount() - prefix.length - suffix.length;
- stdout.write(prefix);
- foreach(i; 0..barLength) {
- stdout.write(i < (ratioCompleted * barLength) ? '#' : ' ');
- }
- stdout.write(suffix);
- stdout.flush();
- }
|