FFmpeg’s concat demuxer plays files in the order listed in a script; it does not shuffle them or prevent repeats. To avoid the same children’s episode playing twice in a row, generate an ordered list that enforces that rule first, then give the list to FFmpeg.
This guide takes you from choosing eligible episode files to writing and checking an ffconcat script. The rule applies within the order you generate. A continuing playback setup must also carry the last episode across each playlist boundary.
Understand what the concat demuxer does
The concat demuxer is a way to present several media files to FFmpeg as one continuous input. It reads a script containing file entries and processes them sequentially. If the script lists Episode A, Episode B and Episode C, that is the order FFmpeg uses. It does not make a random choice between entries.
That distinction matters because the no-immediate-repeat rule is an ordering constraint, not a concatenation setting. There is no concat-demuxer flag that can infer which files are episodes or decide that the previous episode should be excluded. You need to create the order before the FFmpeg command runs.
The FFmpeg project’s concat demuxer documentation describes the script-based input and its sequential file handling. Check the documentation that matches your installed FFmpeg version as well: the project notes that its online documentation is regenerated, and older local installations may differ.
A useful mental model is to separate the job into two stages:
| Stage | Responsibility | Example |
|---|---|---|
| Playlist generation | Selects and orders eligible episodes, enforcing no adjacent duplicate | A, C, B, A |
| Concatenation | Reads that order and joins compatible media files | FFmpeg consumes the script from top to bottom |
If you are not yet sure why two files that look alike behave differently, first review containers, codecs and decoding. The spaces inside that link are not valid markdown; use this direct link instead: video containers and codecs. Matching file extensions alone do not establish that the media streams are suitable for stream-copy concatenation.
Collect eligible episode files
Begin with the files you actually intend to play in this run. Keep the input set explicit rather than asking a script to include every file in a directory. A folder may contain a trailer, an unfinished export, a duplicate, a thumbnail, or an episode you do not want in the current rotation. If one slips into the list, the shuffle can work exactly as designed and still produce the wrong channel sequence.
For a small collection, make a plain inventory with one path per episode. Use stable names that distinguish episodes, such as season and episode numbers, rather than relying on the order a file browser happens to display. If you have multiple versions of an episode, decide whether they count as the same episode for the rule. If they do, represent that shared identity in your ordering logic so that two encodes of the same episode do not land next to each other.
A simple inventory might look like this:
/media/Bluey/S01E01.mp4
/media/Bluey/S01E02.mp4
/media/Bluey/S01E03.mp4
/media/Puffin Rock/S01E01.mp4
The paths are examples, not a required directory layout. Store the inventory somewhere separate from the generated playlist so you can regenerate an order without accidentally shuffling yesterday’s output as though it were an episode.
Decide whether each eligible item should appear once or may appear more than once. A one-time compilation normally uses each selected file once. A recurring stream may need a longer planned sequence, which can involve repeat counts. The constraint is the same in either case: adjacent entries must identify different episodes. If the schedule draws from a small set, a no-repeat order can still contain frequent repeats with other episodes in between; it does not promise that an episode appears only once overall.
Before moving on, check that every path exists, is readable by the account running FFmpeg, and points to a completed media file. This avoids confusing a missing input with a shuffle or concat problem. If the channel is intended to continue beyond one exported file, distinguish that ongoing playback goal from making a single finite compilation; a replay-to-always-on channel workflow covers a different operational question.
Choose a constrained shuffle approach
There are two practical ways to produce a no-adjacent-repeat order. For a modest one-off collection with each file used once, shuffle the list and check it. If the last item is the same episode as the next item, reshuffle or repair the order, then check again. This is straightforward, but it can be inefficient or fail to find a valid order when the collection has an awkward composition.
A more deliberate approach builds the sequence one position at a time. At each step, choose from the remaining eligible episodes other than the episode in the previous position. If only one eligible item remains and it is the same as the previous one, the construction has reached a dead end. A robust implementation should detect that case and backtrack, restart from a different choice, or report that no valid ordering is available rather than silently violating the rule.
When every episode is used once, a valid ordering exists if the most frequent episode identity is not too numerous relative to all the other entries: its count must be no greater than the number of alternatives plus one. This is a feasibility condition, not a promise that a naive greedy choice will always finish successfully. For example, a heavily repeated episode can be separated by other episodes only as long as enough alternatives remain to put gaps between its appearances.
With repeat counts, treat each copy as a remaining count for an episode identity, not as an unrelated file. At each pick, exclude the identity used in the previous position, then choose among identities with counts remaining. If your selection method gets stuck, retry or use a method that checks whether the remaining counts can still fit around one another. Otherwise a locally valid choice can leave an impossible final position.
For a household playlist, you may not need a sophisticated algorithm. A script that shuffles, verifies all adjacent pairs and retries can be enough when the collection has plenty of distinct episodes. For a production schedule, prefer logic that detects infeasibility and records the generated order. The important property is not whether the randomisation is clever; it is whether the final list has been checked against the rule.
Prevent the immediately previous episode
Define “same episode” before writing code. Comparing full paths is sufficient only when every episode has exactly one file. If alternate resolutions, dubbed versions or remastered exports share a folder, path comparison treats them as different even if you want them considered the same episode. In that case, assign each file an episode ID and compare IDs when enforcing the constraint.
For a list where every episode is used once, the validation rule is simple: for each pair of neighbouring entries, confirm that their episode IDs differ. In pseudocode:
for position from 1 to length(order) - 1:
if episode_id(order[position]) == episode_id(order[position + 1]):
reject order
Indexing conventions differ between programming languages, so adapt the positions to the language you use. The comparison itself is the important part. Validate the completed order, not just the selection step: a bug in a later sorting, filtering or playlist-writing stage could otherwise reintroduce an adjacent repeat.
For an ongoing rotation, the first entry of the next batch must be compared with the last episode of the previous batch. Checking only within each batch misses a boundary repeat. Keep the last played episode ID as state and pass it into the next ordering run, or generate a single longer sequence when that better suits your player.
An empty set cannot produce a playlist, and a single episode cannot be arranged without an immediate repeat if it must appear more than once consecutively in the sequence. These are not FFmpeg errors; they are input conditions for the ordering problem. Decide whether to stop, wait for more eligible content or allow a repeat as an explicit exception. Do not claim the output is repeat-free if the generator has made an exception.
A finite exported video only contains the order you generated. After it reaches its end, it cannot invent a fresh random choice. If your real goal is an indefinitely running channel, your playback process must choose or receive another item or playlist and apply the same boundary rule. That is separate from producing one concatenated output file.
Write the ordered ffconcat script
Once the sequence passes validation, write its paths to a text file in the exact intended order. An ffconcat script begins with this exact first line:
ffconcat version 1.0
Then put one file directive on each line. For example:
ffconcat version 1.0
file '/media/Bluey/S01E01.mp4'
file '/media/Puffin Rock/S01E02.mp4'
file '/media/Bluey/S01E03.mp4'
The header must be first, with no blank line or other text ahead of it, for automatic format recognition. The order in the file is the order the demuxer consumes. Therefore, do not rely on the script writer to reorder, randomise or correct anything after it receives the validated sequence.
Paths may contain spaces or special characters. Use the quoting and escaping rules documented for the concat script syntax rather than assuming that ordinary shell quoting applies inside the file. A path such as /media/Stories for Bedtime/episode one.mp4 needs to be represented as a valid script directive. If your generator writes paths itself, test it with a filename containing a space and any punctuation present in your real collection. Avoid hand-editing a generated order after validation unless you validate it again.
Keep the inventory, generated order and ffconcat script distinct where practical. The inventory answers what can be selected; the order records the particular run; and the ffconcat file is the FFmpeg input representation. This makes it easier to inspect a bad sequence, reproduce a run, and identify whether the problem began in selection, ordering or script escaping.
Run FFmpeg with the script
For compatible inputs where you want to avoid re-encoding, a typical command is:
ffmpeg -f concat -safe 0 -i playlist.ffconcat -map 0 -c copy shuffled.mp4
-f concat selects the demuxer, -i supplies the script, and -c copy asks FFmpeg to copy the input streams rather than encode them again. -safe 0 permits paths that do not meet the demuxer’s default safe-path restrictions; use it only with a script you control and trust. It is not a shuffle setting and does not affect the adjacency rule.
Stream copy is useful when the files have compatible stream layouts and properties. The demuxer expects inputs to match in relevant ways, including codecs and time bases. Files with the same .mp4 suffix can still differ in codec, dimensions, frame rate, audio layout or timestamp behaviour. If FFmpeg reports errors or the joined output has glitches, inspect the inputs rather than assuming the ordering code is at fault. The FFmpeg project’s FAQ on concatenating video files describes the demuxer and filter approaches.
| Route | When it fits | Trade-off |
|---|---|---|
| Concat demuxer with stream copy | Inputs are suitable for concatenation and preserving their existing streams is useful | Requires compatible streams and reliable timing information |
| Concat filter with re-encoding | Inputs need conversion or a filter-based join is appropriate | Encoding takes additional processing and may require choosing output settings |
If the files are not compatible for stream copy, use a concat-filter workflow and re-encode as appropriate. The FFmpeg FAQ recommends the filter route when re-encoding is needed and discusses the demuxer when avoiding re-encoding is desired and the formats permit it. Do not switch routes expecting the filter to perform the no-repeat shuffle: the ordered inputs still need to be prepared first.
Duration information also matters. Incorrect duration values can lead to timestamp gaps or other artefacts in a concatenated result. If the relevant file durations are not being inferred correctly, review the demuxer documentation for duration directives or consider normalising the inputs before joining. Test a short output before relying on the full programme.
Validate the sequence and the result
Validation has two separate parts. First validate the playlist rule: every adjacent pair has different episode IDs, including the boundary to any previous or following batch. Then validate the media output: the resulting file plays, transitions occur in the expected order, and audio and video remain in sync through the joins.
A useful checklist before a long run is:
- Confirm that the eligible-file inventory contains only intended episodes.
- Confirm that the generated list contains the expected entries and no accidental omissions or duplicates.
- Compare each adjacent episode ID, not merely the displayed filename, if multiple files can represent the same episode.
- Inspect the first and last entries and, for recurring playback, check the transition between batches.
- Open or play a short output and listen around several joins for gaps, abrupt audio changes or timestamp artefacts.
- Keep the generated order and FFmpeg output log so you can reproduce or diagnose a problem.
A no-repeat check says nothing about whether a clip is suitable for broadcast, whether audio levels match, or whether an output will play cleanly. Those are separate editorial and technical checks. If this playlist is one part of a continuous YouTube broadcast, plan the transition between playlist outputs as carefully as the transitions between episodes. A guide to keeping FFmpeg running through a long YouTube radio broadcast addresses continuity at the broadcast-process level, which is distinct from choosing episode order.
For a one-time file, record the validated order so a later export can be compared with it or regenerated. For a dynamic player, decide how it will receive the next playlist and how it will remember the previous episode ID. If it cannot update its playlist safely while playing, you may need a different control process. The exact integration depends on the player; the concat demuxer itself reads the script you provide and does not manage an indefinite stream of new choices.
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
Does FFmpeg’s concat demuxer shuffle the episodes?
No. It processes the files in the order given in the ffconcat script. Generate and check the order before invoking FFmpeg.
Does this prevent an episode from appearing more than once?
No. The rule only prevents the same episode from appearing in adjacent positions. An episode can appear again later, and a repeat-count schedule must still pass the same neighbour check.
Can I use -c copy with every collection of MP4 files?
No. A shared file extension does not guarantee matching codecs, stream layouts or timing properties. Check the inputs and test the output; use a filter and re-encode when the files need conversion or are not suitable for stream-copy concatenation.
Will one generated file keep choosing new episodes after it ends?
No. It contains a finite, fixed order. An ongoing playback setup needs a process that supplies the next item or playlist and carries the last episode across the boundary so the same rule continues to apply.