• R/O
  • SSH

Commit

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Auxiliary tools for users of pixiv_down


Commit MetaInfo

Revisióncfb7b2aa29cae9cbf8bc6cb00bee4529d63ad1c0 (tree)
Tiempo2023-11-29 20:30:29
Autornemophila <stigma@disr...>
Commiternemophila

Log Message

Reformat combine_users

Cambiar Resumen

Diferencia incremental

diff -r 5998b942e91b -r cfb7b2aa29ca pixiv_combine_users.d
--- a/pixiv_combine_users.d Wed Nov 29 20:12:55 2023 +1000
+++ b/pixiv_combine_users.d Wed Nov 29 21:30:29 2023 +1000
@@ -15,17 +15,6 @@
1515 */
1616 module pixiv_combine_users;
1717
18-import std.file;
19-import std.path : buildPath;
20-import std.stdio;
21-
22-import mlib.configparser;
23-import mlib.directories;
24-import mlib.trash;
25-
26-import pixivd;
27-import pixivd.types;
28-
2918 struct Config
3019 {
3120 string sessid;
@@ -34,32 +23,36 @@
3423
3524 int main(string[] args)
3625 {
37- import std.experimental.logger : critical;
38-
39- Config config;
40-
41- setupLogger();
26+ import std.experimental.logger : critical;
27+ import std.file : FileException;
28+ import std.stdio : stderr;
4229
43- try {
44- loadConfig(config);
45- } catch (FileException fe) {
46- critical(fe.msg);
47- stderr.writeln(fe.msg);
48- stderr.writeln("Have you used both pixivd and pixiv_down before?");
49- return 1;
50- } catch (NoSectionException nse) {
51- stderr.writeln("Couldn't find 'output' section in the pixiv_down config.");
52- stderr.writeln("Please see pixiv_down documentation on setting this.");
53- return 1;
54- } catch (NoOptionException noe) {
55- stderr.writeln("Couldn't find the 'base_folder' option in the pixiv_down config.");
56- stderr.writeln("Please see pixiv_down documentation on setting this.");
57- return 1;
58- }
30+ import mlib.configparser : NoSectionException, NoOptionException;
5931
60- string[][string] dupes = findDupes(config);
61- removeDupes(dupes, config);
62- return 0;
32+ Config config;
33+
34+ setupLogger();
35+
36+ try {
37+ config = loadConfig();
38+ } catch (FileException fe) {
39+ critical(fe.msg);
40+ stderr.writeln(fe.msg);
41+ stderr.writeln("Have you used both pixivd and pixiv_down before?");
42+ return 1;
43+ } catch (NoSectionException nse) {
44+ stderr.writeln("Couldn't find 'output' section in the pixiv_down config.");
45+ stderr.writeln("Please see pixiv_down documentation on setting this.");
46+ return 1;
47+ } catch (NoOptionException noe) {
48+ stderr.writeln("Couldn't find the 'base_folder' option in the pixiv_down config.");
49+ stderr.writeln("Please see pixiv_down documentation on setting this.");
50+ return 1;
51+ }
52+
53+ string[][string] dupes = findDupes(config);
54+ removeDupes(dupes, config);
55+ return 0;
6356 }
6457
6558 private void setupLogger()
@@ -96,26 +89,28 @@
9689 * pixivd is used to check the current user's <displayName>, while pixiv_down's
9790 * settings are used to see where the pictures are stored.
9891 */
99-void loadConfig(ref Config config)
92+private Config loadConfig()
10093 {
101- import std.string : strip;
102-
103- DirEntry configDir = open(Directory.config);
104- string pdPath = buildPath(configDir.name, "pixivd", "phpsessid.txt");
105- string pdownPath = buildPath(configDir.name, "pixiv_down", "settings.conf");
94+ import std.experimental.logger : critical;
95+ import std.file : DirEntry, readText;
96+ import std.path : buildPath;
97+ import std.string : strip;
98+ import std.stdio : stderr;
10699
107- if (false == exists(pdPath)) {
108- throw new FileException(pdPath, "file does not exist");
109- }
110- if (false == exists(pdownPath)) {
111- throw new FileException(pdownPath, "file does not exist");
112- }
100+ import mlib.configparser : ConfigParser;
101+ import mlib.directories : Directory, open;
113102
114- config.sessid = File(pdPath, "r").readln().strip();
103+ const configDir = open(Directory.config);
104+ const pdPath = buildPath(configDir.name, "pixivd", "phpsessid.txt");
105+ const pdownPath = buildPath(configDir.name, "pixiv_down", "settings.conf");
115106
116- scope ConfigParser parser = new ConfigParser();
117- parser.read(pdownPath);
118- config.baseFolder = parser.get("output", "base_folder");
107+ // May throw, but this is expected and caught in main.
108+ const sessid = pdPath.readText.strip;
109+ scope parser = new ConfigParser();
110+ parser.read(pdownPath);
111+ const baseDir = parser.get("output", "base_folder");
112+
113+ return Config(sessid, baseDir);
119114 }
120115
121116 /**
@@ -131,116 +126,129 @@
131126 *
132127 * Each returned ID will have more than one <displayName>.
133128 */
134-string[][string] findDupes(in ref Config config)
129+private string[][string] findDupes(in ref Config config)
135130 {
136- import std.array : join, split;
137- import std.path : baseName;
138-
139- const cwd = getcwd();
140- scope(exit) chdir(cwd);
141-
142- chdir(config.baseFolder);
143-
144- string[][string] ids;
145-
146- foreach(string name; dirEntries(config.baseFolder, SpanMode.shallow)) {
147- string bname = baseName(name);
148- string[] spl = bname.split("_");
149- string id = spl[0];
131+ import std.array : join, split;
132+ import std.path : baseName;
133+ import std.file : SpanMode, chdir, dirEntries, getcwd;
150134
151- /* Don't include any non-numerical "ids" (other directories) */
152- if (id[0] < '0' && id[0] > '9')
153- continue;
154-
155- ids[id] ~= spl[1..$].join("_");
156- }
135+ const cwd = getcwd();
136+ scope(exit) chdir(cwd);
157137
158- foreach(id, names; ids) {
159- if (names.length == 1) {
160- ids.remove(id);
161- }
162- }
163-
164- return ids;
165-}
138+ chdir(config.baseFolder);
166139
167-void prettyPrint(string[] arr)
168-{
169- foreach(num, elem; arr) {
170- writefln(" %d) %s", num, elem);
171- }
140+ string[][string] ids;
141+
142+ foreach(string name; dirEntries(config.baseFolder, SpanMode.shallow)) {
143+ string bname = baseName(name);
144+ string[] spl = bname.split("_");
145+ string id = spl[0];
146+
147+ /* Don't include any non-numerical "ids" (other directories) */
148+ if (id[0] < '0' && id[0] > '9')
149+ continue;
150+
151+ ids[id] ~= spl[1..$].join("_");
152+ }
153+
154+ foreach(id, names; ids) {
155+ if (names.length == 1) {
156+ ids.remove(id);
157+ }
158+ }
159+
160+ return ids;
172161 }
173162
174-void removeDupes(string[][string] dupes, in ref Config config)
163+private void prettyPrint(string[] arr)
175164 {
176- import core.thread : Thread;
177- import core.time : dur;
178- import std.experimental.logger : criticalf;
179- import std.string : empty;
165+ import std.stdio : writefln;
180166
181- Client client = new Client(config.sessid);
182- foreach(id, names; dupes) {
183- try {
184- FullUser user = client.fetchUser(id);
185- writefln("All names for ID %s (current: %s):", id, user.userName);
186- prettyPrint(names);
187- write("Which number should be used? ");
188- int choice;
189- readf!" %d"(choice);
190- removeDupe(id, names, choice, config);
191- } catch (PixivJSONException pje) {
192- criticalf("Fetching ID '%s': %s", id, pje.message);
193- stderr.writeln(pje.msg);
194- continue;
195- }
196-
197- writeln("Sleeping for 3 seconds");
198- Thread.sleep(dur!"seconds"(3));
199- }
200- writeln("Done removing duplicates! (if any!)");
167+ foreach(num, elem; arr) {
168+ writefln(" %d) %s", num, elem);
169+ }
201170 }
202171
203-void removeDupe(string id, string[] names, int choice, in ref Config config)
172+private void removeDupes(string[][string] dupes, in ref Config config)
204173 {
205- import std.algorithm.mutation : remove;
206- import std.array : array, split;
207- import std.experimental.logger : tracef, infof;
208- import std.path : baseName, dirSeparator;
209-
210- const newPath = buildPath(config.baseFolder, id ~ "_" ~ names[choice]);
211- writefln("Moving all files for %s to %s", names[choice], newPath);
212- names = names.remove(choice);
213-
214- /* eww */
215- foreach(name; names) {
216- const baseDir = buildPath(config.baseFolder, id ~ "_" ~ name);
217- foreach(entry; dirEntries(baseDir, SpanMode.breadth)) {
174+ import core.thread : Thread;
175+ import core.time : dur;
176+ import std.experimental.logger : criticalf;
177+ import std.string : empty;
178+ import std.stdio : stderr, readf, write, writefln;
218179
219- // We need to account for Manga and multi-page illustrations.
220- string maybeIllustId = entry.name.split(dirSeparator)[$-2];
221- string destPath = newPath;
222- if (maybeIllustId != (id ~ "_" ~ name)) {
223- // Is Manga or multi-paged illustration.
224- destPath = buildPath(destPath, maybeIllustId);
225- tracef("Found Manga or multi-paged illust--output: %s", destPath);
226- }
180+ import pixivd : Client;
181+ import pixivd.types : FullUser, PixivJSONException;
227182
228- if (false == exists(buildPath(destPath, baseName(entry.name)))) {
229- writefln("%s does not exist in %s... Copying.", entry.name, destPath);
230- string fname = baseName(entry.name);
231- destPath = buildPath(destPath, fname);
232- // FIXME: Why not just rename() the directory?
233- if (isDir(entry.name)) {
234- infof("Making directory: %s", destPath);
235- mkdir(destPath);
236- continue;
237- }
238- infof("Copying %s to %s", entry.name, destPath);
239- copy(entry.name, destPath);
183+ Client client = new Client(config.sessid);
184+ foreach(id, names; dupes) {
185+ try {
186+ FullUser user = client.fetchUser(id);
187+ writefln("All names for ID %s (current: %s):", id, user.userName);
188+ prettyPrint(names);
189+ write("Which number should be used? ");
190+ int choice;
191+ readf!" %d"(choice);
192+ removeDupe(id, names, choice, config);
193+ } catch (PixivJSONException pje) {
194+ criticalf("Fetching ID '%s': %s", id, pje.message);
195+ stderr.writeln(pje.msg);
196+ continue;
197+ }
198+
199+ write("Sleeping for 3 seconds\n");
200+ Thread.sleep(dur!"seconds"(3));
201+ }
202+ write("Done removing duplicates! (if any!)\n");
203+}
204+
205+private void removeDupe(string id, string[] names, int choice, in ref Config config)
206+{
207+ import std.file;
208+
209+ import std.algorithm.mutation : remove;
210+ import std.array : array, split;
211+ import std.experimental.logger : tracef, infof;
212+ import std.path : baseName, buildPath, dirSeparator;
213+ import std.stdio : writeln, writefln;
214+
215+ import mlib.trash : trash;
216+
217+ enum kReplacement = "-";
218+
219+ const newPath = buildPath(config.baseFolder, id ~ kReplacement ~ names[choice]);
220+ writefln("Moving all files for %s to %s", names[choice], newPath);
221+ names = names.remove(choice);
222+
223+ /* eww */
224+ foreach(name; names) {
225+ const baseDir = buildPath(config.baseFolder, id ~ kReplacement ~ name);
226+ foreach(entry; dirEntries(baseDir, SpanMode.breadth)) {
227+ // We need to account for Manga and multi-page illustrations.
228+ string maybeIllustId = entry.name.split(dirSeparator)[$-2];
229+ string destPath = newPath;
230+ if (maybeIllustId != (id ~ kReplacement ~ name)) {
231+ // Is Manga or multi-paged illustration.
232+ destPath = buildPath(destPath, maybeIllustId);
233+ tracef("Found Manga or multi-paged illust--output: %s", destPath);
234+ }
235+
236+ if (false == exists(buildPath(destPath, baseName(entry.name)))) {
237+ writefln("%s does not exist in %s... Copying.", entry.name, destPath);
238+ string fname = baseName(entry.name);
239+ destPath = buildPath(destPath, fname);
240+ // FIXME: Why not just rename() the directory?
241+ if (isDir(entry.name)) {
242+ infof("Making directory: %s", destPath);
243+ mkdir(destPath);
244+ continue;
240245 }
241- }
242- writeln("Moved everything from ", baseDir, "... Deleting.");
243- infof("Trashing '%s'", baseDir);
244- trash(baseDir);
245- }
246+ infof("Copying %s to %s", entry.name, destPath);
247+ copy(entry.name, destPath);
248+ }
249+ }
250+ writeln("Moved everything from ", baseDir, "... Deleting.");
251+ infof("Trashing '%s'", baseDir);
252+ trash(baseDir);
253+ }
246254 }