Files
2026-04-18 17:02:56 +02:00
..
2026-04-18 17:02:56 +02:00
2026-04-18 17:02:56 +02:00
2026-04-18 17:02:56 +02:00
2026-04-18 17:02:56 +02:00
2026-04-18 17:02:56 +02:00
2026-04-18 17:02:56 +02:00
2026-04-18 17:02:56 +02:00
2026-04-18 17:02:56 +02:00
2026-04-18 17:02:56 +02:00

Discover Pages

This folder documents how each public discovery surface is assembled today.

It is intentionally page-oriented rather than architecture-oriented. For the broader discovery engine, signal collection, and personalization background, also see docs/discovery-personalization-engine.md.

Pages Covered

  • TrendingGET /discover/trending
  • RisingGET /discover/rising
  • FreshGET /discover/fresh
  • Top RatedGET /discover/top-rated
  • Most DownloadedGET /discover/most-downloaded
  • Today DownloadsGET /downloads/today
  • On This DayGET /discover/on-this-day
  • For YouGET /discover/for-you (auth only)

Shared Request Pipeline

Most Discover pages follow this pattern:

  1. Route enters App\Http\Controllers\Web\DiscoverController.
  2. The controller calls App\Services\ArtworkSearchService.
  3. ArtworkSearchService queries the artworks Meilisearch index through Laravel Scout.
  4. The search result usually contains only search/index fields.
  5. DiscoverController::hydrateDiscoverSearchResults() then reloads full Artwork rows from MySQL with relations (user, profile, categories) and converts them into the view model used by Blade.
  6. Some pages can still pass through additional presentation layers such as GridFiller, depending on the controller action.

Shared Visibility Rules

All search-backed pages use the same base visibility filter in ArtworkSearchService:

is_public = true AND is_approved = true

That means an artwork must be:

  • public
  • approved
  • present in the Meilisearch index

If the database row is correct but search is stale, the page can still miss the artwork until indexing catches up.

Shared Cache Behavior

ArtworkSearchService uses application cache in front of Meilisearch.

  • Default TTL: 300 seconds
  • Rising: 120 seconds
  • Category/content-type sort pages use per-sort TTLs, but those are outside this folder's scope

The page can therefore lag behind a real publish or stat change even when the underlying data is already correct.

Shared Supporting Jobs

These jobs are active in the current Laravel 11 runtime scheduler (routes/console.php):

  • skinbase:flush-redis-stats every 5 minutes
  • skinbase:recalculate-trending --period=24h every 30 minutes
  • skinbase:recalculate-trending --period=7d --skip-index every 30 minutes
  • skinbase:reset-windowed-stats --period=24h daily at 03:30
  • skinbase:reset-windowed-stats --period=7d weekly (Monday) at 03:30
  • nova:recalculate-rankings --sync-rank-scores every 30 minutes
  • artworks:publish-scheduled every minute
  • analytics:aggregate-discovery-feedback daily at 03:25
  • RecBuildItemPairsFromFavouritesJob every 4 hours
  • RecComputeSimilarByTagsJob daily at 02:00
  • RecComputeSimilarByBehaviorJob daily at 02:15
  • RecComputeSimilarHybridJob daily at 02:30

Important Scheduler Caveat

The codebase still contains some discovery-related schedules inside app/Console/Kernel.php, but the active Laravel 11 runtime schedule comes from routes/console.php.

The Rising pipeline depends on these active runtime jobs:

  • nova:metrics-snapshot-hourly hourly
  • nova:recalculate-heat every 15 minutes

If Rising stops moving while Trending changes, check php artisan schedule:list first and confirm both jobs are still active.

File Map

  • trending.md
  • rising.md
  • fresh.md
  • top-rated.md
  • most-downloaded.md
  • today-downloads.md
  • on-this-day.md
  • for-you.md