|
| 1 | +name: Metrics |
| 2 | +description: > |
| 3 | + Comprehensive library analytics for Stash. Adds an interactive Metrics |
| 4 | + dashboard with performer-centric demographics, tag co-occurrence, studio |
| 5 | + output, collaboration networks and time-series charts. Ships runnable |
| 6 | + tasks to generate full JSON/CSV/HTML reports and to refresh the cached |
| 7 | + analytics consumed by the dashboard. |
| 8 | +version: 1.9.0 |
| 9 | +url: https://github.com/Stash-Metrics/stash-metrics-plugin |
| 10 | +# Stash plugin manifest — see https://docs.stashapp.cc/in-app-manual/plugins/ |
| 11 | + |
| 12 | +# Stash's plugin schema requires `exec:` at the plugin level, not per task |
| 13 | +# or hook. Every task / hook shares this launcher; the specific mode is |
| 14 | +# passed via defaultArgs and read from stdin JSON's `args.mode` by |
| 15 | +# backend/compute_metrics.js. |
| 16 | +exec: |
| 17 | + - node |
| 18 | + - "{pluginDir}/backend/compute_metrics.js" |
| 19 | + |
| 20 | +# Communication protocol between Stash and the script. |
| 21 | +interface: raw |
| 22 | + |
| 23 | +ui: |
| 24 | + javascript: |
| 25 | + - assets/lib/chart.umd.min.js |
| 26 | + - assets/lib/chartjs-plugin-datalabels.min.js |
| 27 | + - assets/lib/chartjs-chart-matrix.min.js |
| 28 | + - src/utils/format.js |
| 29 | + - src/utils/stats.js |
| 30 | + - src/utils/correlations.js |
| 31 | + - src/data/graphql.js |
| 32 | + - src/data/cache.js |
| 33 | + - src/data/preferences.js |
| 34 | + # Backend-generated cache bootstrap — Stash's plugin schema only serves |
| 35 | + # files declared in ui.javascript, so we ship the metrics cache as a JS |
| 36 | + # file that assigns to window.__STASH_METRICS_CACHE__. The file ships |
| 37 | + # as a null stub committed to the repo; Update Metrics Cache overwrites |
| 38 | + # it with the real payload. |
| 39 | + - assets/metrics-cache.js |
| 40 | + - src/charts/overview.js |
| 41 | + - src/charts/performers.js |
| 42 | + - src/charts/tags.js |
| 43 | + - src/charts/networks.js |
| 44 | + - src/charts/timeseries.js |
| 45 | + - src/charts/correlations.js |
| 46 | + - src/charts/matches.js |
| 47 | + - src/charts/play.js |
| 48 | + - src/charts/archetypes.js |
| 49 | + - src/charts/insights.js |
| 50 | + - src/charts/fantasy.js |
| 51 | + - src/charts/quality.js |
| 52 | + - src/charts/wrapped.js |
| 53 | + - src/charts/bingo.js |
| 54 | + - src/charts/tagopt.js |
| 55 | + - src/charts/cleanup.js |
| 56 | + - src/dashboard.js |
| 57 | + - src/metrics.js |
| 58 | + css: |
| 59 | + - src/styles.css |
| 60 | + |
| 61 | +# Settings appear under Settings → Plugins → Metrics in the Stash UI and |
| 62 | +# are exposed to backend scripts via the PLUGIN_SETTINGS_JSON env var. |
| 63 | +settings: |
| 64 | + cacheTtlMinutes: |
| 65 | + displayName: Cache TTL (minutes) |
| 66 | + description: How long computed metrics remain valid before the dashboard refetches. |
| 67 | + type: NUMBER |
| 68 | + disableNetworkGraph: |
| 69 | + displayName: Disable collaboration network graph |
| 70 | + description: Skip the performer co-occurrence force-directed graph on slow clients. |
| 71 | + type: BOOLEAN |
| 72 | + topN: |
| 73 | + displayName: Top-N items per chart |
| 74 | + description: How many entries (performers, tags, studios) to show in ranking charts. |
| 75 | + type: NUMBER |
| 76 | + ageBuckets: |
| 77 | + displayName: Age bucket size (years) |
| 78 | + description: Granularity of the performer age pyramid (e.g. 5 = 5-year buckets). |
| 79 | + type: NUMBER |
| 80 | + heightUnit: |
| 81 | + displayName: Height unit |
| 82 | + description: cm or in — used for all height-related charts. |
| 83 | + type: STRING |
| 84 | + excludeTagIds: |
| 85 | + displayName: Excluded tag IDs |
| 86 | + description: Comma-separated tag IDs to exclude from all tag aggregations. |
| 87 | + type: STRING |
| 88 | + excludePerformerIds: |
| 89 | + displayName: Excluded performer IDs |
| 90 | + description: Comma-separated performer IDs to exclude from all performer aggregations. |
| 91 | + type: STRING |
| 92 | + hideOtherBucket: |
| 93 | + displayName: Hide "Other" bucket in top-N charts |
| 94 | + description: > |
| 95 | + When ON, the tail of tag / country / studio distributions is dropped |
| 96 | + entirely instead of being aggregated into an "Other" slice. Cleans |
| 97 | + up pie charts and treemaps when the top-N heavily dominates. |
| 98 | + type: BOOLEAN |
| 99 | + genderFilter: |
| 100 | + displayName: Gender filter |
| 101 | + description: > |
| 102 | + Restrict every tab to the listed genders. Comma-separated list of |
| 103 | + Stash gender enum values: FEMALE, MALE, TRANSGENDER_FEMALE, |
| 104 | + TRANSGENDER_MALE, INTERSEX, NON_BINARY. Leave blank (or write "all") |
| 105 | + for no filter. Examples: "FEMALE" for female-only stats; "FEMALE,MALE" |
| 106 | + for both binary genders; "FEMALE,TRANSGENDER_FEMALE" to include trans |
| 107 | + women. Scenes are kept when they contain at least one performer of |
| 108 | + an allowed gender. Re-run Update Metrics Cache after changing this. |
| 109 | + type: STRING |
| 110 | + reportFormats: |
| 111 | + displayName: Report formats |
| 112 | + description: Comma-separated list of report formats to produce. Any of json,csv,html. |
| 113 | + type: STRING |
| 114 | + refreshOnScanComplete: |
| 115 | + displayName: Refresh cache after scans |
| 116 | + description: Automatically run the Update Metrics Cache task after Stash finishes scanning. |
| 117 | + type: BOOLEAN |
| 118 | + refreshOnMetadataUpdate: |
| 119 | + displayName: Refresh cache after metadata generation |
| 120 | + description: Automatically run the Update Metrics Cache task after metadata generation completes. |
| 121 | + type: BOOLEAN |
| 122 | + |
| 123 | + # ---- Correlation & recommender settings (v1.1) --------------------------- |
| 124 | + # Feature-flag toggles use `disable*` semantics because Stash reports |
| 125 | + # every untouched BOOLEAN as `false` — with `enable*` names, the plugin |
| 126 | + # would ship with every tab OFF on a fresh install. `disable*: true` |
| 127 | + # explicitly opts out of a heavy computation. |
| 128 | + disableCorrelations: |
| 129 | + displayName: Disable correlations tab |
| 130 | + description: Skip attribute × O-count correlation aggregates on very large libraries. |
| 131 | + type: BOOLEAN |
| 132 | + correlationMinSupport: |
| 133 | + displayName: Correlation min support |
| 134 | + description: > |
| 135 | + Drop attribute cells with fewer than this many performers / scenes |
| 136 | + from correlation aggregates so single-sample groups don't dominate. |
| 137 | + type: NUMBER |
| 138 | + preferenceProfile: |
| 139 | + displayName: Preference profile (JSON) |
| 140 | + description: > |
| 141 | + JSON document describing your preference profile. The Matches tab reads |
| 142 | + this on load (and lets you edit it inline). Keys: |
| 143 | + countries, ethnicities, eyeColors, hairColors, cupSizes, hairLengths, |
| 144 | + requiredTags, excludedTags, minHeightCm, maxHeightCm, minAge, maxAge, |
| 145 | + minOCount, weights{country,ethnicity,eyeColor,hairColor,cup,height, |
| 146 | + age,oCount,tagOverlap,rating}, topMatches. |
| 147 | + Empty object {} disables preference filtering entirely. |
| 148 | + type: STRING |
| 149 | + matchMinScore: |
| 150 | + displayName: Match minimum score (0–100) |
| 151 | + description: Drop performers/scenes whose weighted match score is below this threshold. |
| 152 | + type: NUMBER |
| 153 | + matchIncludeUnknown: |
| 154 | + displayName: Treat missing attributes as partial matches |
| 155 | + description: > |
| 156 | + When ON, an attribute with no data (e.g. unknown eye color) contributes |
| 157 | + 0.5 × its weight to the score instead of 0. Useful on libraries with |
| 158 | + sparse performer metadata. |
| 159 | + type: BOOLEAN |
| 160 | + |
| 161 | + # ---- v1.2 advanced analytics ------------------------------------------ |
| 162 | + disablePlayHistory: |
| 163 | + displayName: Disable Play tab |
| 164 | + description: Skip the day-of-week × hour-of-day heatmap + most-watched aggregation. |
| 165 | + type: BOOLEAN |
| 166 | + disableArchetypes: |
| 167 | + displayName: Disable Archetypes tab |
| 168 | + description: Skip the O(n²) k-means clustering + cosine-similarity index on very large libraries. |
| 169 | + type: BOOLEAN |
| 170 | + archetypeClusters: |
| 171 | + displayName: Archetype cluster count (k) |
| 172 | + description: Number of performer archetype clusters to compute (2–12). |
| 173 | + type: NUMBER |
| 174 | + disableDiversity: |
| 175 | + displayName: Disable diversity analytics |
| 176 | + description: Skip Shannon / Gini-Simpson diversity per attribute + gap analysis. |
| 177 | + type: BOOLEAN |
| 178 | + disableTemporal: |
| 179 | + displayName: Disable temporal analytics |
| 180 | + description: Skip release-year + career-timeline + tag-evolution charts on the Insights tab. |
| 181 | + type: BOOLEAN |
| 182 | + disableFantasy: |
| 183 | + displayName: Disable Fantasy Builder |
| 184 | + description: Skip reverse-engineering a preference profile from your favourites / top-rated / top-O seed set. |
| 185 | + type: BOOLEAN |
| 186 | + fantasyMode: |
| 187 | + displayName: Fantasy Builder seed mode |
| 188 | + description: How to pick the seed performers — favorites | top-rated | top-o. |
| 189 | + type: STRING |
| 190 | + fantasyConsensus: |
| 191 | + displayName: Fantasy consensus threshold |
| 192 | + description: Fraction of seed performers that must share a value before it's locked into the derived profile (0.3 = loose, 0.7 = strict). |
| 193 | + type: NUMBER |
| 194 | + |
| 195 | + # ---- v1.3 quality + excel --------------------------------------------- |
| 196 | + enableQuality: |
| 197 | + displayName: Enable Quality tab |
| 198 | + description: Hidden gems, overrated / underrated insights and a composite library-health score. |
| 199 | + type: BOOLEAN |
| 200 | + gemRatingFloor: |
| 201 | + displayName: Hidden-gem rating floor |
| 202 | + description: Minimum rating100 a scene must have to be considered a "hidden gem". |
| 203 | + type: NUMBER |
| 204 | + gemPlayCeiling: |
| 205 | + displayName: Hidden-gem play-count ceiling |
| 206 | + description: Maximum play_count for a scene to still qualify as a hidden gem. |
| 207 | + type: NUMBER |
| 208 | + qualityZThreshold: |
| 209 | + displayName: Over/underrated z-score threshold |
| 210 | + description: | |
| 211 | + |z| above this is required for a scene/performer to land in the |
| 212 | + overrated/underrated lists. 1.0 = top ~16% on each tail. |
| 213 | + type: NUMBER |
| 214 | + qualityMinScenes: |
| 215 | + displayName: Minimum scenes for performer anomalies |
| 216 | + description: A performer needs at least this many scenes before being eligible for the over/underrated list. |
| 217 | + type: NUMBER |
| 218 | + |
| 219 | + # ---- v1.8 tag optimizer ------------------------------------------------ |
| 220 | + disableTagOptimizer: |
| 221 | + displayName: Disable Tag Optimizer tab |
| 222 | + description: Skip near-duplicate detection, always-together pair analysis and hierarchy audit. |
| 223 | + type: BOOLEAN |
| 224 | + tagoptRareThreshold: |
| 225 | + displayName: Tag Optimizer — rare tag threshold |
| 226 | + description: Tags with fewer scenes than this are surfaced as "rare" cleanup candidates. |
| 227 | + type: NUMBER |
| 228 | + tagoptPairMinSupport: |
| 229 | + displayName: Tag Optimizer — pair min support |
| 230 | + description: A tag pair needs at least this many shared scenes before it's considered for merge / specialisation detection. |
| 231 | + type: NUMBER |
| 232 | + tagoptUndertagMax: |
| 233 | + displayName: Tag Optimizer — under-tagged threshold |
| 234 | + description: Scenes with this many tags or fewer are flagged as under-tagged. |
| 235 | + type: NUMBER |
| 236 | + tagoptOvertagMin: |
| 237 | + displayName: Tag Optimizer — over-tagged threshold |
| 238 | + description: Scenes with this many tags or more are flagged as over-tagged (auto-tagger noise). |
| 239 | + type: NUMBER |
| 240 | + |
| 241 | +# Runnable tasks — appear on the Tasks page under Plugin Tasks → Metrics. |
| 242 | +# All tasks share the top-level `exec:` above; `defaultArgs.mode` is the |
| 243 | +# only per-task distinction and is delivered to the script through |
| 244 | +# Stash's stdin JSON payload (args.mode). |
| 245 | +tasks: |
| 246 | + - name: Generate Full Metrics Report |
| 247 | + description: > |
| 248 | + Compute the complete analytics package, write JSON + CSV + a standalone |
| 249 | + visual HTML report to the plugin's cache/reports directory, and refresh |
| 250 | + the dashboard cache. Heavy operation — runs in the background. |
| 251 | + defaultArgs: |
| 252 | + mode: full-report |
| 253 | + |
| 254 | + - name: Update Metrics Cache |
| 255 | + description: > |
| 256 | + Recompute the aggregated metrics consumed by the dashboard. Cheap |
| 257 | + compared to the full report — no CSV/HTML output. Use after a scan or |
| 258 | + metadata regeneration. |
| 259 | + defaultArgs: |
| 260 | + mode: cache-refresh |
| 261 | + |
| 262 | + - name: Find Matches Based on My Preferences |
| 263 | + description: > |
| 264 | + Score every performer and scene against the preferenceProfile setting |
| 265 | + and write ranked match lists to cache/reports/<ts>/matches.{json,html}. |
| 266 | + Also refreshes the dashboard cache so the Matches tab opens instantly. |
| 267 | + defaultArgs: |
| 268 | + mode: match |
| 269 | + |
| 270 | + - name: Generate Year-in-Review |
| 271 | + description: > |
| 272 | + Builds a calendar-year summary (top performers, tags, studios, |
| 273 | + played + rated scenes, debuts, new tags) and writes a standalone |
| 274 | + visual HTML report. Defaults to last year. |
| 275 | + defaultArgs: |
| 276 | + mode: year-review |
| 277 | + |
| 278 | + - name: Export Full Library to Excel |
| 279 | + description: > |
| 280 | + Generates a comprehensive .xlsx workbook with one sheet per entity |
| 281 | + (Scenes, Performers, Tags, Studios), cross-reference junction |
| 282 | + sheets (Scene × Performer, Scene × Tag), tag co-occurrence, |
| 283 | + correlations, top matches (if a preference profile is set) and |
| 284 | + quality insights. Auto-filters and frozen header rows on every |
| 285 | + sheet. Output goes to cache/reports/<ts>/library-export.xlsx + |
| 286 | + cache/reports/latest-library-export.xlsx. |
| 287 | + defaultArgs: |
| 288 | + mode: excel-export |
| 289 | + |
| 290 | +# Optional hooks — wired up if the corresponding setting is on. Stash will |
| 291 | +# still call us on every event; the script no-ops when the toggle is off. |
| 292 | +hooks: |
| 293 | + - name: Refresh metrics after a scan |
| 294 | + description: Recomputes the metrics cache once a scan task finishes. |
| 295 | + triggeredBy: |
| 296 | + - Scan.Complete.Post |
| 297 | + defaultArgs: |
| 298 | + mode: hook |
| 299 | + hook: scan |
| 300 | + |
| 301 | + - name: Refresh metrics after metadata generation |
| 302 | + description: Recomputes the metrics cache once metadata generation finishes. |
| 303 | + triggeredBy: |
| 304 | + - Generate.Complete.Post |
| 305 | + defaultArgs: |
| 306 | + mode: hook |
| 307 | + hook: generate |
0 commit comments