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

> Source: <https://dev.to/kavel/i-counted-150-ai-video-prompts-then-built-an-n8n-workflow-that-posts-one-a-day-3jme>
> Published: 2026-09-07 14:00:21+00:00

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?

Six nodes, no API key, no account:

`sitemap.xml`.` day % total`.` h1`, the `pre` blocks, and the JSON-LD `script` tags.`VideoObject` schema, assemble a Slack-flavoured message.
The source is a [public library of Seedance 2 video prompts](https://emaki.ai/prompt?ref=devto) — 150 of them, each with the clip it generated.

**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.

``` js
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];
```

**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.

``` js
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; }
}
```

**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.

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:

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
```

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](https://emaki.ai/video?ref=devto) and [image-to-video](https://emaki.ai/image-to-video?ref=devto).

`/slack` to the webhook URL — it then accepts the same `{ "text": ... }` payload.
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](https://emaki.ai/seedance-2-5?ref=devto); if you have not run one before, the [walkthrough](https://emaki.ai/how-to-use-seedance?ref=devto) 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](https://hanshs474.gitbook.io/seedance-2-prompt-guide/) (Japanese). There is also a [Japanese write-up of the implementation](https://qiita.com/hanshs474/items/9781f2a83abac5f260ae) 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.
