Doujindesutviribitarigalnimankotsukawas: Top
The name is intentionally whimsical, but the purpose is clear: it will be a “Top‑Doujin” leaderboard for any platform that hosts user‑generated comic/illustration content (e.g., a manga‑sharing site, a fan‑art community, a Discord‑style hub, or even a streaming service that hosts doujin‑style videos).
1️⃣ High‑Level Overview | Item | Description | |------|-------------| | Feature name | Doujindesutviribitarigalnimankotsukawas Top (short: “Doujin Top” ) | | Goal | Surface the highest‑rated, most‑engaged doujin works in a dynamic, community‑driven leaderboard, encouraging discovery, competition, and increased creator retention. | | Primary audience | • Creators who want visibility • Readers looking for quality recommendations • Moderators/curators who need quick “what’s hot” insight | | Key metrics | • Daily active users (DAU) on the leaderboard page • Average time‑on‑page for the Top view • Increase in views/likes for items that enter the Top list (target +20 % within 48 h) • Creator satisfaction (survey NPS ≥ 8) | | Scope | • Front‑end UI widget (mobile‑responsive) • Back‑end ranking engine (daily & hourly refreshes) • API endpoints (public + internal) • Admin panel for weighting, blacklist, and manual pinning. | | Out of scope | • Full‑blown recommendation engine (this is a pure “popularity” leaderboard). • Paid‑promotion integration (will be added as a future “sponsored‑slot”). |
2️⃣ User Stories | ID | As a … | I want to … | So that … | |----|--------|--------------|-----------| | US‑001 | Reader | See a “Top Doujin” carousel on the home page | I can instantly discover the most popular works without searching | | US‑002 | Reader | Filter the Top list by genre, language, and time‑frame (24 h, 7 d, 30 d) | I can find the freshest hits or the all‑time classics | | US‑003 | Creator | Know when my work enters the Top list and get a notification | I feel recognized and am motivated to create more | | US‑004 | Creator | Pin a “Top” badge on my work’s thumbnail when it reaches a threshold | Visitors immediately see that my work is trending | | US‑005 | Moderator | Exclude flagged or NSFW items from the Top list automatically | The leaderboard stays safe for all audiences | | US‑006 | Admin | Adjust the weighting of likes vs. views vs. comments | We can fine‑tune the algorithm to match community values | | US‑007 | Developer | Retrieve the Top list via a REST endpoint (JSON) | Other services (mobile app, embed widgets) can reuse the data |
3️⃣ Functional Requirements 3.1 Ranking Algorithm | Metric | Weight (default) | Normalisation | |--------|------------------|---------------| | Views (unique, per‑hour) | 0.30 | Log‑scale to dampen viral spikes | | Likes (positive reactions) | 0.40 | Raw count, capped at 10 k to avoid outliers | | Comments (unique commenters) | 0.15 | Each unique commenter counts 1 point | | Share count (external link clicks) | 0.10 | Optional, 0 if not tracked | | Age decay | ‑0.05 per day | Ensures newer works can climb | Score formula (per‑hour): Score = (log1p(Views) * 0.30) + (Likes * 0.40) + (UniqueCommenters * 0.15) + (Shares * 0.10) - (DaysSincePublish * 0.05) doujindesutviribitarigalnimankotsukawas top
Hourly refresh : runs at the top of each hour, recomputes scores for the past 30 days. Daily snapshot : stores the top‑20 for each time‑frame (24 h, 7 d, 30 d, All‑time) for quick retrieval.
3.2 API Endpoints | Method | Path | Params | Response | |--------|------|--------|----------| | GET | /api/v1/doujin-top | timeframe=24h|7d|30d|all , genre=string , lang=string , limit=10 | {items: [{id, title, thumbnail, score, rank, stats}]} | | GET | /api/v1/doujin-top/{id} | — | Detailed entry with creator info, rating breakdown | | POST | /admin/v1/doujin-top/weights | {views, likes, comments, shares, decay} (admin only) | {status: "ok", newWeights} | | POST | /admin/v1/doujin-top/blacklist | {workId} (admin only) | {status:"ok"} | All responses are JSON with standard pagination fields ( page , per_page , total ). 3.3 UI Components
Home‑page carousel – horizontal scroll, 5‑item viewport, lazy‑loaded thumbnails. Dedicated “Top Doujin” page – grid (4‑col on desktop, 2‑col mobile), filters bar at the top, “Load more” infinite scroll. Badge overlay – a small “★ TOP” ribbon on each thumbnail that reaches rank ≤ 10 . Creator notification – push/email/web‑socket message when a work enters any Top list (configurable thresholds). The name is intentionally whimsical, but the purpose
3.4 Admin / Moderator Tools
Weight editor (slider UI, persisted in config.doujin_top_weights ). Blacklist UI – search by title/ID, toggle “Exclude from Top”. Analytics Dashboard – charts of top‑10 entry churn, average score trend, genre breakdown.
3.5 Non‑Functional Requirements | NFR | Requirement | |-----|-------------| | Performance | Ranking job must finish < 2 minutes for a dataset of up to 2 M works. API latency < 150 ms for the first 20 results. | | Scalability | Use a Redis sorted‑set for the live hourly ranking; persist snapshots to PostgreSQL (or your primary DB). | | Reliability | Run ranking job in a Kubernetes CronJob with retry‑on‑failure; store a backup snapshot in S3. | | Security | API endpoints require OAuth2 scopes ( read:top , admin:top ). Rate‑limit GET /api/v1/doujin-top to 60 rpm per IP. | | Accessibility | All UI components meet WCAG 2.1 AA (ARIA labels, focus order, contrast). | | Internationalisation | All UI strings externalised; support at least EN, JA, ZH‑CN, KO out‑of‑the‑box. | | | Out of scope | • Full‑blown
4️⃣ Data Model (Simplified) -- Existing table (simplified) CREATE TABLE works ( id BIGSERIAL PRIMARY KEY, title TEXT NOT NULL, creator_id BIGINT NOT NULL, published_at TIMESTAMP NOT NULL, genre TEXT, language TEXT, thumbnail_url TEXT, is_nsfw BOOLEAN DEFAULT FALSE, ... -- other columns );
-- New materialised view for daily snapshots CREATE MATERIALIZED VIEW doujin_top_snapshot AS SELECT w.id, w.title, w.thumbnail_url, w.genre, w.language, w.published_at, SUM( LOG(1 + w.views) * 0.30 + w.likes * 0.40 + w.unique_commenters * 0.15 + w.shares * 0.10 - (EXTRACT(DAY FROM now() - w.published_at) * 0.05) ) AS score, now() AS snapshot_at FROM works w WHERE NOT w.is_blacklisted GROUP BY w.id, w.title, w.thumbnail_url, w.genre, w.language, w.published_at;