Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 31 additions & 15 deletions firetv/store/capture.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,46 @@
# /search: real artwork and real titles (Mighty Oaks Daily Devotional, The
# Harry Sisson Show) were in the listing, and we hold no licence to any of it.
#
# So the grid comes from /tv-preview, which renders the real card grid with
# invented shows and no image URLs — <Art> then draws its own letter tile for
# every square. The app is a WebView of exactly this site at exactly this size,
# so a headless capture is what the TV shows, without needing the Android SDK.
# Both shots therefore come from /tv-preview, which renders the real card grid
# with invented shows and no image URLs, so <Art> draws its own typographic
# tile for every square. The app is a WebView of this site at this size, so a
# headless capture is what the TV shows, without needing the Android SDK.
#
# Usage (defaults to production):
# sh firetv/store/capture.sh [BASE_URL]
# Three things that are not obvious, each found the hard way:
#
# * NOT the home page. Below its hero it shows the "deep catalogues" row,
# which is real cover art - and two of those shows are the ones Amazon
# named. /tv-preview?hero=1 gives the same hero with our own grid instead.
# * NOT /search either, even with no query: it carries a sponsored ad and
# names a real third-party domain in its help text.
# * Point BASE at the app container, not at the public hostname. p0dcasters
# answers a headless browser with its own x402 paywall ("You have used up
# the free allowance") after 20 requests an hour, and you will screenshot
# that instead. A browser user agent is still needed: the default
# HeadlessChrome string is treated as a crawler.
#
# Usage:
# sh firetv/store/capture.sh # the dev2 app container
# sh firetv/store/capture.sh http://127.0.0.1:PORT
set -eu
BASE=${1:-https://p0dcasters.com}
BASE=${1:-http://127.0.0.1:3235}
OUT=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)/shots
UA="Mozilla/5.0 (X11; CrKey armv7l 1.5.16041) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
mkdir -p "$OUT"

shot() { # shot <name> <path>
docker run --rm --network host -v "$OUT:/out" -w /out \
--entrypoint chromium-browser zenika/alpine-chrome:124 \
--headless --no-sandbox --disable-gpu --hide-scrollbars \
--window-size=1920,1080 --virtual-time-budget=12000 \
--screenshot="/out/$1.png" "$BASE$2" >/dev/null 2>&1
docker run --rm --memory 3g --shm-size 1g --network host \
-v "$OUT:/out" -w /out --entrypoint chromium-browser \
zenika/alpine-chrome:124 \
--headless --no-sandbox --disable-gpu --disable-dev-shm-usage \
--hide-scrollbars --window-size=1920,1080 --virtual-time-budget=15000 \
--user-agent="$UA" --screenshot="/out/$1.png" "$BASE$2"
echo "$1.png <- $BASE$2"
}

shot 01-home /
shot 01-hero "/tv-preview?hero=1"
shot 02-grid /tv-preview

echo
echo "Check both by eye before uploading: no cover art that is not ours, no real show titles."
ls -l "$OUT"/01-home.png "$OUT"/02-grid.png
echo "Look at both before uploading: no cover art that is not ours, no real show titles."
ls -l "$OUT"
2 changes: 1 addition & 1 deletion firetv/store/listing.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ podcasts, podcast player, radio, live tv, p0dcasters, podcasters, rss, free podc
| Icon 512x512 / 114x114 | out/icon-512.png, out/icon-114.png |
| Fire TV icon 1280x720 | out/firetv-icon-1280x720.png |
| Fire TV background 1920x1080 | out/firetv-background-1920x1080.png |
| Screenshots 1920x1080 | shots/01-home.png, 02-grid.png, 03-keyboard.png |
| Screenshots 1920x1080 | shots/01-hero.png, shots/02-grid.png |

### Screenshots must not carry anyone else's artwork

Expand Down
Binary file added firetv/store/shots/01-hero.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added firetv/store/shots/02-grid.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 30 additions & 3 deletions src/app/tv-preview/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,16 @@ const SAMPLE: Array<Pick<Podcast, "title" | "host" | "episode_count"> & { days:

const DAY = 86_400;

export default function TvPreviewPage() {
export default async function TvPreviewPage({
searchParams,
}: {
searchParams: Promise<{ hero?: string }>;
}) {
// ?hero=1 adds the real hero above the grid, for the first store screenshot.
// The home page cannot be used for one: below its hero it shows the "deep
// catalogues" row, which is real cover art, and two of those shows are the
// ones Amazon named in the rejection.
const hero = (await searchParams).hero === "1";
const now = Math.floor(Date.now() / 1000);
const shows = SAMPLE.map((s, i) => ({
id: i + 1,
Expand All @@ -65,7 +74,24 @@ export default function TvPreviewPage() {
})) as unknown as Podcast[];

return (
<main className="wrap">
<>
{hero && (
<div className="hero">
<div className="wrap">
<h1>Every podcast with a feed — on any host.</h1>
<p className="lede">
390,000 live shows, read from their RSS feeds: shows on their own domain,
shows on every hosting platform, and the big library networks. Free, no
account, listen on your TV.
</p>
<form className="searchbar" action="/search">
<input name="q" placeholder="Search podcasts…" aria-label="Search podcasts" />
<button type="submit">Search</button>
</form>
</div>
</div>
)}
<main className="wrap">
<section>
<h2 className="sec">Browse every show</h2>
<div className="grid">
Expand All @@ -74,6 +100,7 @@ export default function TvPreviewPage() {
))}
</div>
</section>
</main>
</main>
</>
);
}
Loading