Saving a YouTube workout and using it in a repeatable training system are different product problems.
A bookmark preserves a URL. A training workflow needs to preserve intent: which movement matters, where it starts, why it belongs in a routine, and what the user should do next.
While building TrainFlow, I have been exploring an architecture that treats timestamped video moments as reusable domain objects and separates desktop planning from mobile execution. This post explains the design decisions behind that workflow.
Disclosure: I am the maker of TrainFlow. The examples below describe the product and engineering patterns I used, not a generic recommendation disguised as a review.
1. Model the useful moment, not the whole video
A YouTube URL is a source reference, not an exercise definition. One video may contain a warm-up, three demonstrations, a progression, and a cooldown. The reusable unit is usually a moment inside the video.
A conceptual model can stay small:
type ExerciseAction = {
id: string;
title: string;
videoUrl: string;
startSeconds: number;
endSeconds?: number;
cue?: string;
role?: "warmup" | "skill" | "strength" | "conditioning" | "recovery";
};
The timestamp is stored as a number rather than as part of a formatted string. That makes validation, sorting, duration calculations, and player integration easier.
The original URL remains important. It preserves attribution and lets the user return to the full context. The action is a pointer into the source, not a copied replacement for it.
2. Normalize YouTube URLs at the boundary
YouTube links arrive in several shapes: standard watch URLs, short links, mobile shares, and links that already contain a t parameter.
Do not let every component interpret these independently. Normalize once when the user creates an action:
type NormalizedVideoRef = {
provider: "youtube";
videoId: string;
startSeconds: number;
};
A boundary function should:
- Parse the URL with the platform URL API.
- Accept only supported YouTube hosts.
- Extract and validate the video ID.
- Convert timestamp formats such as
90,1m30s, or existing query parameters into seconds. - Reject negative or non-finite values.
- Store canonical data rather than the original query-string shape.
This keeps player code boring. It receives a validated video ID and a number.
3. Separate authoring from execution
TrainFlow uses a Next.js App Router application with two distinct surfaces:
- a desktop dashboard for building actions and routines
- a mobile PWA for running a session
This separation is more than responsive CSS. The two contexts have different jobs.
Desktop authoring benefits from density. Users compare sources, edit notes, reorder actions, and see the whole routine. Mobile execution benefits from focus. It should show the current action, the relevant video moment, a small number of controls, and a clear next step.
Trying to force both jobs into one universal screen usually leaves the desktop view too sparse and the mobile view too busy.
The shared domain model still matters. Both surfaces should read the same action and routine data, but they can render different interaction models around it.
4. Make routines ordered compositions
Once actions are reusable, a routine becomes an ordered composition rather than a copied block of video metadata.
Conceptually:
type RoutineItem = {
actionId: string;
position: number;
noteOverride?: string;
};
type Routine = {
id: string;
title: string;
items: RoutineItem[];
};
Referencing an action avoids duplicating the source URL and timestamp every time it appears. A routine item can carry a small override when the session context differs, while the core action remains reusable.
Ordering should be explicit. Relying on creation time or database return order will eventually produce surprising sessions.
5. Treat workout execution as a state machine
A session is easier to reason about when it has explicit states. Even a small state machine is better than a collection of unrelated booleans.
For example:
type SessionStatus =
| "ready"
| "playing"
| "resting"
| "paused"
| "completed";
Events then define valid transitions:
-
START: ready → playing -
COMPLETE_ACTION: playing → resting or the next action -
RESUME: resting or paused → playing -
FINISH: playing → completed
This helps with edge cases. What happens if the app is backgrounded during rest? Can a completed session be resumed? Should the next clip preload? These questions become transition rules instead of scattered UI conditions.
Persist the minimum state needed to recover a session: the routine version, current item, status, and relevant timing data. A mobile PWA should assume interruptions are normal.
6. Keep AI output editable
AI can reduce the cost of turning a long video into structured actions. It can propose titles, timestamps, cues, or a first routine draft.
The important design choice is to keep those fields editable and visibly provisional.
Transcripts may be incomplete. A video can demonstrate several variations. Exercise names are not always standardized. An automated system also cannot decide whether a movement is appropriate for a specific person.
A reliable workflow is:
- AI produces a draft.
- The user reviews the source moment.
- The user edits the action.
- Only the reviewed version enters a routine.
Store provenance when it is useful, but do not make the interface feel like an audit log. The product goal is faster review, not blind automation.
7. Log only what supports the next decision
It is tempting to build a large workout-event schema immediately. Start smaller.
A useful first session record may contain:
- routine ID and version
- started and completed timestamps
- completed action count
- a short user note
- optional perceived difficulty
This is enough to answer practical questions: Did the user finish? How long did it take? What should change next time?
Additional metrics should earn their place by improving a future decision. Otherwise they add friction to the execution flow.
8. Design for link durability
External video content can change. A video may become unavailable, timestamps may no longer match an edited source, or a creator may restrict playback.
The application should fail gracefully:
- keep the action title and user notes available
- show a clear source-unavailable state
- allow the URL or timestamp to be repaired
- never silently replace the source
- avoid claiming ownership of third-party content
Durability comes from preserving the user’s structure even when an external dependency fails.
The larger pattern
This architecture applies beyond workouts. Any workflow built from long-form media can benefit from the same layers:
- normalize the source
- capture a precise moment
- turn it into a reusable object
- compose objects into an ordered plan
- execute with a focused state machine
- record the minimum useful result
The key is to stop treating saved content as completed work. A bookmark is only the beginning of a workflow.
For TrainFlow, that means turning YouTube timestamps into actions, actions into routines, and routines into sessions that are practical to run on a phone. The technical stack matters, but the product boundary matters more: authoring and execution are related tasks, not the same interface.
Top comments (0)