forked from hydrusnetwork/hydrus
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclient_api.html
1554 lines (1553 loc) · 80.2 KB
/
client_api.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<html>
<head>
<title>client api</title>
<link href="hydrus.ico" rel="shortcut icon" />
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="content">
<h3 id="intro"><a href="#intro">client api</a></h3>
<p>The hydrus client now supports a very simple API so you can access it with external programs.</p>
<p>By default, the Client API is not turned on. Go to <i>services->manage services</i> and give it a port to get it started. I recommend you not allow non-local connections (i.e. only requests from the same computer will work) to start with.</p>
<p>The Client API should start immediately. It will only be active while the client is open. To test it is running all correct (and assuming you used the default port of 45869), try loading this:</p>
<a href="http://127.0.0.1:45869"><code>http://127.0.0.1:45869</code></a>
<p>You should get a welcome page. By default, the Client API is HTTP, which means it is ok for communication on the same computer or across your home network (e.g. your computer's web browser talking to your computer's hydrus), but not secure for transmission across the internet (e.g. your phone to your home computer). You can turn on HTTPS, but due to technical complexities it will give itself a self-signed 'certificate', so the security is good but imperfect, and whatever is talking to it (e.g. your web browser looking at <a href="https://127.0.0.1:45869">https://127.0.0.1:45869</a>) may need to add an exception.</p>
<p>The Client API is still experimental and sometimes not user friendly. If you want to talk to your home computer across the internet, you will need some networking experience. You'll need a static IP or reverse proxy service or dynamic domain solution like no-ip.org so your device can locate it, and potentially port-forwarding on your router to expose the port. If you have a way of hosting a domain and have a signed certificate (e.g. from <a href="https://letsencrypt.org/">Let's Encrypt</a>), you can overwrite the client.crt and client.key files in your 'db' directory and HTTPS hydrus should host with those.</p>
<p>Once the API is running, go to its entry in <i>services->review services</i>. Each external program trying to access the API will need its own access key, which is the familiar 64-character hexadecimal used in many places in hydrus. You can enter the details manually from the review services panel and then copy/paste the key to your external program, or the program may have the ability to request its own access while a mini-dialog launched from the review services panel waits to catch the request.</p>
<h3 id="tools"><a href="#tools">Browsers and tools created by hydrus users:</a></h3>
<ul>
<li><a href="https://gitgud.io/prkc/hydrus-companion">https://gitgud.io/prkc/hydrus-companion</a> - Hydrus Companion, a Chrome/Firefox extension for hydrus that allows easy download queueing as you browse and advanced login support</li>
<li><a href="https://github.com/floogulinc/hydrus-web">https://github.com/floogulinc/hydrus-web</a> - Hydrus Web, a web client for hydrus (allows phone browsing of hydrus)</li>
<li><a href="https://github.com/NO-ob/LoliSnatcher_Droid">https://github.com/NO-ob/LoliSnatcher_Droid</a> - LoliSnatcher, a booru client for Android that can talk to hydrus</li>
<li><a href="https://www.animebox.es/">https://www.animebox.es/</a> - Anime Boxes, a booru browser, now supports adding your client as a Hydrus Server</li>
<li><a href="https://ififfy.github.io/flipflip/#/">https://ififfy.github.io/flipflip/#/</a> - FlipFlip, an advanced slideshow interface, now supports hydrus as a source</li>
<li><a href="https://github.com/GoAwayNow/Iwara-Hydrus">https://github.com/GoAwayNow/Iwara-Hydrus</a> - Iwara-Hydrus, a userscript to simplify sending Iwara videos to Hydrus Network</li>
<li><a href="https://gitgud.io/koto/hydrus-archive-delete">https://gitgud.io/koto/hydrus-archive-delete</a> - Archive/Delete filter in your web browser</li>
<li><a href="https://gitgud.io/koto/hydrus-dd">https://gitgud.io/koto/hydrus-dd</a> - DeepDanbooru neural network tagging for Hydrus</li>
<li><a href="https://github.com/floogulinc/hyextract">https://github.com/floogulinc/hyextract</a> - Extract archives from Hydrus and reimport with tags and URL associations</li>
<li><a href="https://gitgud.io/prkc/dolphin-hydrus-actions">https://gitgud.io/prkc/dolphin-hydrus-actions</a> - Adds Hydrus right-click context menu actions to Dolphin file manager.</li>
</ul>
<h3 id="modules"><a href="#modules">Library modules created by hydrus users:</a></h3>
<ul>
<li><a href="https://gitlab.com/cryzed/hydrus-api">https://gitlab.com/cryzed/hydrus-api</a> - A python module that talks to the API.</li>
<li><a href="https://github.com/cravxx/hydrus.js">https://github.com/cravxx/hydrus.js</a> - A node.js module that talks to the API.</li>
</ul>
<h3 id="api"><a href="#api">API</a></h3>
<p>In general, the API deals with standard UTF-8 JSON. POST requests and 200 OK responses are generally going to be a JSON 'Object' with variable names as keys and values obviously as values. There are examples throughout this document. For GET requests, everything is in standard GET parameters, but some variables are complicated and will need to be JSON encoded and then URL encoded. An example would be the 'tags' parameter on <a href="#get_files_search_files">GET /get_files/search_files</a>, which is a list of strings. Since GET http URLs have limits on what characters are allowed, but hydrus tags can have all sorts of characters, you'll be doing this:</p>
<ul>
<li>
<p>Your list of tags:</p>
<p><code>[ 'character:samus aran', 'creator:青い桜', 'system:height > 2000' ]</code></p>
</li>
<li>
<p>JSON encoded:</p>
<p><code>["character:samus aran", "creator:\\u9752\\u3044\\u685c", "system:height > 2000"]</code></p>
</li>
<li>
<p>Then URL encoded:</p>
<p><code>%5B%22character%3Asamus%20aran%22%2C%20%22creator%3A%5Cu9752%5Cu3044%5Cu685c%22%2C%20%22system%3Aheight%20%3E%202000%22%5D</code></p>
</li>
<li>
<p>In python, converting your tag list to the URL encoded string would be:</p>
<p><code>urllib.parse.quote( json.dumps( tag_list ) )</code></p>
</li>
<li>
<p>Full URL path example:</p>
<p><code>/get_files/search_files?file_sort_type=6&file_sort_asc=false&tags=%5B%22character%3Asamus%20aran%22%2C%20%22creator%3A%5Cu9752%5Cu3044%5Cu685c%22%2C%20%22system%3Aheight%20%3E%202000%22%5D</code></p>
</li>
</ul>
<p>On 200 OK, the API returns JSON for everything except actual file/thumbnail requests. On 4XX and 5XX, assume it will return plain text, which may be a raw traceback that I'd be interested in seeing. You'll typically get 400 for a missing parameter, 401/403/419 for missing/insufficient/expired access, and 500 for a real deal serverside error.</p>
<h3 id="access"><a href="#access">Access and permissions</a></h3>
<p>The client gives access to its API through different 'access keys', which are the typical 64-character hex used in many other places across hydrus. Each guarantees different permissions such as handling files or tags. Most of the time, a user will provide full access, but do not assume this. If the access header or parameter is not provided, you will get 401, and all insufficient permission problems will return 403 with appropriate error text.</p>
<p>Access is required for every request. You can provide this as an http header, like so:</p>
<ul>
<li><p>Hydrus-Client-API-Access-Key : 0150d9c4f6a6d2082534a997f4588dcf0c56dffe1d03ffbf98472236112236ae</p></li>
</ul>
<p>Or you can include it as a GET or POST parameter on any request (except <i>POST /add_files/add_file</i>, which uses the entire POST body for the file's bytes). Use the same name for your GET or POST argument, such as:</p>
<ul>
<li><p>/get_files/thumbnail?file_id=452158&Hydrus-Client-API-Access-Key=0150d9c4f6a6d2082534a997f4588dcf0c56dffe1d03ffbf98472236112236ae</p></li>
</ul>
<p>There is now a simple 'session' system, where you can get a temporary key that gives the same access without having to include the permanent access key in every request. You can fetch a session key with the <a href="#session_key">/session_key</a> command and thereafter use it just as you would an access key, just with <i>Hydrus-Client-API-Session-Key</i> instead.</p>
<p>Session keys will expire if they are not used within 24 hours, or if the client is restarted, or if the underlying access key is deleted. An invalid/expired session key will give a <b>419</b> result with an appropriate error text.</p>
<p>Bear in mind the Client API is still under construction. Setting up the Client API to be accessible across the internet requires technical experience to be convenient. HTTPS is available for encrypted comms, but the default certificate is self-signed (which basically means an eavesdropper can't see through it, but your ISP/government could if they decided to target you). If you have your own domain and SSL cert, you can replace them though (check the db directory for client.crt and client.key). Otherwise, be careful about transmitting sensitive content outside of your localhost/network.</p>
<h3 id="contents"><a href="#contents">Contents</a></h3>
<ul>
<li>
<h4><a href="#access_management">Access Management</a></h4>
<ul>
<li><a href="#api_version">GET /api_version</a></li>
<li><a href="#request_new_permissions">GET /request_new_permissions</a></li>
<li><a href="#session_key">GET /session_key</a></li>
<li><a href="#verify_access_key">GET /verify_access_key</a></li>
<li><a href="#get_services">GET /get_services</a></li>
</ul>
<h4><a href="#adding_files">Adding Files</a></h4>
<ul>
<li><a href="#add_files_add_file">POST /add_files/add_file</a></li>
<li><a href="#add_files_delete_files">POST /add_files/delete_files</a></li>
<li><a href="#add_files_undelete_files">POST /add_files/undelete_files</a></li>
<li><a href="#add_files_archive_files">POST /add_files/archive_files</a></li>
<li><a href="#add_files_unarchive_files">POST /add_files/unarchive_files</a></li>
</ul>
<h4><a href="#adding_tags">Adding Tags</a></h4>
<ul>
<li><a href="#add_tags_clean_tags">GET /add_tags/clean_tags</a></li>
<li><a href="#add_tags_get_tag_services">GET /add_tags/get_tag_services</a> (legacy, do not use)</li>
<li><a href="#add_tags_add_tags">POST /add_tags/add_tags</a></li>
</ul>
<h4><a href="#adding_urls">Adding URLs</a></h4>
<ul>
<li><a href="#add_urls_get_url_files">GET /add_urls/get_url_files</a></li>
<li><a href="#add_urls_get_url_info">GET /add_urls/get_url_info</a></li>
<li><a href="#add_urls_add_url">POST /add_urls/add_url</a></li>
<li><a href="#add_urls_associate_url">POST /add_urls/associate_url</a></li>
</ul>
<h4><a href="#managing_cookies">Managing Cookies and HTTP Headers</a></h4>
<ul>
<li><a href="#manage_cookies_get_cookies">GET /manage_cookies/get_cookies</a></li>
<li><a href="#manage_cookies_set_cookies">POST /manage_cookies/set_cookies</a></li>
<li><a href="#manage_headers_set_user_agent">POST /manage_headers/set_user_agent</a></li>
</ul>
<h4><a href="#managing_pages">Managing Pages</a></h4>
<ul>
<li><a href="#manage_pages_get_pages">GET /manage_pages/get_pages</a></li>
<li><a href="#manage_pages_get_page_info">GET /manage_pages/get_page_info</a></li>
<li><a href="#manage_pages_add_files">POST /manage_pages/add_files</a></li>
<li><a href="#manage_pages_focus_page">POST /manage_pages/focus_page</a></li>
</ul>
<h4><a href="#searching_files">Searching and Fetching Files</a></h4>
<ul>
<li><a href="#get_files_search_files">GET /get_files/search_files</a></li>
<li><a href="#get_files_file_metadata">GET /get_files/file_metadata</a></li>
<li><a href="#get_files_file">GET /get_files/file</a></li>
<li><a href="#get_files_thumbnail">GET /get_files/thumbnail</a></li>
</ul>
<h4><a href="#managing_database">Managing the Database</a></h4>
<ul>
<li><a href="#manage_database_lock_on">POST /manage_database/lock_on</a></li>
<li><a href="#manage_database_lock_off">POST /manage_database/lock_off</a></li>
</ul>
</ul>
<h3 id="access_management"><a href="#access_management">Access Management</a></h3>
<div class="apiborder">
<h3 id="api_version"><a href="#api_version"><b>GET /api_version</b></a></h3>
<p><i>Gets the current API version. I will increment this every time I alter the API.</i></p>
<ul>
<li><p>Restricted access: NO.</p></li>
<li><p>Required Headers: n/a</p></li>
<li><p>Arguments: n/a</p></li>
<li><p>Response description: Some simple JSON describing the current api version (and hydrus client version, if you are interested).</p></li>
<li>
<p>Example response:</p>
<ul>
<li>
<pre>{
"version" : 17,
"hydrus_version" : 441
}</pre></li>
</ul>
</li>
</ul>
</div>
<div class="apiborder">
<h3 id="request_new_permissions"><a href="#request_new_permissions"><b>GET /request_new_permissions</b></a></h3>
<p><i>Register a new external program with the client. This requires the 'add from api request' mini-dialog under </i>services->review services<i> to be open, otherwise it will 403.</i></p>
<ul>
<li><p>Restricted access: NO.</p></li>
<li><p>Required Headers: n/a</p></li>
<li>
<p>Arguments:</p>
<ul>
<li>name : (descriptive name of your access)</li>
<li>basic_permissions : A JSON-encoded list of numerical permission identifiers you want to request.</li>
</ul>
</li>
<li>
<p>The permissions are currently:</p>
<ul>
<li>0 - Import URLs</li>
<li>1 - Import Files</li>
<li>2 - Add Tags</li>
<li>3 - Search for Files</li>
<li>4 - Manage Pages</li>
<li>5 - Manage Cookies</li>
<li>6 - Manage Database</li>
</ul>
</li>
<li>
<p>Example request:</p>
<ul>
<li><p>/request_new_permissions?name=my%20import%20script&basic_permissions=[0,1]</p></li>
</ul>
</li>
<li><p>Response description: Some JSON with your access key, which is 64 characters of hex. This will not be valid until the user approves the request in the client ui.</p></li>
<li>
<p>Example response:</p>
<ul>
<li><pre>{"access_key" : "73c9ab12751dcf3368f028d3abbe1d8e2a3a48d0de25e64f3a8f00f3a1424c57"}</pre></li>
</ul>
</li>
</ul>
</div>
<div class="apiborder">
<h3 id="session_key"><a href="#session_key"><b>GET /session_key</b></a></h3>
<p><i>Get a new session key.</i></p>
<ul>
<li><p>Restricted access: YES. No permissions required.</p></li>
<li><p>Required Headers: n/a</p></li>
<li><p>Arguments: n/a</p></li>
<li><p>Response description: Some JSON with a new session key in hex.</p></li>
<li>
<p>Example response:</p>
<ul>
<li>
<pre>{"session_key" : "f6e651e7467255ade6f7c66050f3d595ff06d6f3d3693a3a6fb1a9c2b278f800"}</pre>
</li>
</ul>
</li>
<li>
<p>Note that the access you provide to get a new session key <b>can</b> be a session key, if that happens to be useful. As long as you have some kind of access, you can generate a new session key.</p>
<p>A session key expires after 24 hours of inactivity, whenever the client restarts, or if the underlying access key is deleted. A request on an expired session key returns 419.</p>
</li>
</ul>
</div>
<div class="apiborder">
<h3 id="verify_access_key"><a href="#verify_access_key"><b>GET /verify_access_key</b></a></h3>
<p><i>Check your access key is valid.</i></p>
<ul>
<li><p>Restricted access: YES. No permissions required.</p></li>
<li><p>Required Headers: n/a</p></li>
<li><p>Arguments: n/a</p></li>
<li><p>Response description: 401/403/419 and some error text if the provided access/session key is invalid, otherwise some JSON with basic permission info.</p></li>
<li>
<p>Example response:</p>
<ul>
<li>
<pre>{
"basic_permissions" : [0, 1, 3],
"human_description" : "API Permissions (autotagger): add tags to files, import files, search for files: Can search: only autotag this"
}</pre>
</li>
</ul>
</li>
</ul>
</div>
<div class="apiborder">
<h3 id="get_services"><a href="#get_services"><b>GET /get_services</b></a></h3>
<p><i>Ask the client about its file and tag services.</i></p>
<ul>
<li><p>Restricted access: YES. At least one of Add Files, Add Tags, Manage Pages, or Search Files permission needed.</p></li>
<li><p>Required Headers: n/a</p></li>
<li><p>Arguments: n/a</p></li>
<li>
<p>Response description: Some JSON listing the client's file and tag services by name and 'service key'.</p>
</li>
<li>
<p>Example response:</p>
<ul>
<li>
<pre>{
'local_tags': [
{
'name': 'my tags',
'service_key': '6c6f63616c2074616773'
},
{
'name': 'filenames',
'service_key': '231a2e992b67101318c410abb6e7d98b6e32050623f138ca93bd4ad2993de31b'
}
],
'tag_repositories': [
{
'name': 'PTR',
'service_key': 'ccb0cf2f9e92c2eb5bd40986f72a339ef9497014a5fb8ce4cea6d6c9837877d9'
}
],
'local_files': [
{
'name': 'my files',
'service_key': '6c6f63616c2066696c6573'
}
],
'file_repositories': [
],
'all_local_files': [
{
'name': 'all local files',
'service_key': '616c6c206c6f63616c2066696c6573'
}
],
'all_known_files': [
{
'name': 'all known files',
'service_key': '616c6c206b6e6f776e2066696c6573'
}
],
'all_known_tags': [
{
'name': 'all known tags',
'service_key': '616c6c206b6e6f776e2074616773'
}
],
'trash': [
{
'name': 'trash',
'service_key': '7472617368'
}
]
}</pre>
</li>
</ul>
<p>These services may be referred to in various metadata responses or required in request parameters (e.g. where to add tag mappings). Note that a user can rename their services. Much of this Client API uses this renameable 'service name' as service identifier, but I may start using service key, which is non-mutable ID specific to each client. The hardcoded services have shorter service key strings (it is usually just 'all known files' etc.. ASCII-converted to hex), but user-made stuff will have 64-character hex.</p>
</li>
</ul>
</div>
<h3 id="adding_files"><a href="#adding_files">Adding Files</a></h3>
<div class="apiborder">
<h3 id="add_files_add_file"><a href="#add_files_add_file"><b>POST /add_files/add_file</b></a></h3>
<p><i>Tell the client to import a file.</i></p>
<ul>
<li><p>Restricted access: YES. Import Files permission needed.</p></li>
<li>
<p>Required Headers:</p>
<ul>
<li>Content-Type : application/json (if sending path), application/octet-stream (if sending file)</li>
</ul>
</li>
<li><p>Arguments (in JSON):</p></li>
<blockquote>path : (the path you want to import)</blockquote>
<li>
<p>Example request body:</p>
<blockquote><pre>{"path" : "E:\\to_import\\ayanami.jpg"}</pre></blockquote>
</li>
<li><p>Arguments (as bytes): You can alternately just send the file's bytes as the POST body.</p></li>
<li><p>Response description: Some JSON with the import result. Please note that file imports for large files may take several seconds, and longer if the client is busy doing other db work, so make sure your request is willing to wait that long for the response.</p></li>
<li>
<p>Example response:</p>
<pre>{
"status" : 1,
"hash" : "29a15ad0c035c0a0e86e2591660207db64b10777ced76565a695102a481c3dd1",
"note" : ""
}</pre>
<p>'status' is:</p>
<ul>
<li>1 - File was successfully imported</li>
<li>2 - File already in database</li>
<li>3 - File previously deleted</li>
<li>4 - File failed to import</li>
<li>7 - File vetoed</li>
</ul>
<p>A file 'veto' is caused by the file import options (which in this case is the 'quiet' set under the client's <i>options->importing</i>) stopping the file due to its resolution or minimum file size rules, etc...</p>
<p>'hash' is the file's SHA256 hash in hexadecimal, and 'note' is some occasional additional human-readable text appropriate to the file status that you may recognise from hydrus's normal import workflow. For an import error, it will always be the full traceback.</p>
</li>
</ul>
</div>
<div class="apiborder">
<h3 id="add_files_delete_files"><a href="#add_files_delete_files"><b>POST /add_files/delete_files</b></a></h3>
<p><i>Tell the client to send files to the trash.</i></p>
<ul>
<li><p>Restricted access: YES. Import Files permission needed.</p></li>
<li>
<p>Required Headers:</p>
<ul>
<li>Content-Type : application/json</li>
</ul>
</li>
<li><p>Arguments (in JSON):</p></li>
<ul>
<li>hash : (an SHA256 hash for a file in 64 characters of hexadecimal)</li>
<li>hashes : (a list of SHA256 hashes)</li>
</ul>
<li>
<p>Example request body:</p>
<blockquote><pre>{"hash" : "78f92ba4a786225ee2a1236efa6b7dc81dd729faf4af99f96f3e20bad6d8b538"}</pre></blockquote>
</li>
<li><p>Response description: 200 and no content.</p></li>
<li>
<p>You can use hash or hashes, whichever is more convenient.</p>
<p>At the moment, this is only able to send files from 'my files' to the trash, and so it cannot perform physical deletes. There is no error if any files do not currently exist in 'my files'. In future, it will take some sort of file service parameter to do more.</p>
</li>
</ul>
</div>
<div class="apiborder">
<h3 id="add_files_undelete_files"><a href="#add_files_undelete_files"><b>POST /add_files/undelete_files</b></a></h3>
<p><i>Tell the client to pull files back out of the trash.</i></p>
<ul>
<li><p>Restricted access: YES. Import Files permission needed.</p></li>
<li>
<p>Required Headers:</p>
<ul>
<li>Content-Type : application/json</li>
</ul>
</li>
<li><p>Arguments (in JSON):</p></li>
<ul>
<li>hash : (an SHA256 hash for a file in 64 characters of hexadecimal)</li>
<li>hashes : (a list of SHA256 hashes)</li>
</ul>
<li>
<p>Example request body:</p>
<blockquote><pre>{"hash" : "78f92ba4a786225ee2a1236efa6b7dc81dd729faf4af99f96f3e20bad6d8b538"}</pre></blockquote>
</li>
<li><p>Response description: 200 and no content.</p></li>
<li>
<p>You can use hash or hashes, whichever is more convenient.</p>
<p>This is just the reverse of a delete_files--removing files from trash and putting them back in 'my files'. There is no error if any files do not currently exist in 'trash'.</p>
</li>
</ul>
</div>
<div class="apiborder">
<h3 id="add_files_archive_files"><a href="#add_files_archive_files"><b>POST /add_files/archive_files</b></a></h3>
<p><i>Tell the client to archive inboxed files.</i></p>
<ul>
<li><p>Restricted access: YES. Import Files permission needed.</p></li>
<li>
<p>Required Headers:</p>
<ul>
<li>Content-Type : application/json</li>
</ul>
</li>
<li><p>Arguments (in JSON):</p></li>
<ul>
<li>hash : (an SHA256 hash for a file in 64 characters of hexadecimal)</li>
<li>hashes : (a list of SHA256 hashes)</li>
</ul>
<li>
<p>Example request body:</p>
<blockquote><pre>{"hash" : "78f92ba4a786225ee2a1236efa6b7dc81dd729faf4af99f96f3e20bad6d8b538"}</pre></blockquote>
</li>
<li><p>Response description: 200 and no content.</p></li>
<li>
<p>You can use hash or hashes, whichever is more convenient.</p>
<p>This puts files in the 'archive', taking them out of the inbox. It only has meaning for files currently in 'my files' or 'trash'. There is no error if any files do not currently exist or are already in the archive.</p>
</li>
</ul>
</div>
<div class="apiborder">
<h3 id="add_files_unarchive_files"><a href="#add_files_unarchive_files"><b>POST /add_files/unarchive_files</b></a></h3>
<p><i>Tell the client re-inbox archived files.</i></p>
<ul>
<li><p>Restricted access: YES. Import Files permission needed.</p></li>
<li>
<p>Required Headers:</p>
<ul>
<li>Content-Type : application/json</li>
</ul>
</li>
<li><p>Arguments (in JSON):</p></li>
<ul>
<li>hash : (an SHA256 hash for a file in 64 characters of hexadecimal)</li>
<li>hashes : (a list of SHA256 hashes)</li>
</ul>
<li>
<p>Example request body:</p>
<blockquote><pre>{"hash" : "78f92ba4a786225ee2a1236efa6b7dc81dd729faf4af99f96f3e20bad6d8b538"}</pre></blockquote>
</li>
<li><p>Response description: 200 and no content.</p></li>
<li>
<p>You can use hash or hashes, whichever is more convenient.</p>
<p>This puts files back in the inbox, taking them out of the archive. It only has meaning for files currently in 'my files' or 'trash'. There is no error if any files do not currently exist or are already in the inbox.</p>
</li>
</ul>
</div>
<h3 id="adding_tags"><a href="#adding_tags">Adding Tags</a></h3>
<div class="apiborder">
<h3 id="add_tags_clean_tags"><a href="#add_tags_clean_tags"><b>GET /add_tags/clean_tags</b></a></h3>
<p><i>Ask the client about how it will see certain tags.</i></p>
<ul>
<li><p>Restricted access: YES. Add Tags permission needed.</p></li>
<li><p>Required Headers: n/a</p></li>
<li><p>Arguments (in percent-encoded JSON):</p></li>
<ul>
<li>tags : (a list of the tags you want cleaned)</li>
</ul>
<li>
<p>Example request:</p>
<pre>Given tags [ " bikini ", "blue eyes", " character : samus aran ", ":)", " ", "", "10", "11", "9", "system:wew", "-flower" ]:</pre>
<ul>
<li><p>/add_tags/clean_tags?tags=%5B%22%20bikini%20%22%2C%20%22blue%20%20%20%20eyes%22%2C%20%22%20character%20%3A%20samus%20aran%20%22%2C%20%22%3A%29%22%2C%20%22%20%20%20%22%2C%20%22%22%2C%20%2210%22%2C%20%2211%22%2C%20%229%22%2C%20%22system%3Awew%22%2C%20%22-flower%22%5D</p></li>
</ul>
</li>
<li>
<p>Response description: The tags cleaned according to hydrus rules. They will also be in hydrus human-friendly sorting order.</p>
</li>
<li>
<p>Example response:</p>
<ul>
<li>
<pre>{
"tags" : [ "9", "10", "11", "::)", "bikini", "blue eyes", "character:samus aran", "flower", "wew" ]
}</pre>
</li>
</ul>
<p>Mostly, hydrus simply trims excess whitespace, but the other examples are rare issues you might run into. 'system' is an invalid namespace, tags cannot be prefixed with hyphens, and any tag starting with ':' is secretly dealt with internally as "[no namespace]:[colon-prefixed-subtag]". Again, you probably won't run into these, but if you see a mismatch somewhere and want to figure it out, or just want to sort some numbered tags, you might like to try this.</p>
</li>
</ul>
</div>
<div class="apiborder">
<h3 id="add_tags_get_tag_services"><a href="#add_tags_get_tag_services"><b>GET /add_tags/get_tag_services</b></a></h3>
<p class="warning"><b>This is becoming obsolete and will be removed! Use <a href="#get_services">/get_services</a> instead!</b></p>
<p><i>Ask the client about its tag services.</i></p>
<ul>
<li><p>Restricted access: YES. Add Tags permission needed.</p></li>
<li><p>Required Headers: n/a</p></li>
<li><p>Arguments: n/a</p></li>
<li>
<p>Response description: Some JSON listing the client's 'local tags' and tag repository services by name.</p>
</li>
<li>
<p>Example response:</p>
<ul>
<li>
<pre>{
"local_tags" : [ "my tags" ]
"tag_repositories" : [ "public tag repository", "mlp fanfic tagging server" ]
}</pre>
</li>
</ul>
<p>Note that a user can rename their services. Don't assume the client's local tags service will be "my tags".</p>
</li>
</ul>
</div>
<div class="apiborder">
<h3 id="add_tags_add_tags"><a href="#add_tags_add_tags"><b>POST /add_tags/add_tags</b></a></h3>
<p><i>Make changes to the tags that files have.</i></p>
<ul>
<li><p>Restricted access: YES. Add Tags permission needed.</p></li>
<li><p>Required Headers: n/a</p></li>
<li><p>Arguments (in JSON):</p></li>
<ul>
<li>hash : (selective A, an SHA256 hash for a file in 64 characters of hexadecimal)</li>
<li>hashes : (selective A, a list of SHA256 hashes)</li>
<li>service_names_to_tags : (selective B, an Object of service names to lists of tags to be 'added' to the files)</li>
<li>service_keys_to_tags : (selective B, an Object of service keys to lists of tags to be 'added' to the files)</li>
<li>service_names_to_actions_to_tags : (selective B, an Object of service names to content update actions to lists of tags)</li>
<li>service_keys_to_actions_to_tags : (selective B, an Object of service keys to content update actions to lists of tags)</li>
</ul>
<p>You can use either 'hash' or 'hashes'.</p>
<p>You can use either 'service_names_to...' or 'service_keys_to...', where names is simple and human-friendly "my tags" and similar (but may be renamed by a user), but keys is a little more complicated but accurate/unique. Since a client may have multiple tag services with non-default names and pseudo-random keys, if it is not your client you will need to check the <a href="#get_services">/get_services</a> call to get the names or keys, and you may need some selection UI on your end so the user can pick what to do if there are multiple choices. I encourage using keys if you can.</p>
<p>Also, you can use either '...to_tags', which is simple and add-only, or '...to_actions_to_tags', which is more complicated and allows you to remove/petition or rescind pending content.</p>
<p>The permitted 'actions' are:</p>
<ul>
<li>0 - Add to a local tag service.</li>
<li>1 - Delete from a local tag service.</li>
<li>2 - Pend to a tag repository.</li>
<li>3 - Rescind a pend from a tag repository.</li>
<li>4 - Petition from a tag repository. (This is special)</li>
<li>5 - Rescind a petition from a tag repository.</li>
</ul>
<p>When you petition a tag from a repository, a 'reason' for the petition is typically needed. If you send a normal list of tags here, a default reason of "Petitioned from API" will be given. If you want to set your own reason, you can instead give a list of [ tag, reason ] pairs.</p>
<p>Some example requests:</p>
<p>Adding some tags to a file:</p>
<pre>{
"hash" : "df2a7b286d21329fc496e3aa8b8a08b67bb1747ca32749acb3f5d544cbfc0f56",
"service_names_to_tags" : {
"my tags" : [ "character:supergirl", "rating:safe" ]
}
}</pre>
<p>Adding more tags to two files:</p>
<pre>{
"hashes" : [ "df2a7b286d21329fc496e3aa8b8a08b67bb1747ca32749acb3f5d544cbfc0f56", "f2b022214e711e9a11e2fcec71bfd524f10f0be40c250737a7861a5ddd3faebf" ],
"service_names_to_tags" : {
"my tags" : [ "process this" ],
"public tag repository" : [ "creator:dandon fuga" ]
}
}</pre>
<p>A complicated transaction with all possible actions:</p>
<pre>{
"hash" : "df2a7b286d21329fc496e3aa8b8a08b67bb1747ca32749acb3f5d544cbfc0f56",
"service_keys_to_actions_to_tags" : {
"6c6f63616c2074616773" : {
"0" : [ "character:supergirl", "rating:safe" ],
"1" : [ "character:superman" ]
},
"aa0424b501237041dab0308c02c35454d377eebd74cfbc5b9d7b3e16cc2193e9" : {
"2" : [ "character:supergirl", "rating:safe" ],
"3" : [ "filename:image.jpg" ],
"4" : [ [ "creator:danban faga", "typo" ], [ "character:super_girl", "underscore" ] ]
"5" : [ "skirt" ]
}
}
}</pre>
<p>This last example is far more complicated than you will usually see. Pend rescinds and petition rescinds are not common. Petitions are also quite rare, and gathering a good petition reason for each tag is often a pain.</p>
<p>Note that the enumerated status keys in the service_names_to_actions_to_tags structure are strings, not ints (JSON does not support int keys for Objects).</p>
<p>Response description: 200 and no content.</p>
<p>Note also that hydrus tag actions are safely idempotent. You can pend a tag that is already pended and not worry about an error--it will be discarded. The same for other reasonable logical scenarios: deleting a tag that does not exist will silently make no change, pending a tag that is already 'current' will again be passed over. It is fine to just throw 'process this' tags at every file import you add and not have to worry about checking which files you already added it to.</p>
</ul>
</div>
<h3 id="adding_urls"><a href="#adding_urls">Adding URLs</a></h3>
<div class="apiborder">
<h3 id="add_urls_get_url_files"><a href="#add_urls_get_url_files"><b>GET /add_urls/get_url_files</b></a></h3>
<p><i>Ask the client about an URL's files.</i></p>
<ul>
<li><p>Restricted access: YES. Import URLs permission needed.</p></li>
<li><p>Required Headers: n/a</p></li>
<li>
<p>Arguments:</p>
<ul>
<li>url : (the url you want to ask about)</li>
</ul>
</li>
<li>
<p>Example request (for URL http://safebooru.org/index.php?page=post&s=view&id=2753608):</p>
<ul>
<li><p>/add_urls/get_url_files?url=http%3A%2F%2Fsafebooru.org%2Findex.php%3Fpage%3Dpost%26s%3Dview%26id%3D2753608</p></li>
</ul>
</li>
<li>
<p>Response description: Some JSON which files are known to be mapped to that URL. Note this needs a database hit, so it may be delayed if the client is otherwise busy. Don't rely on this to always be fast.</p>
</li>
<li>
<p>Example response:</p>
<ul>
<li>
<pre>{
"normalised_url" : "https://safebooru.org/index.php?id=2753608&page=post&s=view"
"url_file_statuses" : [
{
"status" : 2
"hash" : "20e9002824e5e7ffc240b91b6e4a6af552b3143993c1778fd523c30d9fdde02c",
"note" : "url recognised: Imported at 2015/10/18 10:58:01, which was 3 years 4 months ago (before this check)."
}
]
}</pre>
</li>
</ul>
<p>The 'url_file_statuses' is a list of zero-to-n JSON Objects, each representing a file match the client found in its database for the URL. Typically, it will be of length 0 (for as-yet-unvisited URLs or Gallery/Watchable URLs that are not attached to files) or 1, but sometimes multiple files are given the same URL (sometimes by mistaken misattribution, sometimes by design, such as pixiv manga pages). Handling n files per URL is a pain but an unavoidable issue you should account for.</p>
<p>'status' is the same as for /add_files/add_file:</p>
<ul>
<li>0 - File not in database, ready for import (you will only see this very rarely--usually in this case you will just get no matches)</li>
<li>2 - File already in database</li>
<li>3 - File previously deleted</li>
</ul>
<p>'hash' is the file's SHA256 hash in hexadecimal, and 'note' is some occasional additional human-readable text you may recognise from hydrus's normal import workflow.</p>
</li>
</ul>
</div>
<div class="apiborder">
<h3 id="add_urls_get_url_info"><a href="#add_urls_get_url_info"><b>GET /add_urls/get_url_info</b></a></h3>
<p><i>Ask the client for information about a URL.</i></p>
<ul>
<li><p>Restricted access: YES. Import URLs permission needed.</p></li>
<li><p>Required Headers: n/a</p></li>
<li>
<p>Arguments:</p>
<ul>
<li>url : (the url you want to ask about)</li>
</ul>
</li>
<li>
<p>Example request (for URL https://8ch.net/tv/res/1846574.html):</p>
<ul>
<li><p>/add_urls/get_url_info?url=https%3A%2F%2F8ch.net%2Ftv%2Fres%2F1846574.html</p></li>
</ul>
</li>
<li>
<p>Response description: Some JSON describing what the client thinks of the URL.</p>
</li>
<li>
<p>Example response:</p>
<ul>
<li>
<pre>{
"normalised_url" : "https://8ch.net/tv/res/1846574.html",
"url_type" : 4,
"url_type_string" : "watchable url",
"match_name" : "8chan thread",
"can_parse" : true,
}</pre>
</li>
</ul>
<p>The url types are currently:</p>
<ul>
<li>0 - Post URL</li>
<li>2 - File URL</li>
<li>3 - Gallery URL</li>
<li>4 - Watchable URL</li>
<li>5 - Unknown URL (i.e. no matching URL Class)</li>
</ul>
<p>'Unknown' URLs are treated in the client as direct File URLs. Even though the 'File URL' type is available, most file urls do not have a URL Class, so they will appear as Unknown. Adding them to the client will pass them to the URL Downloader as a raw file for download and import.</p>
</li>
</ul>
</div>
<div class="apiborder">
<h3 id="add_urls_add_url"><a href="#add_urls_add_url"><b>POST /add_urls/add_url</b></a></h3>
<p><i>Tell the client to 'import' a URL. This triggers the exact same routine as drag-and-dropping a text URL onto the main client window.</i></p>
<ul>
<li><p>Restricted access: YES. Import URLs permission needed. Add Tags needed to include tags.</p></li>
<li>
<p>Required Headers:</p>
<ul>
<li>Content-Type : application/json</li>
</ul>
</li>
<li>
<p>Arguments (in JSON):</p>
<ul>
<li>url : (the url you want to add)</li>
<li>destination_page_key : (optional page identifier for the page to receive the url)</li>
<li>destination_page_name : (optional page name to receive the url)</li>
<li>show_destination_page : (optional, defaulting to false, controls whether the UI will change pages on add)</li>
<li>service_names_to_additional_tags : (optional, selective, tags to give to any files imported from this url)</li>
<li>service_keys_to_additional_tags : (optional, selective, tags to give to any files imported from this url)</li>
<li>filterable_tags : (optional tags to be filtered by any tag import options that applies to the URL)</li>
<li><i>service_names_to_tags : (obsolete, legacy synonym for service_names_to_additional_tags)</i></li>
</ul>
</li>
<p>If you specify a destination_page_name and an appropriate importer page already exists with that name, that page will be used. Otherwise, a new page with that name will be recreated (and used by subsequent calls with that name). Make sure it that page name is unique (e.g. '/b/ threads', not 'watcher') in your client, or it may not be found.</p>
<p>Alternately, destination_page_key defines exactly which page should be used. Bear in mind this page key is only valid to the current session (they are regenerated on client reset or session reload), so you must figure out which one you want using the <a href="#manage_pages_get_pages">/manage_pages/get_pages</a> call. If the correct page_key is not found, or the page it corresponds to is of the incorrect type, the standard page selection/creation rules will apply.</p>
<p>show_destination_page defaults to False to reduce flicker when adding many URLs to different pages quickly. If you turn it on, the client will behave like a URL drag and drop and select the final page the URL ends up on.</p>
<p>service_names_to_additional_tags and service_keys_to_additional_tags use the same data structure as in /add_tags/add_tags--service ids to a list of tags to add. You will need 'add tags' permission or this will 403. These tags work exactly as 'additional' tags work in a <i>tag import options</i>. They are service specific, and always added unless some advanced tag import options checkbox (like 'only add tags to new files') is set.</p>
<p>filterable_tags works like the tags parsed by a hydrus downloader. It is just a list of strings. They have no inherant service and will be sent to a <i>tag import options</i>, if one exists, to decide which tag services get what. This parameter is useful if you are pulling all a URL's tags outside of hydrus and want to have them processed like any other downloader, rather than figuring out service names and namespace filtering on your end. Note that in order for a tag import options to kick in, I think you will have to have a Post URL URL Class hydrus-side set up for the URL so some tag import options (whether that is Class-specific or just the default) can be loaded at import time.</p>
<li>
<p>Example request bodies:</p>
<ul>
<li>
<pre>{
"url" : "https://8ch.net/tv/res/1846574.html",
"destination_page_name" : "kino zone",
"service_names_to_additional_tags" : {
"my tags" : [ "as seen on /tv/" ]
}
}</pre>
</li>
<li>
<pre>{
"url" : "https://safebooru.org/index.php?page=post&s=view&id=3195917"
"filterable_tags" : [
"1girl",
"artist name",
"creator:azto dio",
"blonde hair",
"blue eyes",
"breasts",
"character name",
"commentary",
"english commentary",
"formal",
"full body",
"glasses",
"gloves",
"hair between eyes",
"high heels",
"highres",
"large breasts",
"long hair",
"long sleeves",
"looking at viewer",
"series:metroid",
"mole",
"mole under mouth",
"patreon username",
"ponytail",
"character:samus aran",
"solo",
"standing",
"suit",
"watermark"
]
}</pre>
</li>
</ul>
</li>
<li><p>Response description: Some JSON with info on the URL added.</p></li>
<li>
<p>Example response:</p>
<ul>
<li>
<pre>{
"human_result_text" : "\"https://8ch.net/tv/res/1846574.html\" URL added successfully.",
"normalised_url" : "https://8ch.net/tv/res/1846574.html"
}</pre>
</li>
</ul>
</li>
</ul>
</div>
<div class="apiborder">
<h3 id="add_urls_associate_url"><a href="#add_urls_associate_url"><b>POST /add_urls/associate_url</b></a></h3>
<p><i>Manage which URLs the client considers to be associated with which files.</i></p>
<ul>
<li><p>Restricted access: YES. Import URLs permission needed.</p></li>
<li>
<p>Required Headers:</p>
<ul>
<li>Content-Type : application/json</li>
</ul>
</li>
<li>
<p>Arguments (in JSON):</p>
<ul>
<li>url_to_add : (an url you want to associate with the file(s))</li>
<li>urls_to_add : (a list of urls you want to associate with the file(s))</li>
<li>url_to_delete : (an url you want to disassociate from the file(s))</li>
<li>urls_to_delete : (a list of urls you want to disassociate from the file(s))</li>
<li>hash : (an SHA256 hash for a file in 64 characters of hexadecimal)</li>
<li>hashes : (a list of SHA256 hashes)</li>
</ul>
</li>
<p>All of these are optional, but you obviously need to have at least one of 'url' arguments and one of the 'hash' arguments. The single/multiple arguments work the same--just use whatever is convenient for you. Unless you really know what you are doing with URL Classes, I strongly recommend you stick to associating URLs with just one single 'hash' at a time. Multiple hashes pointing to the same URL is unusual and frequently unhelpful.</p>
<li>
<p>Example request body:</p>
<ul>
<li>
<pre>{
"url_to_add" : "https://rule34.xxx/index.php?id=2588418&page=post&s=view",
"hash" : "3b820114f658d768550e4e3d4f1dced3ff8db77443472b5ad93700647ad2d3ba"
}</pre>
</li>
</ul>
</li>
<li><p>Response description: 200 with no content. Like when adding tags, this is safely idempotent--do not worry about re-adding URLs associations that already exist or accidentally trying to delete ones that don't.</p></li>
</ul>
</div>
<h3 id="managing_cookies"><a href="#managing_cookies">Managing Cookies and HTTP Headers</a></h3>
<p>This refers to the cookies held in the client's session manager, which are sent with network requests to different domains.</p>
<div class="apiborder">
<h3 id="manage_cookies_get_cookies"><a href="#manage_cookies_get_cookies"><b>GET /manage_cookies/get_cookies</b></a></h3>
<p><i>Get the cookies for a particular domain.</i></p>
<ul>
<li><p>Restricted access: YES. Manage Cookies permission needed.</p></li>
<li><p>Required Headers: n/a</p></li>
<li>
<p>Arguments: domain</p>
</li>
<li>
<p>Example request (for gelbooru.com):</p>
<ul>
<li><p>/manage_cookies/get_cookies?domain=gelbooru.com</p></li>
</ul>
</li>
<p>Response description: A JSON Object listing all the cookies for that domain in [ name, value, domain, path, expires ] format.</p>
<li>
<p>Example response:</p>
<ul>
<li>
<pre>{
"cookies" : [
[ "__cfduid", "f1bef65041e54e93110a883360bc7e71", ".gelbooru.com", "/", 1596223327 ],
[ "pass_hash", "0b0833b797f108e340b315bc5463c324", "gelbooru.com", "/", 1585855361 ],
[ "user_id", "123456", "gelbooru.com", "/", 1585855361 ]
]
}</pre>
</li>
</ul>
</li>
<p>Note that these variables are all strings except 'expires', which is either an integer timestamp or <i>null</i> for session cookies.</p>
<p>This request will also return any cookies for subdomains. The session system in hydrus generally stores cookies according to the second-level domain, so if you request for specific.someoverbooru.net, you will still get the cookies for someoverbooru.net and all its subdomains.</p>
</ul>
</div>
<div class="apiborder">
<h3 id="manage_cookies_set_cookies"><a href="#manage_cookies_set_cookies"><b>POST /manage_cookies/set_cookies</b></a></h3>
<p>Set some new cookies for the client. This makes it easier to 'copy' a login from a web browser or similar to hydrus if hydrus's login system can't handle the site yet.</p>
<ul>
<li><p>Restricted access: YES. Manage Cookies permission needed.</p></li>
<li>
<p>Required Headers:</p>
<ul>
<li>Content-Type : application/json</li>
</ul>
</li>
<li>
<p>Arguments (in JSON):</p>
<ul>
<li>cookies : (a list of cookie rows in the same format as the GET request above)</li>
</ul>
</li>
<li>
<p>Example request body:</p>
<ul>
<li>
<pre>{
"cookies" : [
[ "PHPSESSID", "07669eb2a1a6e840e498bb6e0799f3fb", ".somesite.com", "/", 1627327719 ],
[ "tag_filter", "1", ".somesite.com", "/", 1627327719 ]
]
}</pre>
</li>
</ul>
</li>
<p>You can set 'value' to be null, which will clear any existing cookie with the corresponding name, domain, and path (acting essentially as a delete).</p>
<p>Expires can be null, but session cookies will time-out in hydrus after 60 minutes of non-use.</p>
</ul>
</div>
<div class="apiborder">
<h3 id="manage_headers_set_user_agent"><a href="#manage_headers_set_user_agent"><b>POST /manage_headers/set_user_agent</b></a></h3>
<p>This sets the 'Global' User-Agent for the client, as typically editable under <i>network->data->manage http headers</i>, for instance if you want hydrus to appear as a specific browser associated with some cookies.</p>
<ul>
<li><p>Restricted access: YES. Manage Cookies permission needed.</p></li>
<li>
<p>Required Headers:</p>
<ul>
<li>Content-Type : application/json</li>
</ul>
</li>
<li>
<p>Arguments (in JSON):</p>
<ul>
<li>user-agent : (a string)</li>
</ul>
</li>
<li>
<p>Example request body:</p>
<ul>
<li>
<pre>{
"user-agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0"
}</pre>
</li>
</ul>
</li>
<p>Send an empty string to reset the client back to the default User-Agent, which should be 'Mozilla/5.0 (compatible; Hydrus Client)'.</p>
</ul>
</div>
<h3 id="managing_pages"><a href="#managing_pages">Managing Pages</a></h3>
<p>This refers to the pages of the main client UI.</p>
<div class="apiborder">
<h3 id="manage_pages_get_pages"><a href="#manage_pages_get_pages"><b>GET /manage_pages/get_pages</b></a></h3>
<p><i>Get the page structure of the current UI session.</i></p>
<ul>
<li><p>Restricted access: YES. Manage Pages permission needed.</p></li>
<li><p>Required Headers: n/a</p></li>
<li>
<p>Arguments: n/a</p>
</li>
<p>Response description: A JSON Object of the top-level page 'notebook' (page of pages) detailing its basic information and current sub-pages. Page of pages beneath it will list their own sub-page lists.</p>
<li>
<p>Example response:</p>
<ul>
<li>
<pre>{
"pages" : {
"name" : "top pages notebook",
"page_key" : "3b28d8a59ec61834325eb6275d9df012860a1ecfd9e1246423059bc47fb6d5bd",
"page_type" : 10,
"selected" : true,
"pages" : [
{
"name" : "files",
"page_key" : "d436ff5109215199913705eb9a7669d8a6b67c52e41c3b42904db083255ca84d",
"page_type" : 6,
"selected" : false
},
{
"name" : "thread watcher",
"page_key" : "40887fa327edca01e1d69b533dddba4681b2c43e0b4ebee0576177852e8c32e7",
"page_type" : 9,
"selected" : false
},
{
"name" : "pages",
"page_key" : "2ee7fa4058e1e23f2bd9e915cdf9347ae90902a8622d6559ba019a83a785c4dc",
"page_type" : 10,
"selected" : true,
"pages" : [
{
"name" : "urls",
"page_key" : "9fe22cb760d9ee6de32575ed9f27b76b4c215179cf843d3f9044efeeca98411f",
"page_type" : 7,
"selected" : true
},
{
"name" : "files",
"page_key" : "2977d57fc9c588be783727bcd54225d577b44e8aa2f91e365a3eb3c3f580dc4e",
"page_type" : 6,
"selected" : false
}
]
}
]
}
}</pre>
</li>
</ul>
</li>
<p>The page types are as follows:</p>
<ul>
<li>1 - Gallery downloader</li>
<li>2 - Simple downloader</li>
<li>3 - Hard drive import</li>
<li>5 - Petitions (used by repository janitors)</li>
<li>6 - File search</li>
<li>7 - URL downloader</li>
<li>8 - Duplicates</li>
<li>9 - Thread watcher</li>
<li>10 - Page of pages</li>
</ul>
<p>The top page of pages will always be there, and always selected. 'selected' means which page is currently in view and will propagate down other page of pages until it terminates. It may terminate in an empty page of pages, so do not assume it will end on a 'media' page.</p>
<p>The 'page_key' is a unique identifier for the page. It will stay the same for a particular page throughout the session, but new ones are generated on a client restart or other session reload.</p>
</ul>
</div>
<div class="apiborder">
<h3 id="manage_pages_get_page_info"><a href="#manage_pages_get_page_info"><b>GET /manage_pages/get_page_info</b></a></h3>
<p><i>Get information about a specific page.</i></p>
<p class="warning">This is under construction. The current call dumps a ton of info for different downloader pages. Please experiment in IRL situations and give feedback for now! I will flesh out this help with more enumeration info and examples as this gets nailed down. POST commands to alter pages (adding, removing, highlighting), will come later.</p>
<ul>
<li><p>Restricted access: YES. Manage Pages permission needed.</p></li>
<li><p>Required Headers: n/a</p></li>
<li>
<p>Arguments:</p>
<ul>
<li>page_key : (hexadecimal page_key as stated in <a href="#manage_pages_get_pages">/manage_pages/get_pages</a>)</li>
<li>simple : true or false (optional, defaulting to true)</li>
</ul>
</li>
<li>
<p>Example request:</p>
<ul>
<li><p>/manage_pages/get_page_info?page_key=aebbf4b594e6986bddf1eeb0b5846a1e6bc4e07088e517aff166f1aeb1c3c9da&simple=true</p></li>
</ul>
</li>