{"release_id":"01a1998d-9802-0000-b0f7-33a23307f4ed","chunk_id":"01a1998d-992a-7d93-a209-e72e500ff5bb","file":"D72wGpZh.mjs","mappings":"qTAWO,MAAMA,EAAc,CAAC,MAAO,SAAU,QAAQ,EAIxCC,EAAiB,kBAIjBC,EAAYC,GAAcA,EAAE,WAAa,EACzCC,EAAaD,GAAcA,EAAE,WAAaA,EAAE,WAAa,EAE/D,SAASE,EAAaF,EAAWG,EAA4B,CACnE,OAAIA,IAAU,SAAiBJ,EAASC,CAAC,EACrCG,IAAU,SAAiBF,EAAUD,CAAC,EACnC,EACR,CAEO,SAASI,EAAaC,EAA6B,CACzD,OAAO,OAAOA,GAAM,UAAaR,EAAkC,SAASQ,CAAC,CAC9E,CAEA,MAAMC,EAAc,IAAI,IAAI,CAAC,QAAS,SAAU,MAAO,OAAQ,OAAQ,OAAO,CAAC,EACzEC,EAAc,IAAI,IAAI,CAAC,QAAS,SAAU,MAAO,OAAQ,UAAW,UAAU,CAAC,EAO9E,SAASC,EAAgBC,EAAoD,CACnF,IAAIC,EAAQ,GACRC,EAAQ,GACZ,MAAMC,EAAiB,CAAA,EACvB,UAAWC,KAAKJ,EAAM,OAAO,cAAc,MAAM,KAAK,EAAE,OAAO,OAAO,EACjEH,EAAY,IAAIO,CAAC,EAAGH,EAAQ,GACvBH,EAAY,IAAIM,CAAC,EAAGF,EAAQ,GAChCC,EAAK,KAAKC,CAAC,EAGjB,MAAO,CAAE,MADiBH,IAAUC,EAAQ,MAAQD,EAAQ,SAAW,SACvD,KAAME,EAAK,KAAK,GAAG,CAAA,CACpC,CAGO,SAASE,EAAcd,EAA0B,CACvD,MAAMe,EAAU,CAAChB,EAASC,CAAC,EAAI,SAAW,KAAMC,EAAUD,CAAC,EAAI,SAAW,IAAI,EAAE,OAAO,OAAO,EAC9F,OAAOe,EAAQ,OAASA,EAAQ,KAAK,OAAO,EAAI,IACjD","names":["ALBUM_MEDIA","MEDIA_PREF_KEY","hasClips","a","hasPhotos","matchesMedia","media","isAlbumMedia","v","VIDEO_WORDS","PHOTO_WORDS","parseMediaQuery","query","video","photo","rest","w","browseShelves","shelves"],"sources":["../../../../../../src/lib/album-media.ts"],"sourcesContent":["import type { AlbumCard } from '$lib/types/album';\n\n/**\n * Photo albums and video albums, told apart for the buyer (RAT-621). The kind\n * comes from what's in the album, never from a choice the creator makes: a\n * third of the published albums mix photos and clips, and they show under\n * both. Albums with a clip in them sold more often (2.8 unlocks on average\n * against 2.3 for photo-only, published 14+ days, 2026-10-25), at the same\n * median price of 10 tokens.\n */\n\nexport const ALBUM_MEDIA = ['all', 'videos', 'photos'] as const;\nexport type AlbumMedia = (typeof ALBUM_MEDIA)[number];\n\n/** The browser's choice, kept the way the tag chips keep theirs. */\nexport const MEDIA_PREF_KEY = 'rmd_album_media';\n\ntype Counts = Pick<AlbumCard, 'photoCount' | 'videoCount'>;\n\nexport const hasClips = (a: Counts) => a.videoCount > 0;\nexport const hasPhotos = (a: Counts) => a.photoCount - a.videoCount > 0;\n\nexport function matchesMedia(a: Counts, media: AlbumMedia): boolean {\n\tif (media === 'videos') return hasClips(a);\n\tif (media === 'photos') return hasPhotos(a);\n\treturn true;\n}\n\nexport function isAlbumMedia(v: unknown): v is AlbumMedia {\n\treturn typeof v === 'string' && (ALBUM_MEDIA as readonly string[]).includes(v);\n}\n\nconst VIDEO_WORDS = new Set(['video', 'videos', 'vid', 'vids', 'clip', 'clips']);\nconst PHOTO_WORDS = new Set(['photo', 'photos', 'pic', 'pics', 'picture', 'pictures']);\n\n/**\n * A search for \"hairy videos\" is a search for \"hairy\" in albums with clips:\n * the media words come out of the text and become a filter. Words for both\n * cancel out.\n */\nexport function parseMediaQuery(query: string): { media: AlbumMedia; text: string } {\n\tlet video = false;\n\tlet photo = false;\n\tconst rest: string[] = [];\n\tfor (const w of query.trim().toLowerCase().split(/\\s+/).filter(Boolean)) {\n\t\tif (VIDEO_WORDS.has(w)) video = true;\n\t\telse if (PHOTO_WORDS.has(w)) photo = true;\n\t\telse rest.push(w);\n\t}\n\tconst media: AlbumMedia = video === photo ? 'all' : video ? 'videos' : 'photos';\n\treturn { media, text: rest.join(' ') };\n}\n\n/** Where the album browser lists an album, for its creator: \"videos and photos\". */\nexport function browseShelves(a: Counts): string | null {\n\tconst shelves = [hasClips(a) ? 'videos' : null, hasPhotos(a) ? 'photos' : null].filter(Boolean);\n\treturn shelves.length ? shelves.join(' and ') : null;\n}\n"],"version":3}