DEV Community

ohan
ohan

Posted on

I counted 150 AI video prompts, then built an n8n workflow that posts one a day

Most "prompt of the day" bots are a random line of text in a Slack channel. Nobody reads them, because a prompt with no output next to it is just a sentence.

I wanted the opposite: one prompt a day, with the clip that prompt actually produced, and no credentials to set up. It turned into a small n8n workflow and a much more interesting question — what does a prompt that actually works look like, statistically?

The workflow

Six nodes, no API key, no account:

  1. Schedule trigger — once a day.
  2. HTTP Request — fetch the site's public sitemap.xml.
  3. Code — filter it down to prompt detail pages, pick one by day % total.
  4. HTTP Request — fetch that page as text.
  5. HTML — extract h1, the pre blocks, and the JSON-LD script tags.
  6. Code — pull the example video out of the VideoObject schema, assemble a Slack-flavoured message.
  7. HTTP Request — POST to a Slack or Discord webhook.

The source is a public library of Seedance 2 video prompts — 150 of them, each with the clip it generated.

Three things that made it work

Rotate, don't randomize. Math.floor(Date.now() / 86400000) % urls.length gives you one prompt per day that is stable across re-runs. Math.random() gives you duplicates within a week and a different result every time you debug.

const urls = [...xml.matchAll(/<loc>([^<]+)<\/loc>/g)]
  .map((m) => m[1].trim())
  .filter((u) => u.includes('/prompt/'))
  .filter((u) => !u.includes('/prompt/category/'));

const dayIndex = Math.floor(Date.now() / 86400000);
const url = urls[dayIndex % urls.length];
Enter fullscreen mode Exit fullscreen mode

Read the schema, not the layout. Every prompt page ships a VideoObject in JSON-LD with name, description and contentUrl. That is a stable contract; the rendered markup is not. Scraping the schema means a CSS refactor on their side does not silently break my workflow.

for (const raw of item.structuredData || []) {
  const parsed = JSON.parse(raw);
  const blocks = Array.isArray(parsed) ? parsed : [parsed];
  const found = blocks.find((b) => b['@type'] === 'VideoObject');
  if (found) { video = found; break; }
}
Enter fullscreen mode Exit fullscreen mode

Count your selectors before you trust index 0. Each page has two <pre> blocks: the Japanese original and the English version. cssSelector: "pre" with returnArray: false silently gives you the first one — so my first run posted Japanese into an English channel and looked completely fine. Set returnArray: true and choose explicitly.

The part I didn't expect: the prompts are short

Since I had all 150 in a list, I counted them. Median prompt length is 152 characters. Longest is 215.

That is roughly one sentence each for style, place, subject and motion. Every "advanced prompting guide" I have read implies the opposite — that control comes from length. In this corpus, it doesn't exist. Long prompts tend to contain contradicting instructions, and the model drops one of them, non-deterministically.

Element frequency across the 150:

Element Appears in
Camera / lens / framing 69 / 150 (46%)
Duration stated in seconds 63 / 150 (42%)
Lighting 55 / 150 (37%)
Cut structure 22 / 150 (15%)
Audio (ambience, SFX, dialogue, ASMR) 22 / 150 (15%)
Resolution (4K / 8K) 14 / 150 (9%)
Slow motion 4 / 150 (3%)

Two readings I'd stand behind:

  • Camera direction is not mandatory. 54% of these prompts never mention a camera. It earns its characters when distance changes the meaning of the shot — macro for texture, wide for scale — and otherwise it is filler.
  • Audio is the cheapest differentiator. Only 15% specify it, and it is most of the gap between "an AI clip" and "a finished shot".

A third of the corpus (34/150) splits the prompt by time range rather than describing one continuous action:

[00:00-00:05] intro, camera enters from behind the subject
[00:05-00:10] turn, light source switches
[00:10-00:15] wide, full scene
Enter fullscreen mode Exit fullscreen mode

That form clusters hard: sci-fi (9), cinematic (7), people (5) — and it is almost absent from scenery (2) and food (0). It shows up exactly where order carries meaning. For a single continuous shot, stating total duration is enough.

And 32 of 150 need a reference image rather than text alone. The tell is simple: the moment a prompt points at a specific person or a specific frame instead of a kind of thing, text can no longer specify it. That is the line between text-to-video and image-to-video.

Setup

  1. Create an Incoming Webhook in Slack, or a Webhook in a Discord channel.
  2. Paste it into the last node.
  3. Discord only: append /slack to the webhook URL — it then accepts the same { "text": ... } payload.
  4. Activate.

That's the whole thing. Everything it reads is public, so there is no credential step and nothing to rotate.

The prompts themselves are written against Seedance 2.5; if you have not run one before, the walkthrough is shorter than this post.

I wrote the full breakdown of all 150 — element frequency, duration patterns, the reference-image split by category — as a standalone reference (Japanese). There is also a Japanese write-up of the implementation on Qiita if that is your reading language.

If you build something similar against another corpus, the transferable part is not the scraping — it is checking whether the thing you are scraping publishes structured data first. It usually does, and it is usually more stable than the HTML around it.

Top comments (0)