dsh-multimodal-node 0.0.0-stage → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +504 -2
- package/client.js +2817 -0
- package/cordis.patch.yml +4 -0
- package/icon.svg +9 -0
- package/index.js +960 -0
- package/lib/apikeys.js +250 -0
- package/lib/assets.js +234 -0
- package/lib/auth.js +315 -0
- package/lib/blender.js +644 -0
- package/lib/chain.js +161 -0
- package/lib/comfy.js +204 -0
- package/lib/config.js +377 -0
- package/lib/gateway.js +974 -0
- package/lib/http.js +119 -0
- package/lib/imagesize.js +92 -0
- package/lib/instances.js +554 -0
- package/lib/library.js +1209 -0
- package/lib/models.js +322 -0
- package/lib/nodeapi.js +491 -0
- package/lib/nodeclient.js +143 -0
- package/lib/pages.js +5788 -0
- package/lib/pool.js +591 -0
- package/lib/promptassist.js +1077 -0
- package/lib/promptlibrary.js +92 -0
- package/lib/remote.js +3348 -0
- package/lib/runindex.js +126 -0
- package/lib/seed.js +118 -0
- package/lib/tagdata.json +1 -0
- package/lib/tiers.js +283 -0
- package/lib/tools.js +230 -0
- package/lib/viewport.js +134 -0
- package/lib/workflows.js +472 -0
- package/lib/ws.js +106 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +62 -4
package/lib/runindex.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who owns what, and what survives a restart.
|
|
3
|
+
*
|
|
4
|
+
* The remote console is a **per-account** view, not a shared one: an account sees
|
|
5
|
+
* the workflows it created and the runs it started, nothing else. That is both a
|
|
6
|
+
* privacy rule (one person's prompts are not another's reading material) and the
|
|
7
|
+
* reason the tier question disappears from the asset list — an SFW account cannot
|
|
8
|
+
* create adult work in the first place, so there is nothing adult in its own list
|
|
9
|
+
* to hide.
|
|
10
|
+
*
|
|
11
|
+
* Surviving logout is a storage problem, not a session one: the pool holds runs
|
|
12
|
+
* in memory only, so an account's own history is written here as soon as a run is
|
|
13
|
+
* submitted and kept after the webserver, the pool, or DSH restarts. Files are
|
|
14
|
+
* addressed by name on the instance, which is how they are fetched again later.
|
|
15
|
+
*/
|
|
16
|
+
import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
|
|
19
|
+
/** Keep each account's history bounded; a year of runs is not a feature. */
|
|
20
|
+
const MAX_PER_ACCOUNT = 400;
|
|
21
|
+
|
|
22
|
+
export class RunIndex {
|
|
23
|
+
constructor(dataDir) {
|
|
24
|
+
this.file = join(dataDir, 'runs.json');
|
|
25
|
+
/** @type {Map<string, object[]>} owner -> runs, newest first */
|
|
26
|
+
this.byOwner = new Map();
|
|
27
|
+
/**
|
|
28
|
+
* Writes are serialised through one chain. Several runs settle in the same
|
|
29
|
+
* scheduling tick, and two concurrent rename() calls onto the same target
|
|
30
|
+
* are how a half-written index (or an ENOENT on the temp name) happens.
|
|
31
|
+
*/
|
|
32
|
+
this.writing = Promise.resolve();
|
|
33
|
+
this.counter = 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async init() {
|
|
37
|
+
await mkdir(join(this.file, '..'), { recursive: true });
|
|
38
|
+
try {
|
|
39
|
+
const raw = await readFile(this.file, 'utf8');
|
|
40
|
+
const parsed = JSON.parse(raw);
|
|
41
|
+
for (const [owner, runs] of Object.entries(parsed?.byOwner ?? {})) {
|
|
42
|
+
if (Array.isArray(runs)) this.byOwner.set(owner, runs);
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
await this.save();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Queue one durable write; resolves when this snapshot has landed. */
|
|
50
|
+
save() {
|
|
51
|
+
this.writing = this.writing
|
|
52
|
+
.catch(() => {})
|
|
53
|
+
.then(async () => {
|
|
54
|
+
this.counter += 1;
|
|
55
|
+
const temp = `${this.file}.${this.counter}.tmp`;
|
|
56
|
+
const plain = Object.fromEntries(this.byOwner);
|
|
57
|
+
await writeFile(temp, `${JSON.stringify({ byOwner: plain }, null, 2)}\n`, 'utf8');
|
|
58
|
+
await rename(temp, this.file);
|
|
59
|
+
})
|
|
60
|
+
.catch(() => {
|
|
61
|
+
// A failed write must not wedge the chain; the next call retries.
|
|
62
|
+
});
|
|
63
|
+
return this.writing;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Record a submitted run. Called before the pool has run anything. */
|
|
67
|
+
async submit(owner, record) {
|
|
68
|
+
const list = this.byOwner.get(owner) ?? [];
|
|
69
|
+
list.unshift({ ...record, owner, submittedAt: Date.now() });
|
|
70
|
+
if (list.length > MAX_PER_ACCOUNT) list.length = MAX_PER_ACCOUNT;
|
|
71
|
+
this.byOwner.set(owner, list);
|
|
72
|
+
await this.save();
|
|
73
|
+
return record;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Update one run when the pool knows how it ended (and which files it made). */
|
|
77
|
+
async settle(owner, promptId, patch) {
|
|
78
|
+
const list = this.byOwner.get(owner);
|
|
79
|
+
if (list === undefined) return undefined;
|
|
80
|
+
const index = list.findIndex((run) => run.promptId === promptId);
|
|
81
|
+
if (index < 0) return undefined;
|
|
82
|
+
list[index] = { ...list[index], ...patch };
|
|
83
|
+
await this.save();
|
|
84
|
+
return list[index];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** One account's runs, newest first. */
|
|
88
|
+
list(owner, limit = 60) {
|
|
89
|
+
return (this.byOwner.get(owner) ?? []).slice(0, limit);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Every owner with at least one recorded run.
|
|
94
|
+
*
|
|
95
|
+
* The asset sync needs this: its whole purpose is repairing rows the in-memory pool can
|
|
96
|
+
* no longer see, so it has to enumerate owners from disk rather than from whoever is
|
|
97
|
+
* logged in at the moment.
|
|
98
|
+
*/
|
|
99
|
+
owners() {
|
|
100
|
+
return [...this.byOwner.keys()];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
get(owner, promptId) {
|
|
104
|
+
return (this.byOwner.get(owner) ?? []).find((run) => run.promptId === promptId);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Does this account own this prompt id? The media route's only question. */
|
|
108
|
+
owns(owner, promptId) {
|
|
109
|
+
return this.get(owner, promptId) !== undefined;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async forget(owner, promptId) {
|
|
113
|
+
const list = this.byOwner.get(owner);
|
|
114
|
+
if (list === undefined) return { ok: false };
|
|
115
|
+
const next = list.filter((run) => run.promptId !== promptId);
|
|
116
|
+
this.byOwner.set(owner, next);
|
|
117
|
+
await this.save();
|
|
118
|
+
return { ok: next.length !== list.length };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Drop an account's whole index when the account is removed. */
|
|
122
|
+
async dropOwner(owner) {
|
|
123
|
+
this.byOwner.delete(owner);
|
|
124
|
+
await this.save();
|
|
125
|
+
}
|
|
126
|
+
}
|
package/lib/seed.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sampling seeds: one mode selector, one generator, one applier.
|
|
3
|
+
*
|
|
4
|
+
* Three facts shape this file.
|
|
5
|
+
*
|
|
6
|
+
* **A seed is not a normal parameter.** Every other knob in the console is "the same value
|
|
7
|
+
* for all N runs of this click"; a seed is the one that must be different on purpose.
|
|
8
|
+
* Running a batch with repeat=4 and one seed gives four identical pictures, which is the
|
|
9
|
+
* opposite of what "give me a few options" means. So a random seed is drawn *inside* the
|
|
10
|
+
* per-run loop, never once before it.
|
|
11
|
+
*
|
|
12
|
+
* **A ComfyUI graph can carry several seeds.** The common case is one sampler, but a
|
|
13
|
+
* two-pass workflow (base then refiner) has two, and a hires-fix graph may have three.
|
|
14
|
+
* Setting only the first one leaves the second pinned, which looks like "random is broken"
|
|
15
|
+
* because the pictures stay suspiciously similar. Every seed input is written.
|
|
16
|
+
*
|
|
17
|
+
* **The value has to stay in JS's exact-integer range.** 12 digits is 1e11–1e12, well under
|
|
18
|
+
* 2^53, so a plain Number round-trips exactly through JSON and through ComfyUI's own
|
|
19
|
+
* parser. Do not switch this to 64-bit values: they would silently lose precision on the
|
|
20
|
+
* way in, and the picture would be reproducible but not from the number shown.
|
|
21
|
+
*/
|
|
22
|
+
import { randomInt } from 'node:crypto';
|
|
23
|
+
|
|
24
|
+
/** The smallest and largest 12-digit integers, inclusive. */
|
|
25
|
+
export const SEED_MIN = 100_000_000_000;
|
|
26
|
+
export const SEED_MAX = 999_999_999_999;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Input names that hold a sampling seed.
|
|
30
|
+
*
|
|
31
|
+
* Matched on the input key rather than the class, because ComfyUI's core samplers say
|
|
32
|
+
* `seed` while several popular custom nodes say `noise_seed` or `rand_seed`. A class list
|
|
33
|
+
* would go stale the moment someone installs a new sampler.
|
|
34
|
+
*/
|
|
35
|
+
const SEED_INPUTS = /^(?:seed|noise_seed|rand_seed|random_seed)$/i;
|
|
36
|
+
|
|
37
|
+
/** Is this graph input a seed? Exported so the console and the tests agree with the server. */
|
|
38
|
+
export function isSeedInput(name) {
|
|
39
|
+
return SEED_INPUTS.test(String(name ?? ''));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** One 12-digit seed. Uniform over [SEED_MIN, SEED_MAX]. */
|
|
43
|
+
export function randomSeed() {
|
|
44
|
+
return randomInt(SEED_MIN, SEED_MAX + 1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Coerce anything a caller might send into an acceptable seed, or null when unusable. */
|
|
48
|
+
export function normalizeSeed(value) {
|
|
49
|
+
// A blank string is the one trap here: Number('') is 0, which is a *valid* seed and
|
|
50
|
+
// would therefore be accepted, silently turning "mode: fixed, value: ''" into seed 0
|
|
51
|
+
// instead of falling back to random. Check for emptiness before coercing.
|
|
52
|
+
if (value === null || value === undefined) return null;
|
|
53
|
+
if (typeof value === 'string' && value.trim() === '') return null;
|
|
54
|
+
const number = typeof value === 'number' ? value : Number(String(value).trim());
|
|
55
|
+
if (!Number.isFinite(number)) return null;
|
|
56
|
+
if (!Number.isSafeInteger(number) || number < 0) return null;
|
|
57
|
+
return number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Every seed input one graph declares.
|
|
62
|
+
*
|
|
63
|
+
* Returns `{nodeId, input, value}` rows so callers can both count them (does this workflow
|
|
64
|
+
* even have a seed?) and report which ones a run used.
|
|
65
|
+
*/
|
|
66
|
+
export function seedTargets(prompt) {
|
|
67
|
+
const rows = [];
|
|
68
|
+
for (const [nodeId, node] of Object.entries(prompt ?? {})) {
|
|
69
|
+
for (const [input, value] of Object.entries(node?.inputs ?? {})) {
|
|
70
|
+
if (!SEED_INPUTS.test(input)) continue;
|
|
71
|
+
if (typeof value !== 'number' && typeof value !== 'string') continue;
|
|
72
|
+
rows.push({ nodeId, input, value });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return rows;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Does this graph expose a seed at all? Drives whether the console shows the control. */
|
|
79
|
+
export function hasSeed(prompt) {
|
|
80
|
+
return seedTargets(prompt).length > 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Write one seed into every seed input of a graph. Mutates, because the caller already
|
|
85
|
+
* holds the deep copy that belongs to this single run.
|
|
86
|
+
*
|
|
87
|
+
* Returns the ids it wrote, so a reply can name them and a test can assert on them.
|
|
88
|
+
*/
|
|
89
|
+
export function applySeed(prompt, seed) {
|
|
90
|
+
const value = normalizeSeed(seed);
|
|
91
|
+
if (value === null) return [];
|
|
92
|
+
const written = [];
|
|
93
|
+
for (const target of seedTargets(prompt)) {
|
|
94
|
+
prompt[target.nodeId].inputs[target.input] = value;
|
|
95
|
+
written.push(target);
|
|
96
|
+
}
|
|
97
|
+
return written;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Resolve one run's seed from the request, drawing a fresh number when the mode is random.
|
|
102
|
+
*
|
|
103
|
+
* Called once per run, by design: `repeat` runs of one click must not share a seed. The
|
|
104
|
+
* fixed case is normalized here too, so an unusable value falls back to random rather than
|
|
105
|
+
* submitting `NaN` — a failed run is a worse answer than a different picture.
|
|
106
|
+
*/
|
|
107
|
+
export function seedForRun(seedRequest, fallbackSeed) {
|
|
108
|
+
const mode = seedRequest?.mode === 'fixed' ? 'fixed' : 'random';
|
|
109
|
+
if (mode === 'fixed') {
|
|
110
|
+
const fixed = normalizeSeed(seedRequest?.value);
|
|
111
|
+
if (fixed !== null) return { mode: 'fixed', seed: fixed };
|
|
112
|
+
// An unusable fixed value must still produce a usable number: the run is already
|
|
113
|
+
// committed to the queue, and `NaN` would fail it on the instance side.
|
|
114
|
+
const fallback = normalizeSeed(fallbackSeed);
|
|
115
|
+
return { mode: 'random', seed: fallback !== null ? fallback : randomSeed() };
|
|
116
|
+
}
|
|
117
|
+
return { mode: 'random', seed: randomSeed() };
|
|
118
|
+
}
|
package/lib/tagdata.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"t":"1girl","z":"单个女性","s":"主体","n":0},{"t":"1boy","z":"单个男性","s":"主体","n":0},{"t":"2girls","z":"两个女性","s":"主体","n":0},{"t":"solo","z":"单人无他人","s":"主体","n":0},{"t":"solo_focus","z":"单人为主体","s":"主体","n":0},{"t":"male_focus","z":"男性为主体","s":"主体","n":0},{"t":"long_hair","z":"长发","s":"头部与脸","n":0},{"t":"short_hair","z":"短发","s":"头部与脸","n":0},{"t":"very_long_hair","z":"超长发,过腰","s":"头部与脸","n":0},{"t":"medium_hair","z":"中长发","s":"头部与脸","n":0},{"t":"ponytail","z":"马尾","s":"头部与脸","n":0},{"t":"high_ponytail","z":"高马尾","s":"头部与脸","n":0},{"t":"side_ponytail","z":"侧马尾","s":"头部与脸","n":0},{"t":"low_twintails","z":"低位双马尾","s":"头部与脸","n":0},{"t":"braid","z":"辫子","s":"头部与脸","n":0},{"t":"twin_braids","z":"双辫","s":"头部与脸","n":0},{"t":"single_braid","z":"单辫","s":"头部与脸","n":0},{"t":"side_braid","z":"侧辫","s":"头部与脸","n":0},{"t":"hair_bun","z":"丸子头","s":"头部与脸","n":0},{"t":"double_bun","z":"双丸子头","s":"头部与脸","n":0},{"t":"ahoge","z":"呆毛","s":"头部与脸","n":0},{"t":"antenna_hair","z":"触角状发丝","s":"头部与脸","n":0},{"t":"blunt_bangs","z":"齐刘海","s":"头部与脸","n":0},{"t":"swept_bangs","z":"斜刘海","s":"头部与脸","n":0},{"t":"hair_between_eyes","z":"发丝垂在两眼之间","s":"头部与脸","n":0},{"t":"hair_over_one_eye","z":"遮住一只眼","s":"头部与脸","n":0},{"t":"hair_behind_ear","z":"头发别在耳后","s":"头部与脸","n":0},{"t":"hair_over_shoulder","z":"头发垂在肩前","s":"头部与脸","n":0},{"t":"straight_hair","z":"直发","s":"头部与脸","n":0},{"t":"wavy_hair","z":"波浪卷","s":"头部与脸","n":0},{"t":"curly_hair","z":"卷发","s":"头部与脸","n":0},{"t":"messy_hair","z":"凌乱发","s":"头部与脸","n":0},{"t":"floating_hair","z":"飘起的头发","s":"头部与脸","n":0},{"t":"hair_spread_out","z":"头发铺散开,躺姿常用","s":"头部与脸","n":0},{"t":"wet_hair","z":"湿发","s":"头部与脸","n":0},{"t":"black_hair","z":"黑发","s":"头部与脸","n":0},{"t":"blonde_hair","z":"金发","s":"头部与脸","n":0},{"t":"brown_hair","z":"棕发","s":"头部与脸","n":0},{"t":"white_hair","z":"白发","s":"头部与脸","n":0},{"t":"grey_hair","z":"灰发","s":"头部与脸","n":0},{"t":"blue_hair","z":"蓝发","s":"头部与脸","n":0},{"t":"red_hair","z":"红发","s":"头部与脸","n":0},{"t":"pink_hair","z":"粉发","s":"头部与脸","n":0},{"t":"purple_hair","z":"紫发","s":"头部与脸","n":0},{"t":"green_hair","z":"绿发","s":"头部与脸","n":0},{"t":"orange_hair","z":"橙发","s":"头部与脸","n":0},{"t":"multicolored_hair","z":"多色发","s":"头部与脸","n":0},{"t":"streaked_hair","z":"挑染","s":"头部与脸","n":0},{"t":"gradient_hair","z":"渐变发","s":"头部与脸","n":0},{"t":"two-tone_hair","z":"双色发","s":"头部与脸","n":0},{"t":"colored_inner_hair","z":"内层染发","s":"头部与脸","n":0},{"t":"blue_eyes","z":"蓝眼","s":"头部与脸","n":0},{"t":"red_eyes","z":"红眼","s":"头部与脸","n":0},{"t":"purple_eyes","z":"紫眼","s":"头部与脸","n":0},{"t":"green_eyes","z":"绿眼","s":"头部与脸","n":0},{"t":"brown_eyes","z":"棕眼","s":"头部与脸","n":0},{"t":"yellow_eyes","z":"黄眼","s":"头部与脸","n":0},{"t":"pink_eyes","z":"粉眼","s":"头部与脸","n":0},{"t":"black_eyes","z":"黑眼","s":"头部与脸","n":0},{"t":"grey_eyes","z":"灰眼","s":"头部与脸","n":0},{"t":"orange_eyes","z":"橙眼","s":"头部与脸","n":0},{"t":"aqua_eyes","z":"水蓝眼","s":"头部与脸","n":0},{"t":"white_eyes","z":"白眼,无瞳孔","s":"头部与脸","n":0},{"t":"multicolored_eyes","z":"异色瞳,单眼多色","s":"头部与脸","n":0},{"t":"heterochromia","z":"双眼不同色","s":"头部与脸","n":0},{"t":"closed_eyes","z":"闭眼","s":"头部与脸","n":0},{"t":"half-closed_eyes","z":"半睁眼","s":"头部与脸","n":0},{"t":"half-closed_eye","z":"单眼半睁","s":"头部与脸","n":0},{"t":"one_eye_closed","z":"睁一只闭一只","s":"头部与脸","n":0},{"t":"slit_pupils","z":"竖瞳","s":"头部与脸","n":0},{"t":"heart-shaped_pupils","z":"心形瞳","s":"头部与脸","n":0},{"t":"eyelashes","z":"睫毛","s":"头部与脸","n":0},{"t":"thick_eyelashes","z":"浓睫毛","s":"头部与脸","n":0},{"t":"eyeliner","z":"眼线","s":"头部与脸","n":0},{"t":"eyeshadow","z":"眼影","s":"头部与脸","n":0},{"t":"eyebrows","z":"眉毛","s":"头部与脸","n":0},{"t":"makeup","z":"化妆","s":"头部与脸","n":0},{"t":"lipstick","z":"口红","s":"头部与脸","n":0},{"t":"mole_under_eye","z":"眼下泪痣","s":"头部与脸","n":0},{"t":"mole_on_cheek","z":"脸颊痣","s":"头部与脸","n":0},{"t":"freckles","z":"雀斑","s":"头部与脸","n":0},{"t":"open_mouth","z":"张嘴","s":"头部与脸","n":0},{"t":"closed_mouth","z":"闭嘴","s":"头部与脸","n":0},{"t":"parted_lips","z":"微启唇","s":"头部与脸","n":0},{"t":"teeth","z":"露齿","s":"头部与脸","n":0},{"t":"sharp_teeth","z":"尖牙","s":"头部与脸","n":0},{"t":"fang","z":"单颗虎牙","s":"头部与脸","n":0},{"t":"fangs","z":"双虎牙","s":"头部与脸","n":0},{"t":"tongue","z":"舌","s":"头部与脸","n":0},{"t":"tongue_out","z":"吐舌","s":"头部与脸","n":0},{"t":"pout","z":"撅嘴","s":"头部与脸","n":0},{"t":"smile","z":"微笑","s":"头部与脸","n":0},{"t":"light_smile","z":"浅笑","s":"头部与脸","n":0},{"t":"grin","z":"咧嘴笑","s":"头部与脸","n":0},{"t":"seductive_smile","z":"诱惑笑","s":"头部与脸","n":0},{"t":"evil_smile","z":"坏笑","s":"头部与脸","n":0},{"t":"nervous_smile","z":"尴尬笑","s":"头部与脸","n":0},{"t":"expressionless","z":"无表情","s":"头部与脸","n":0},{"t":"frown","z":"皱眉","s":"头部与脸","n":0},{"t":"blush","z":"脸红","s":"头部与脸","n":0},{"t":"light_blush","z":"淡红","s":"头部与脸","n":0},{"t":"full-face_blush","z":"满脸通红","s":"头部与脸","n":0},{"t":"nose_blush","z":"鼻头红","s":"头部与脸","n":0},{"t":"body_blush","z":"身体泛红","s":"头部与脸","n":0},{"t":"blush_stickers","z":"脸红贴纸效果","s":"头部与脸","n":0},{"t":"tears","z":"泪","s":"头部与脸","n":0},{"t":"crying","z":"哭","s":"头部与脸","n":0},{"t":"crying_with_eyes_open","z":"睁眼流泪","s":"头部与脸","n":0},{"t":"sweatdrop","z":"汗滴","s":"头部与脸","n":0},{"t":"flying_sweatdrops","z":"飞溅汗滴","s":"头部与脸","n":0},{"t":"hair_ornament","z":"发饰","s":"头部与脸","n":0},{"t":"hair_ribbon","z":"发带","s":"头部与脸","n":0},{"t":"hair_bow","z":"蝴蝶结","s":"头部与脸","n":0},{"t":"hairband","z":"发箍","s":"头部与脸","n":0},{"t":"hairclip","z":"发夹","s":"头部与脸","n":0},{"t":"hair_flower","z":"发花","s":"头部与脸","n":0},{"t":"earrings","z":"耳环","s":"头部与脸","n":0},{"t":"ear_piercing","z":"耳洞","s":"头部与脸","n":0},{"t":"eyebrow_piercing","z":"眉钉","s":"头部与脸","n":0},{"t":"nose_piercing","z":"鼻钉","s":"头部与脸","n":0},{"t":"lip_piercing","z":"唇环","s":"头部与脸","n":0},{"t":"collarbone","z":"锁骨","s":"颈肩臂手","n":0},{"t":"bare_shoulders","z":"裸肩","s":"颈肩臂手","n":0},{"t":"bare_arms","z":"裸臂","s":"颈肩臂手","n":0},{"t":"armpits","z":"腋下","s":"颈肩臂手","n":0},{"t":"hand_on_own_neck","z":"手放自己脖子上","s":"颈肩臂手","n":0},{"t":"arm_up","z":"单臂上举","s":"颈肩臂手","n":0},{"t":"arms_up","z":"双臂上举","s":"颈肩臂手","n":0},{"t":"hand_up","z":"单手举起","s":"颈肩臂手","n":0},{"t":"hands_up","z":"双手举起","s":"颈肩臂手","n":0},{"t":"outstretched_arm","z":"单臂伸出","s":"颈肩臂手","n":0},{"t":"outstretched_arms","z":"双臂伸出","s":"颈肩臂手","n":0},{"t":"crossed_arms","z":"抱臂","s":"颈肩臂手","n":0},{"t":"arms_behind_back","z":"双手背后","s":"颈肩臂手","n":0},{"t":"arm_behind_back","z":"单手背后","s":"颈肩臂手","n":0},{"t":"spread_arms","z":"双臂张开","s":"颈肩臂手","n":0},{"t":"clenched_hand","z":"握拳","s":"颈肩臂手","n":0},{"t":"own_hands_together","z":"双手交握","s":"颈肩臂手","n":0},{"t":"hands_in_pockets","z":"双手插兜","s":"颈肩臂手","n":0},{"t":"hand_on_own_hip","z":"一手叉腰","s":"颈肩臂手","n":0},{"t":"hands_on_own_hips","z":"双手叉腰","s":"颈肩臂手","n":0},{"t":"hand_on_own_chest","z":"手放胸口","s":"颈肩臂手","n":0},{"t":"hand_on_own_face","z":"手摸脸","s":"颈肩臂手","n":0},{"t":"hands_on_own_cheeks","z":"双手捧脸","s":"颈肩臂手","n":0},{"t":"hand_on_own_thigh","z":"手放大腿","s":"颈肩臂手","n":0},{"t":"hand_on_own_stomach","z":"手放腹部","s":"颈肩臂手","n":0},{"t":"hand_between_own_legs","z":"手在双腿之间","s":"颈肩臂手","n":0},{"t":"fingernails","z":"指甲","s":"颈肩臂手","n":0},{"t":"nail_polish","z":"涂指甲油","s":"颈肩臂手","n":0},{"t":"red_nails","z":"红指甲","s":"颈肩臂手","n":0},{"t":"black_nails","z":"黑指甲","s":"颈肩臂手","n":0},{"t":"toenail_polish","z":"脚趾甲油","s":"颈肩臂手","n":0},{"t":"gloves","z":"手套","s":"颈肩臂手","n":0},{"t":"elbow_gloves","z":"长手套,过肘","s":"颈肩臂手","n":0},{"t":"fingerless_gloves","z":"露指手套","s":"颈肩臂手","n":0},{"t":"wrist_cuffs","z":"腕套","s":"颈肩臂手","n":0},{"t":"armband","z":"臂环","s":"颈肩臂手","n":0},{"t":"flat_chest","z":"平胸","s":"胸","n":1},{"t":"small_breasts","z":"小胸","s":"胸","n":1},{"t":"medium_breasts","z":"中等","s":"胸","n":1},{"t":"large_breasts","z":"大胸","s":"胸","n":1},{"t":"huge_breasts","z":"巨乳","s":"胸","n":1},{"t":"cleavage","z":"乳沟","s":"胸","n":1},{"t":"full_cleavage","z":"完整乳沟,大开口","s":"胸","n":1},{"t":"cleavage_cutout","z":"乳沟处的镂空开口","s":"胸","n":1},{"t":"sideboob","z":"侧乳","s":"胸","n":1},{"t":"underbust","z":"胸下围,托高效果","s":"胸","n":1},{"t":"breast_press","z":"胸部压在物体上","s":"胸","n":1},{"t":"between_breasts","z":"物体夹在双乳之间","s":"胸","n":1},{"t":"breasts_squeezed_together","z":"双乳挤在一起","s":"胸","n":1},{"t":"breasts_apart","z":"双乳分开","s":"胸","n":1},{"t":"bouncing_breasts","z":"晃动","s":"胸","n":1},{"t":"hanging_breasts","z":"自然下垂","s":"胸","n":1},{"t":"arm_under_breasts","z":"手臂托在胸下","s":"胸","n":1},{"t":"nipples","z":"乳头","s":"胸","n":1},{"t":"covered_nipples","z":"乳头被遮住","s":"胸","n":1},{"t":"puffy_nipples","z":"凸起乳头","s":"胸","n":1},{"t":"inverted_nipples","z":"凹陷乳头","s":"胸","n":1},{"t":"large_areolae","z":"大乳晕","s":"胸","n":1},{"t":"nipple_slip","z":"乳头走光","s":"胸","n":1},{"t":"areola_slip","z":"乳晕走光","s":"胸","n":1},{"t":"breasts_out","z":"乳房露出衣外","s":"胸","n":1},{"t":"navel","z":"肚脐","s":"腹与腰","n":0},{"t":"covered_navel","z":"肚脐被遮住","s":"腹与腰","n":0},{"t":"navel_cutout","z":"肚脐处镂空","s":"腹与腰","n":0},{"t":"navel_piercing","z":"脐钉","s":"腹与腰","n":0},{"t":"stomach","z":"腹部","s":"腹与腰","n":0},{"t":"midriff","z":"露腰,腹部裸露","s":"腹与腰","n":0},{"t":"midriff_peek","z":"露一点腰","s":"腹与腰","n":0},{"t":"abs","z":"腹肌线条","s":"腹与腰","n":0},{"t":"covered_abs","z":"腹肌被遮","s":"腹与腰","n":0},{"t":"belly","z":"肚子","s":"腹与腰","n":0},{"t":"big_belly","z":"大肚","s":"腹与腰","n":0},{"t":"skindentation","z":"皮肤被衣物勒出的压痕","s":"腹与腰","n":0},{"t":"narrow_waist","z":"细腰","s":"腹与腰","n":0},{"t":"wide_hips","z":"宽胯","s":"腹与腰","n":0},{"t":"waist_apron","z":"半身围裙,只围下半身","s":"腹与腰","n":1},{"t":"clothes_around_waist","z":"衣服系在腰上","s":"腹与腰","n":0},{"t":"jacket_around_waist","z":"外套系腰","s":"腹与腰","n":0},{"t":"belly_chain","z":"腰链","s":"腹与腰","n":0},{"t":"stomach_tattoo","z":"腹部纹身","s":"腹与腰","n":0},{"t":"back","z":"背","s":"躯干与背","n":0},{"t":"bare_back","z":"裸背","s":"躯干与背","n":0},{"t":"cropped_torso","z":"躯干特写,裁掉头脚","s":"躯干与背","n":0},{"t":"on_back","z":"仰躺","s":"躯干与背","n":0},{"t":"on_stomach","z":"俯卧","s":"躯干与背","n":0},{"t":"backless_outfit","z":"露背装","s":"躯干与背","n":0},{"t":"backless_dress","z":"露背裙","s":"躯干与背","n":0},{"t":"backless_shirt","z":"露背上衣","s":"躯干与背","n":0},{"t":"hip_vent","z":"胯部开衩","s":"躯干与背","n":0},{"t":"hip_focus","z":"焦点在胯部","s":"躯干与背","n":0},{"t":"dorsal_fin","z":"背部鳍状装饰(奇幻)","s":"躯干与背","n":0},{"t":"ass","z":"臀","s":"臀与胯","n":1},{"t":"huge_ass","z":"丰满大臀","s":"臀与胯","n":1},{"t":"ass_focus","z":"焦点在臀","s":"臀与胯","n":1},{"t":"butt_crack","z":"臀缝","s":"臀与胯","n":1},{"t":"hand_on_own_ass","z":"手放自己臀上","s":"臀与胯","n":1},{"t":"spreading_own_ass","z":"双手掰开臀","s":"臀与胯","n":1},{"t":"curvy_hips","z":"圆润胯部曲线","s":"臀与胯","n":1},{"t":"thighs","z":"大腿","s":"腿与大腿","n":0},{"t":"legs","z":"腿","s":"腿与大腿","n":0},{"t":"thick_thighs","z":"粗大腿,肉感","s":"腿与大腿","n":0},{"t":"bare_legs","z":"裸腿","s":"腿与大腿","n":0},{"t":"thigh_gap","z":"大腿缝","s":"腿与大腿","n":0},{"t":"between_legs","z":"双腿之间","s":"腿与大腿","n":0},{"t":"spread_legs","z":"张开双腿","s":"腿与大腿","n":1},{"t":"wide_spread_legs","z":"大幅张开","s":"腿与大腿","n":1},{"t":"legs_apart","z":"双腿分开","s":"腿与大腿","n":0},{"t":"legs_together","z":"双腿并拢","s":"腿与大腿","n":0},{"t":"crossed_legs","z":"交叠腿","s":"腿与大腿","n":0},{"t":"knees_up","z":"屈膝抬起","s":"腿与大腿","n":0},{"t":"legs_up","z":"双腿抬起","s":"腿与大腿","n":0},{"t":"leg_up","z":"单腿抬起","s":"腿与大腿","n":0},{"t":"knees_together_feet_apart","z":"膝并脚分","s":"腿与大腿","n":0},{"t":"standing_on_one_leg","z":"单腿站立","s":"腿与大腿","n":0},{"t":"thigh_strap","z":"大腿绑带","s":"腿与大腿","n":0},{"t":"thigh_boots","z":"过膝长靴","s":"腿与大腿","n":0},{"t":"knee_boots","z":"及膝靴","s":"腿与大腿","n":0},{"t":"kneehighs","z":"及膝袜","s":"腿与大腿","n":0},{"t":"thighband_pantyhose","z":"大腿处有环带的连裤袜","s":"腿与大腿","n":0},{"t":"shiny_skin","z":"皮肤反光","s":"腿与大腿","n":0},{"t":"shiny_clothes","z":"衣物反光","s":"腿与大腿","n":0},{"t":"barefoot","z":"赤脚","s":"脚","n":0},{"t":"feet","z":"脚","s":"脚","n":0},{"t":"toes","z":"脚趾","s":"脚","n":0},{"t":"soles","z":"脚底","s":"脚","n":0},{"t":"toenails","z":"脚趾甲","s":"脚","n":0},{"t":"spread_toes","z":"脚趾张开","s":"脚","n":0},{"t":"toe_scrunch","z":"脚趾蜷起","s":"脚","n":0},{"t":"ankle","z":"脚踝","s":"脚","n":0},{"t":"crossed_ankles","z":"脚踝交叉","s":"脚","n":0},{"t":"feet_up","z":"双脚抬起","s":"脚","n":0},{"t":"foot_focus","z":"焦点在脚","s":"脚","n":0},{"t":"foot_out_of_frame","z":"单脚出画","s":"脚","n":0},{"t":"feet_out_of_frame","z":"双脚出画","s":"脚","n":0},{"t":"tiptoes","z":"踮脚","s":"脚","n":0},{"t":"pigeon-toed","z":"内八","s":"脚","n":0},{"t":"high_heels","z":"高跟鞋","s":"脚","n":0},{"t":"high_heel_boots","z":"高跟靴","s":"脚","n":0},{"t":"high_heel_sandals","z":"高跟凉鞋","s":"脚","n":0},{"t":"ankle_boots","z":"短靴","s":"脚","n":0},{"t":"shoe_soles","z":"鞋底","s":"脚","n":0},{"t":"toeless_legwear","z":"露趾袜","s":"脚","n":0},{"t":"anklet","z":"脚链","s":"脚","n":0},{"t":"dark_skin","z":"深色皮肤","s":"皮肤与体表","n":0},{"t":"dark-skinned_female","z":"深肤色女性","s":"皮肤与体表","n":0},{"t":"dark-skinned_male","z":"深肤色男性","s":"皮肤与体表","n":0},{"t":"pale_skin","z":"白皙皮肤","s":"皮肤与体表","n":0},{"t":"colored_skin","z":"异色皮肤(幻想设定)","s":"皮肤与体表","n":0},{"t":"sweat","z":"出汗","s":"皮肤与体表","n":0},{"t":"full_body","z":"全身","s":"皮肤与体表","n":0},{"t":"upper_body","z":"上半身","s":"皮肤与体表","n":0},{"t":"lower_body","z":"下半身","s":"皮肤与体表","n":0},{"t":"pussy","z":"女性外阴","s":"生殖器","n":1},{"t":"vaginal","z":"阴道(可见内部)","s":"生殖器","n":1},{"t":"clitoris","z":"阴蒂","s":"生殖器","n":1},{"t":"partially_visible_vulva","z":"部分可见外阴","s":"生殖器","n":1},{"t":"spread_pussy","z":"掰开展示","s":"生殖器","n":1},{"t":"spreading_own_pussy","z":"自己掰开","s":"生殖器","n":1},{"t":"pussy_juice","z":"爱液","s":"生殖器","n":1},{"t":"female_pubic_hair","z":"女性阴毛","s":"生殖器","n":1},{"t":"penis","z":"阴茎","s":"生殖器","n":1},{"t":"large_penis","z":"大","s":"生殖器","n":1},{"t":"huge_penis","z":"极大","s":"生殖器","n":1},{"t":"testicles","z":"睾丸","s":"生殖器","n":1},{"t":"male_pubic_hair","z":"男性阴毛","s":"生殖器","n":1},{"t":"anus","z":"肛","s":"生殖器","n":1},{"t":"spread_anus","z":"掰开","s":"生殖器","n":1},{"t":"pubic_hair","z":"阴毛(通用)","s":"生殖器","n":1},{"t":"pubic_hair_peek","z":"阴毛隐约露出","s":"生殖器","n":1},{"t":"colored_pubic_hair","z":"染色阴毛","s":"生殖器","n":1},{"t":"groin","z":"胯部","s":"生殖器","n":1},{"t":"crotch","z":"裆部","s":"生殖器","n":1},{"t":"covering_crotch","z":"遮住裆部","s":"生殖器","n":1},{"t":"crotch_seam","z":"裆缝(衣物)","s":"生殖器","n":1},{"t":"crotchless","z":"开裆","s":"生殖器","n":1},{"t":"cameltoe","z":"骆驼趾","s":"生殖器","n":1},{"t":"wet","z":"湿","s":"生殖器","n":1},{"t":"vulva","z":"计数 529K 但 `pussy` 更常用","s":"生殖器","n":1},{"t":"labia","z":"零热度","s":"生殖器","n":1},{"t":"shaved","z":"零热度","s":"生殖器","n":1},{"t":"wetness","z":"零热度","s":"生殖器","n":1},{"t":"shirt","z":"衬衫","s":"服装上装","n":0},{"t":"white_shirt","z":"白衬衫","s":"服装上装","n":0},{"t":"black_shirt","z":"黑衬衫","s":"服装上装","n":0},{"t":"blue_shirt","z":"蓝衬衫","s":"服装上装","n":0},{"t":"red_shirt","z":"红衬衫","s":"服装上装","n":0},{"t":"grey_shirt","z":"灰衬衫","s":"服装上装","n":0},{"t":"pink_shirt","z":"粉衬衫","s":"服装上装","n":0},{"t":"yellow_shirt","z":"黄衬衫","s":"服装上装","n":0},{"t":"collared_shirt","z":"有领衬衫","s":"服装上装","n":0},{"t":"dress_shirt","z":"正装衬衫","s":"服装上装","n":0},{"t":"t-shirt","z":"T 恤","s":"服装上装","n":0},{"t":"sleeveless_shirt","z":"无袖上衣","s":"服装上装","n":0},{"t":"camisole","z":"吊带背心","s":"服装上装","n":0},{"t":"white_camisole","z":"白吊带","s":"服装上装","n":0},{"t":"black_camisole","z":"黑吊带","s":"服装上装","n":0},{"t":"jacket","z":"外套","s":"服装上装","n":0},{"t":"black_jacket","z":"黑外套","s":"服装上装","n":0},{"t":"white_jacket","z":"白外套","s":"服装上装","n":0},{"t":"blue_jacket","z":"蓝外套","s":"服装上装","n":0},{"t":"red_jacket","z":"红外套","s":"服装上装","n":0},{"t":"open_jacket","z":"敞开的外套","s":"服装上装","n":0},{"t":"hooded_jacket","z":"连帽外套","s":"服装上装","n":0},{"t":"cropped_jacket","z":"短外套","s":"服装上装","n":0},{"t":"leather_jacket","z":"皮衣","s":"服装上装","n":0},{"t":"denim_jacket","z":"牛仔外套","s":"服装上装","n":0},{"t":"coat","z":"大衣","s":"服装上装","n":0},{"t":"black_coat","z":"黑大衣","s":"服装上装","n":0},{"t":"white_coat","z":"白大衣","s":"服装上装","n":0},{"t":"open_coat","z":"敞开的大衣","s":"服装上装","n":0},{"t":"sweater","z":"毛衣","s":"服装上装","n":0},{"t":"cardigan","z":"开衫","s":"服装上装","n":0},{"t":"turtleneck","z":"高领","s":"服装上装","n":0},{"t":"vest","z":"背心","s":"服装上装","n":0},{"t":"black_vest","z":"黑背心","s":"服装上装","n":0},{"t":"sweater_vest","z":"毛背心","s":"服装上装","n":0},{"t":"hoodie","z":"连帽衫","s":"服装上装","n":0},{"t":"off_shoulder","z":"露单肩 / 滑肩","s":"服装上装","n":0},{"t":"halterneck","z":"挂脖","s":"服装上装","n":0},{"t":"sleeveless","z":"无袖","s":"服装上装","n":0},{"t":"long_sleeves","z":"长袖","s":"服装上装","n":0},{"t":"short_sleeves","z":"短袖","s":"服装上装","n":0},{"t":"sleeves_past_wrists","z":"袖子盖过手","s":"服装上装","n":0},{"t":"partially_unbuttoned","z":"部分解开","s":"服装上装","n":0},{"t":"unbuttoned","z":"完全解开","s":"服装上装","n":1},{"t":"button_gap","z":"纽扣间露出缝隙","s":"服装上装","n":1},{"t":"shirt_lift","z":"衬衫被掀起","s":"服装上装","n":1},{"t":"open_shirt","z":"敞开的衬衫","s":"服装上装","n":0},{"t":"necktie","z":"领带","s":"服装上装","n":0},{"t":"red_necktie","z":"红领带","s":"服装上装","n":0},{"t":"black_necktie","z":"黑领带","s":"服装上装","n":0},{"t":"neckerchief","z":"领巾","s":"服装上装","n":0},{"t":"neck_ribbon","z":"颈部蝴蝶结","s":"服装上装","n":0},{"t":"skirt","z":"裙","s":"服装下装与连身","n":0},{"t":"pleated_skirt","z":"百褶裙","s":"服装下装与连身","n":0},{"t":"miniskirt","z":"短裙","s":"服装下装与连身","n":0},{"t":"long_skirt","z":"长裙","s":"服装下装与连身","n":0},{"t":"high-waist_skirt","z":"高腰裙","s":"服装下装与连身","n":0},{"t":"pants","z":"长裤","s":"服装下装与连身","n":0},{"t":"black_pants","z":"黑裤","s":"服装下装与连身","n":0},{"t":"jeans","z":"牛仔裤","s":"服装下装与连身","n":0},{"t":"shorts","z":"短裤","s":"服装下装与连身","n":0},{"t":"denim_shorts","z":"牛仔短裤","s":"服装下装与连身","n":0},{"t":"high-waist_shorts","z":"高腰短裤","s":"服装下装与连身","n":0},{"t":"leggings","z":"紧身裤","s":"服装下装与连身","n":0},{"t":"dress","z":"连衣裙","s":"服装下装与连身","n":0},{"t":"white_dress","z":"白裙","s":"服装下装与连身","n":0},{"t":"black_dress","z":"黑裙","s":"服装下装与连身","n":0},{"t":"long_dress","z":"长裙","s":"服装下装与连身","n":0},{"t":"strapless_dress","z":"无肩带裙","s":"服装下装与连身","n":0},{"t":"sundress","z":"吊带夏裙","s":"服装下装与连身","n":0},{"t":"wedding_dress","z":"婚纱","s":"服装下装与连身","n":0},{"t":"gown","z":"礼服","s":"服装下装与连身","n":0},{"t":"robe","z":"长袍、浴袍","s":"服装下装与连身","n":0},{"t":"school_uniform","z":"校服","s":"服装下装与连身","n":0},{"t":"uniform","z":"制服(通用)","s":"服装下装与连身","n":0},{"t":"suit","z":"西装","s":"服装下装与连身","n":0},{"t":"kimono","z":"和服","s":"服装下装与连身","n":0},{"t":"yukata","z":"浴衣","s":"服装下装与连身","n":0},{"t":"apron","z":"围裙","s":"服装下装与连身","n":0},{"t":"naked_apron","z":"裸体围裙","s":"服装下装与连身","n":1},{"t":"panties","z":"内裤","s":"内衣与腿部穿戴","n":1},{"t":"white_panties","z":"白内裤","s":"内衣与腿部穿戴","n":1},{"t":"black_panties","z":"黑内裤","s":"内衣与腿部穿戴","n":1},{"t":"blue_panties","z":"蓝内裤","s":"内衣与腿部穿戴","n":1},{"t":"pink_panties","z":"粉内裤","s":"内衣与腿部穿戴","n":1},{"t":"striped_panties","z":"条纹内裤","s":"内衣与腿部穿戴","n":1},{"t":"side-tie_panties","z":"侧系带内裤","s":"内衣与腿部穿戴","n":1},{"t":"highleg_panties","z":"高开衩内裤","s":"内衣与腿部穿戴","n":1},{"t":"thong","z":"丁字裤","s":"内衣与腿部穿戴","n":1},{"t":"no_panties","z":"没穿内裤","s":"内衣与腿部穿戴","n":1},{"t":"bra","z":"胸罩","s":"内衣与腿部穿戴","n":1},{"t":"black_bra","z":"黑胸罩","s":"内衣与腿部穿戴","n":1},{"t":"no_bra","z":"没穿胸罩","s":"内衣与腿部穿戴","n":1},{"t":"underwear","z":"内衣(通用)","s":"内衣与腿部穿戴","n":1},{"t":"lingerie","z":"性感内衣套装","s":"内衣与腿部穿戴","n":1},{"t":"bodysuit","z":"紧身连体衣","s":"内衣与腿部穿戴","n":1},{"t":"black_bodysuit","z":"黑连体衣","s":"内衣与腿部穿戴","n":1},{"t":"bodystocking","z":"全身网袜","s":"内衣与腿部穿戴","n":1},{"t":"thighhighs","z":"过膝袜","s":"内衣与腿部穿戴","n":1},{"t":"black_thighhighs","z":"黑过膝袜","s":"内衣与腿部穿戴","n":1},{"t":"white_thighhighs","z":"白过膝袜","s":"内衣与腿部穿戴","n":1},{"t":"striped_thighhighs","z":"条纹过膝袜","s":"内衣与腿部穿戴","n":1},{"t":"fishnet_thighhighs","z":"渔网过膝袜","s":"内衣与腿部穿戴","n":1},{"t":"torn_thighhighs","z":"破洞过膝袜","s":"内衣与腿部穿戴","n":1},{"t":"single_thighhigh","z":"只穿一只","s":"内衣与腿部穿戴","n":1},{"t":"highleg","z":"高开衩","s":"内衣与腿部穿戴","n":1},{"t":"pantyhose","z":"连裤袜","s":"内衣与腿部穿戴","n":1},{"t":"black_pantyhose","z":"黑丝","s":"内衣与腿部穿戴","n":1},{"t":"white_pantyhose","z":"白丝","s":"内衣与腿部穿戴","n":1},{"t":"torn_pantyhose","z":"破洞丝袜","s":"内衣与腿部穿戴","n":1},{"t":"fishnet_pantyhose","z":"渔网袜","s":"内衣与腿部穿戴","n":1},{"t":"panties_under_pantyhose","z":"内裤穿在丝袜外","s":"内衣与腿部穿戴","n":1},{"t":"garter_belt","z":"吊袜带","s":"内衣与腿部穿戴","n":1},{"t":"garter_straps","z":"吊袜带绳","s":"内衣与腿部穿戴","n":1},{"t":"panties_aside","z":"内裤拨到一边","s":"内衣与腿部穿戴","n":1},{"t":"panties_around_one_leg","z":"内裤挂在一条腿上","s":"内衣与腿部穿戴","n":1},{"t":"nude","z":"裸","s":"裸露与遮挡","n":1},{"t":"completely_nude","z":"全裸","s":"裸露与遮挡","n":1},{"t":"naked","z":"裸体(口语近义)","s":"裸露与遮挡","n":1},{"t":"topless_female","z":"女性上半身裸","s":"裸露与遮挡","n":1},{"t":"topless_male","z":"男性上半身裸","s":"裸露与遮挡","n":1},{"t":"partially_undressed","z":"半脱","s":"裸露与遮挡","n":1},{"t":"undressing","z":"正在脱衣","s":"裸露与遮挡","n":1},{"t":"implied_nudity","z":"暗示裸体,不直接画","s":"裸露与遮挡","n":1},{"t":"covered_collarbone","z":"锁骨被遮","s":"裸露与遮挡","n":1},{"t":"covering_breasts","z":"用手臂或手遮胸","s":"裸露与遮挡","n":1},{"t":"covered_mouth","z":"遮嘴","s":"裸露与遮挡","n":1},{"t":"covered_eyes","z":"遮眼","s":"裸露与遮挡","n":1},{"t":"covered_face","z":"遮脸","s":"裸露与遮挡","n":1},{"t":"nude_cover","z":"裸体遮挡(通用)","s":"裸露与遮挡","n":1},{"t":"naked_shirt","z":"只穿衬衫","s":"裸露与遮挡","n":1},{"t":"naked_towel","z":"只围浴巾","s":"裸露与遮挡","n":1},{"t":"naked_ribbon","z":"只绑缎带","s":"裸露与遮挡","n":1},{"t":"clothed_sex","z":"穿着衣服做","s":"裸露与遮挡","n":1},{"t":"clothed_female_nude_male","z":"女方穿衣男方裸","s":"裸露与遮挡","n":1},{"t":"clothed_male_nude_female","z":"男方穿衣女方裸","s":"裸露与遮挡","n":1},{"t":"after_sex","z":"事后","s":"裸露与遮挡","n":1},{"t":"open_clothes","z":"衣服敞开","s":"服装状态","n":1},{"t":"clothes_lift","z":"衣服被掀起","s":"服装状态","n":1},{"t":"skirt_lift","z":"裙子被掀起","s":"服装状态","n":1},{"t":"torn_clothes","z":"破衣服","s":"服装状态","n":1},{"t":"wet_clothes","z":"湿衣服","s":"服装状态","n":1},{"t":"translucent","z":"半透明","s":"服装状态","n":1},{"t":"one_breast_out","z":"单乳露出","s":"服装状态","n":1},{"t":"wardrobe_malfunction","z":"走光(通用)","s":"服装状态","n":1},{"t":"standing","z":"站","s":"姿势与体位","n":0},{"t":"sitting","z":"坐","s":"姿势与体位","n":0},{"t":"lying","z":"躺","s":"姿势与体位","n":0},{"t":"kneeling","z":"跪","s":"姿势与体位","n":0},{"t":"squatting","z":"蹲","s":"姿势与体位","n":0},{"t":"crawling","z":"爬行","s":"姿势与体位","n":0},{"t":"fetal_position","z":"蜷缩","s":"姿势与体位","n":0},{"t":"on_side","z":"侧躺","s":"姿势与体位","n":0},{"t":"lying_on_side","z":"侧卧(同义)","s":"姿势与体位","n":0},{"t":"leaning_forward","z":"前倾","s":"姿势与体位","n":0},{"t":"leaning_back","z":"后仰","s":"姿势与体位","n":0},{"t":"leaning_to_the_side","z":"侧倾","s":"姿势与体位","n":0},{"t":"leaning_on_object","z":"靠在物体上","s":"姿势与体位","n":0},{"t":"against_wall","z":"靠墙","s":"姿势与体位","n":0},{"t":"bent_over","z":"弯腰前俯","s":"姿势与体位","n":0},{"t":"arched_back","z":"挺胸弓腰","s":"姿势与体位","n":0},{"t":"all_fours","z":"四肢着地","s":"姿势与体位","n":1},{"t":"on_all_fours","z":"四肢着地(同义)","s":"姿势与体位","n":1},{"t":"straddling","z":"跨坐","s":"姿势与体位","n":1},{"t":"upright_straddle","z":"直立跨坐,面对面抱姿","s":"姿势与体位","n":1},{"t":"squatting_cowgirl_position","z":"蹲踞骑乘","s":"姿势与体位","n":1},{"t":"sitting_on_person","z":"坐在人身上","s":"姿势与体位","n":1},{"t":"sitting_on_lap","z":"坐腿上","s":"姿势与体位","n":0},{"t":"paw_pose","z":"猫爪手势","s":"姿势与体位","n":0}]
|