Skip to content
streamneo.
Setup Guides10 min read

How to Set FFmpeg to Shuffle Children’s Videos Without Repeating an Episode Too Soon

Create a shuffled FFmpeg concat list, check its loop boundary and understand what a one-pass shuffle can—and cannot—guarantee.

sn.
StreamNeoPublished 4 October 2026
Worth sharing?

FFmpeg does not shuffle entries in a concat list: it processes the files in the order written. To shuffle children’s videos, generate a randomized list before starting FFmpeg, then check its last and first entries if you plan to loop it.

A shuffle without replacement plays each listed file once per pass. It does not provide a configurable cooldown across multiple passes; for that, you need an external scheduler that remembers recent episodes. The distinction matters if you want to avoid an episode appearing again too soon, rather than simply avoid duplicates within one pass.

Why FFmpeg follows the list rather than shuffling it

The concat demuxer takes a text file containing file entries and reads them one after another. Its job is to join media inputs, not decide which episode should play next. FFmpeg's documentation describes this sequential behaviour; it does not offer a built-in shuffle or minimum-repeat-interval setting for a concat list. See the FFmpeg concat demuxer documentation for the input format and its constraints.

That means a folder full of videos is not, by itself, a playlist with random order. If a script writes the names alphabetically, FFmpeg will play them alphabetically every time you run it. If it writes a randomized order, FFmpeg will follow that order every time it reads that script.

This separates the work into two parts: choose and validate an order outside FFmpeg, then ask FFmpeg to process that order. It is a useful way to reason about the setup, because a repeat problem may come from how the playlist was generated, not from the media command. If you are comparing ways to keep a recorded channel live, this guide to looping recorded bhajan videos in OBS covers a different playback path and its own practical considerations.

Prepare and validate the episode file list

Keep the episodes for this playlist in a dedicated directory. A focused directory makes it easier to see which files the script will include, avoid accidentally mixing in trailers or test renders, and run the same process again when the library changes. Use a consistent file type where practical, but do not assume that a shared extension means the media streams inside are compatible.

Before shuffling, decide what counts as an episode. A file-level list treats two copies of the same story as two entries, even if one has a different filename or encoding. If children should not hear the same episode again soon, label alternate exports or duplicate copies with one shared episode identity and remove duplicates from the candidate pool. Otherwise the order can be random and still repeat a story close together.

Check that the list is not empty and contains at least two distinct episode identities if you want a meaningful shuffle. With only one eligible episode, there is no way to avoid a repeat when the playlist loops. With a small library, any long cooldown becomes harder to satisfy because there may not be enough different episodes to fill the intervening slots.

Also check names and paths. The simple example below assumes portable filenames and is run from the directory containing videos/. Spaces and special characters need the concat script's own quoting or escaping rules; shell quoting a command is not a substitute for escaping a path inside the script. FFmpeg documents both safe path handling and concat-file escaping in the concat demuxer section.

A separate list is useful for review. For example, before making the playlist, note each filename alongside its episode identity, duration, and whether it is an approved final export. That small check can catch a draft, a duplicate, or an episode with the wrong ending before it enters an overnight broadcast.

Shuffle once without replacement

For one pass in which every file appears once, shuffle the list before writing it. Python's random.shuffle changes the order of a list in place; random.sample can instead return a new list selected without replacement. The Python random module documentation explains both operations.

Here is a compact example for a folder named videos beside the script. It rejects a too-small file list, randomises the order, and checks the loop boundary before writing the concat file:

from pathlib import Path
import random

files = sorted(Path("videos").glob("*.mp4"))
if len(files) < 2:
    raise SystemExit("Put at least two episode files in videos/")

random.shuffle(files)

# Avoid an immediate repeat at the boundary between loop passes.
while files[-1] == files[0]:
    random.shuffle(files)

with open("playlist.ffconcat", "w", encoding="ascii") as playlist:
    playlist.write("ffconcat version 1.0\n")
    for path in files:
        playlist.write(f"file {path.as_posix()}\n")

The initial sort is not what makes the final order random; it gives the input list a predictable starting state for review and repeat runs. The shuffle then changes that order. Each file remains present exactly once in that generated pass, because the operation permutes the list rather than drawing one file repeatedly.

The boundary loop is deliberately narrow. It tests whether the last item and first item are the same path, and reshuffles if they are. With distinct paths, equality at both ends should not persist after a genuine shuffle. If you are deduplicating by episode identity rather than path, compare identities in this check too; differently named encodes of the same episode must be treated as equal for this purpose.

A generated order is not a promise that each run will be different, nor a rule that an episode will stay away for a particular number of later episodes. It means only that the entries in this pass have been reordered without replacement. Keep the generated playlist so you can inspect exactly what FFmpeg will consume.

Write the shuffled order to a concat script

The script writes a header, ffconcat version 1.0, and then one file directive per path. FFmpeg's concat demuxer reads those directives in order. The header is useful because it identifies the script format; do not insert unrelated text or rely on the order in which directory listings happen to appear.

The -safe 1 option in the command below allows the relative, portable paths used by this example. This is a cautious default for a local playlist. If you must use absolute paths or names outside the safe character set, learn the documented path rules and escaping syntax before changing the option. -safe 0 permits unsafe paths; use it only with trusted local inputs, not as a general fix for a malformed list.

For a one-time combined output file, try:

ffmpeg -f concat -safe 1 -i playlist.ffconcat -c copy shuffled.mp4

Stream copy avoids re-encoding when the inputs are suitable for the same output. The concat demuxer expects the files to have matching streams, codecs, and time bases. If one episode has different audio, video, or timing characteristics, a copy operation may fail or produce problems at transitions. The FFmpeg FAQ explains the distinction between concat demuxer use and filter-based concatenation where re-encoding may be needed.

Do not treat -c copy as a universal compatibility switch. If the sources differ, first inspect and normalise them to a consistent format, or use a filter-based workflow that decodes and re-encodes as appropriate. That choice costs processing time and can affect quality, but gives you control to bring mismatched material into a common output format. Keep an original copy of the episodes while testing any conversion.

The playlist also relies on plausible durations. FFmpeg uses durations to position the next file's timestamps, so inaccurate source durations can lead to timestamp gaps or artifacts. If transitions go wrong despite a correctly ordered list, investigate media compatibility and duration metadata before assuming the shuffle itself caused the fault.

Loop the concat input and inspect the boundary

To repeat the whole concat input indefinitely, put the input option -stream_loop -1 before the input declaration:

ffmpeg -stream_loop -1 -f concat -safe 1 -i playlist.ffconcat -c copy output.mp4

The FFmpeg main options documentation describes -stream_loop as an input option, with -1 for infinite looping and 0 for no loop. The concat input still follows the written order on every pass. Unless the playlist changes, looping repeats the same sequence rather than generating a fresh shuffle each time.

That has an important boundary: the final entry of one pass is followed by the first entry of the next. A list can contain every episode just once and still place the same episode at both ends if duplicates were included under different names, or if the list-generation logic does not check the boundary. Inspect the first and last entries after generation, and compare episode identities if the story matters more than the file path.

The example's boundary check prevents that immediate repeat for distinct file paths. It does not establish a longer spacing rule. For instance, an episode near the beginning can still appear near the beginning again after the playlist completes. A one-pass shuffle guarantees one appearance per file in that pass; a boundary check prevents one adjacent repeat at the seam; neither creates a multi-episode cooldown across repeated cycles.

If you need a cooldown across cycles, maintain recent episode history outside FFmpeg. A scheduler can select only episodes not present in its recent-history window, then write a new concat list. Its history must persist across process restarts, or a restart may forget what just played. The library also needs enough distinct episode identities to fill the required history while still leaving an eligible choice. This is a scheduling rule you implement, not a setting in FFmpeg.

For an always-on YouTube channel, consider how the playlist is launched and monitored as well as how its order is made. A locally run FFmpeg command depends on the computer, power, network, and process remaining available. If you are weighing a VPS approach for a children's playlist, the DigitalOcean India setup guide discusses that operating choice; the shuffle and boundary logic still belongs in your generated list.

Test the sequence before running it continuously

First, inspect playlist.ffconcat as text. Confirm the header is present, every intended episode appears once, no unwanted file has slipped in, and the first and last episode identities differ. This catches list-generation mistakes without waiting for a full pass. Keep the file with the code or notes that generated it so the order can be reproduced and audited.

Next, run a short local test or make a one-time output and check the transitions, including the last-to-first transition. Listen and watch around cuts for black frames, missing audio, abrupt time jumps, or a child-friendly episode ending followed by an unintended intro. A file list can be correct while the media itself has a bad cut or mismatched audio layout.

If a short test works, that is evidence about that specific list and those inputs; it is not a guarantee that every later playlist will work. Regenerate and recheck whenever you add, remove, replace, or rename files. If you use a script to run continuously, make sure the generated playlist is final before the process starts, rather than editing it mid-broadcast and assuming FFmpeg will adopt the change.

Keep operational responsibilities separate. A local setup means you need to keep the computer awake, maintain the connection, and notice when the process stops. StreamNeo removes the specific burden of leaving that computer on by taking an uploaded file and stream key and running the YouTube broadcast from the cloud, but it is YouTube-only and does not replace checking your episode order, rights, or stream settings. If a live stream is not yet enabled on the channel, review how to verify an Indian YouTube channel for live streaming and check YouTube's current official guidance.

Before committing, compare the operating options on the pricing page. When the file and channel are ready, start free — 24-hour trial, no card.

FAQ

Can FFmpeg shuffle a concat list by itself?

No. The concat demuxer reads its entries in the order written, and FFmpeg does not provide a built-in shuffle or minimum-repeat interval setting for that list. Generate the order before passing the script to FFmpeg.

Does a shuffled list stop an episode repeating too soon?

It prevents a file from appearing twice within one pass when you shuffle the list without replacement. It does not enforce a cooldown over later passes. To do that, use an external scheduler that remembers recent episode identities and has enough eligible episodes to choose from.

Why check the first and last entries?

When the concat input loops, the last entry of one pass is followed by the first of the next. If those are the same episode, the loop creates an immediate repeat even though each entry appeared only once within the pass. Compare episode identities, not just filenames, if alternate copies count as the same story.

What if the videos do not concatenate cleanly?

The concat demuxer expects matching stream layouts, codecs, and time bases, and inaccurate durations can affect timestamp placement. Inspect the files and normalise incompatible sources, or use an appropriate filter-based concat workflow that re-encodes as needed.

YOU’VE REACHED THE END

Keep the ideas coming.

More guides, useful tools and a little help for your next broadcast.

Back to the journal ↗
YOUR NEXT READ

A little more to explore.

More Setup Guides guides ↗ · All topics ↗