Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
120 commits
Select commit Hold shift + click to select a range
ea6ea84
Add recording mutation domain foundation
hotzenplotz5 Jul 13, 2026
155d489
Implement recording trash planner foundation
hotzenplotz5 Jul 13, 2026
3de31c2
Build recording mutation foundation
hotzenplotz5 Jul 13, 2026
a8ff0ae
Fix Makefile regression from mutation build wiring
hotzenplotz5 Jul 13, 2026
811a963
Add recording analysis provider interfaces
hotzenplotz5 Jul 13, 2026
b3c8778
Implement recording lookup and replay analysis
hotzenplotz5 Jul 13, 2026
ea48fb9
Model unknown recording handler analysis state
hotzenplotz5 Jul 13, 2026
08543c7
Block trash planning on unknown handler state
hotzenplotz5 Jul 13, 2026
163721a
Build recording analysis foundation
hotzenplotz5 Jul 13, 2026
231f780
Add recording handler analysis provider
hotzenplotz5 Jul 13, 2026
e23297e
Implement recording handler usage analysis
hotzenplotz5 Jul 13, 2026
83f1d37
Match upstream Makefile whitespace exactly
hotzenplotz5 Jul 13, 2026
2f3cf48
Split local and remote timer analysis constraints
hotzenplotz5 Jul 13, 2026
b6abbcc
Block trash planning on unknown local or remote timer state
hotzenplotz5 Jul 13, 2026
df51fe6
Add local recording timer analysis provider
hotzenplotz5 Jul 13, 2026
6cabe67
Implement local recording timer analysis
hotzenplotz5 Jul 13, 2026
303193c
Add remote recording timer analysis provider
hotzenplotz5 Jul 13, 2026
04a881f
Implement remote recording timer analysis
hotzenplotz5 Jul 13, 2026
2efae8c
Add EPGSearch recording origin analysis provider
hotzenplotz5 Jul 13, 2026
019927d
Implement SearchTimer origin analysis
hotzenplotz5 Jul 13, 2026
9f5b984
Add recording trash preflight service model
hotzenplotz5 Jul 13, 2026
9df11a6
Implement recording trash preflight service
hotzenplotz5 Jul 13, 2026
ae46d66
Build recording trash preflight service
hotzenplotz5 Jul 13, 2026
c7ca4fa
Add recording trash preview HTTP service
hotzenplotz5 Jul 13, 2026
2d7f049
Implement recording trash preview HTTP service
hotzenplotz5 Jul 13, 2026
c093782
Register recording trash preview service header
hotzenplotz5 Jul 13, 2026
88d213d
Register recording trash preview HTTP route
hotzenplotz5 Jul 13, 2026
877a3e4
Build recording trash preview HTTP service
hotzenplotz5 Jul 13, 2026
90d4e0d
Add deterministic trash preflight state fingerprints
hotzenplotz5 Jul 13, 2026
dcb0b9c
Add recording trash execution gate model
hotzenplotz5 Jul 14, 2026
15ab57f
Implement recording trash execution gate
hotzenplotz5 Jul 14, 2026
5a178db
Build recording trash execution gate
hotzenplotz5 Jul 14, 2026
8092bde
Add recording trash validation HTTP service
hotzenplotz5 Jul 14, 2026
4daef52
Implement recording trash validation HTTP service
hotzenplotz5 Jul 14, 2026
d8e53b5
Register recording trash validation service header
hotzenplotz5 Jul 14, 2026
0020294
Register recording trash validation HTTP route
hotzenplotz5 Jul 14, 2026
acd5026
Build recording trash validation HTTP service
hotzenplotz5 Jul 14, 2026
41f9274
Add safe recording trash executor model
hotzenplotz5 Jul 14, 2026
6a88d2b
Implement safe normal-case recording trash executor
hotzenplotz5 Jul 14, 2026
bd8c991
Build safe recording trash executor
hotzenplotz5 Jul 14, 2026
1d4540e
Include VDR video disk usage declaration
hotzenplotz5 Jul 14, 2026
39817f8
Add recording trash HTTP service
hotzenplotz5 Jul 14, 2026
d4cc424
Implement recording trash HTTP service
hotzenplotz5 Jul 14, 2026
98437ff
Register recording trash service header
hotzenplotz5 Jul 14, 2026
09424f7
Register recording trash HTTP route
hotzenplotz5 Jul 14, 2026
24cbc91
Build recording trash HTTP service
hotzenplotz5 Jul 14, 2026
845b24f
Keep recording trash Makefile wiring minimal
hotzenplotz5 Jul 14, 2026
21b3e94
Model idempotent recording trash results
hotzenplotz5 Jul 14, 2026
b184467
Harden recording trash postconditions and retries
hotzenplotz5 Jul 14, 2026
b9d6f34
Return idempotent recording trash success
hotzenplotz5 Jul 14, 2026
ba6ff8b
Handle local recording timer marker in trash analysis
hotzenplotz5 Jul 14, 2026
c51cbbd
Resolve recording timer markers against local timers first
hotzenplotz5 Jul 14, 2026
089bde1
Do not classify local recording timers as remote
hotzenplotz5 Jul 14, 2026
d244397
Document safe native recording trash API
hotzenplotz5 Jul 14, 2026
d1bd3b1
Add recording move mutation plan foundation
hotzenplotz5 Jul 14, 2026
1d69dd1
Implement recording move mutation planning
hotzenplotz5 Jul 14, 2026
4f203a6
Test recording move mutation planning
hotzenplotz5 Jul 14, 2026
c8c8b0a
Wire recording move planner test
hotzenplotz5 Jul 14, 2026
1fcb192
Add recording move analyzer contract
hotzenplotz5 Jul 14, 2026
ea13917
Implement recording move analysis
hotzenplotz5 Jul 14, 2026
fe2dafb
Test recording move analysis
hotzenplotz5 Jul 14, 2026
a0880a3
Wire recording move analysis test
hotzenplotz5 Jul 14, 2026
ec8491c
Keep recording move Makefile wiring minimal
hotzenplotz5 Jul 14, 2026
4cc2df3
Tests: keep recording move analyzer bound to mutable fakes
hotzenplotz5 Jul 14, 2026
7046bfe
Add recording move preflight service contract
hotzenplotz5 Jul 14, 2026
26d2243
Implement recording move preflight service
hotzenplotz5 Jul 14, 2026
ce0dee5
Test recording move preflight service
hotzenplotz5 Jul 14, 2026
0e2afd7
Wire recording move preflight test
hotzenplotz5 Jul 14, 2026
17b5674
Tests: link recording analysis into move preflight test
hotzenplotz5 Jul 14, 2026
371b52e
Split move preflight implementation from trash preflight
hotzenplotz5 Jul 14, 2026
b9024ee
Add isolated recording move preflight implementation
hotzenplotz5 Jul 14, 2026
b39f2c6
Isolate recording move preflight build wiring
hotzenplotz5 Jul 14, 2026
9922523
Add recording move preview HTTP contract
hotzenplotz5 Jul 14, 2026
cd06508
Implement recording move preview HTTP service
hotzenplotz5 Jul 14, 2026
59d1de5
Wire recording move preview service build
hotzenplotz5 Jul 14, 2026
2f1d701
Register recording move preview service header
hotzenplotz5 Jul 14, 2026
44da3be
Register recording move preview HTTP route
hotzenplotz5 Jul 14, 2026
8a9d23f
Add recording move execution gate contract
hotzenplotz5 Jul 15, 2026
c66f0c2
Implement recording move execution gate
hotzenplotz5 Jul 15, 2026
5750a08
Add recording move validate HTTP contract
hotzenplotz5 Jul 15, 2026
54fae30
Add recording move execution gate contract
hotzenplotz5 Jul 15, 2026
aca328e
Implement recording move validate HTTP service
hotzenplotz5 Jul 15, 2026
df5deda
Revert duplicate recording move gate declaration
hotzenplotz5 Jul 15, 2026
52f84e5
Test recording move execution gate
hotzenplotz5 Jul 15, 2026
75b135a
Wire recording move validation build and test
hotzenplotz5 Jul 15, 2026
92ed636
Register recording move validate service header
hotzenplotz5 Jul 15, 2026
e764934
Register recording move validate HTTP route
hotzenplotz5 Jul 15, 2026
226e4dc
Add safe recording move executor contract
hotzenplotz5 Jul 15, 2026
f16dfb0
Implement safe recording move execution
hotzenplotz5 Jul 15, 2026
aa315de
Add safe recording move HTTP contract
hotzenplotz5 Jul 15, 2026
710f598
Implement safe recording move HTTP service
hotzenplotz5 Jul 15, 2026
cd81910
Wire safe recording move execution build
hotzenplotz5 Jul 15, 2026
f2d1faa
Register safe recording move service header
hotzenplotz5 Jul 15, 2026
66881ed
Register safe recording move HTTP route
hotzenplotz5 Jul 15, 2026
3a9727d
Document safe native recording move API
hotzenplotz5 Jul 15, 2026
e0a897b
Add recording rename plan contract
hotzenplotz5 Jul 15, 2026
cb0295b
Implement recording rename planning
hotzenplotz5 Jul 15, 2026
7603423
Test recording rename planning
hotzenplotz5 Jul 15, 2026
558a04e
Wire recording rename planner test
hotzenplotz5 Jul 15, 2026
2f67c3d
Add recording rename preflight contract
hotzenplotz5 Jul 15, 2026
3722d68
Implement recording rename preflight service
hotzenplotz5 Jul 15, 2026
2174ac4
Test recording rename preflight service
hotzenplotz5 Jul 15, 2026
e295b80
Wire recording rename preflight test
hotzenplotz5 Jul 15, 2026
3666803
Fix recording rename preflight constraint names
hotzenplotz5 Jul 15, 2026
10a8d3a
Add recording rename preview HTTP contract
hotzenplotz5 Jul 15, 2026
d50a4bb
Implement recording rename preview HTTP service
hotzenplotz5 Jul 15, 2026
e03b91a
Wire recording rename preview service build
hotzenplotz5 Jul 15, 2026
d7cad7d
Register recording rename preview service header
hotzenplotz5 Jul 15, 2026
9acf184
Register recording rename preview HTTP route
hotzenplotz5 Jul 15, 2026
0603da7
Add recording rename validate HTTP contract
hotzenplotz5 Jul 15, 2026
eb5abe3
Implement recording rename validate HTTP service
hotzenplotz5 Jul 15, 2026
a85100f
Wire recording rename validate service build
hotzenplotz5 Jul 15, 2026
cb24c27
Register recording rename validate service header
hotzenplotz5 Jul 15, 2026
8f936a3
Register recording rename validate HTTP route
hotzenplotz5 Jul 15, 2026
690b97e
Add safe recording rename HTTP contract
hotzenplotz5 Jul 15, 2026
424eb72
Implement safe recording rename HTTP service
hotzenplotz5 Jul 15, 2026
7402574
Wire safe recording rename execution build
hotzenplotz5 Jul 15, 2026
0305dba
Register safe recording rename service header
hotzenplotz5 Jul 15, 2026
aa02e83
Register safe recording rename HTTP route
hotzenplotz5 Jul 15, 2026
d7c7550
Document safe native recording rename API
hotzenplotz5 Jul 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 55 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ LIBS += $(shell pkg-config --libs Magick++)
CXXFLAGS += -DUSE_LIBMAGICKPLUSPLUS
endif

OBJS = $(PLUGIN).o serverthread.o tools.o info.o searchtimers.o channels.o events.o recordings.o remote.o timers.o changestate.o eventsstreamthread.o changestatetracker.o scraper2vdr.o statusmonitor.o osd.o jsonparser.o epgsearch.o wirbelscan.o webapp.o femon.o
OBJS = $(PLUGIN).o serverthread.o tools.o info.o searchtimers.o channels.o events.o recordings.o recordingmutation.o recordinganalysis.o recordingmoveanalysis.o recordingrenameplan.o recordingrenamepreflight.o recordingpreflight.o recordingmovepreflight.o recordingpreview.o recordingmovepreview.o recordingrenamepreview.o recordingexecution.o recordingmoveexecution.o recordingvalidate.o recordingmovevalidate.o recordingrenamevalidate.o recordingmoveexecutor.o recordingmove.o recordingrename.o recordingtrashexecutor.o recordingtrash.o remote.o timers.o changestate.o eventsstreamthread.o changestatetracker.o scraper2vdr.o statusmonitor.o osd.o jsonparser.o epgsearch.o wirbelscan.o webapp.o femon.o
CFGS = API.html

all: $(SOFILE) i18n
Expand All @@ -55,8 +55,8 @@ $(DEPFILE): Makefile

PODIR = po
I18Npo = $(wildcard $(PODIR)/*.po)
I18Nmo = $(addsuffix .mo, $(foreach file, $(I18Npo), $(basename $(file))))
I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file))))))
I18Nmo = $(addsuffix .mo, $(foreach file,$(I18Npo),$(basename $(file))))
I18Nmsgs = $(addprefix $(DESTDIR)$(LOCDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file,$(I18Npo),$(basename $(file))))))
I18Npot = $(PODIR)/$(PLUGIN).pot

%.mo: %.po
Expand All @@ -72,9 +72,59 @@ $(I18Npot): $(wildcard *.cpp)
$(I18Nmsgs): $(DESTDIR)$(LOCDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo
install -D -m644 $< $@

.PHONY: i18n
.PHONY: i18n test-recording-move-plan test-recording-move-analysis test-recording-move-preflight test-recording-move-execution-gate test-recording-rename-plan test-recording-rename-preflight
i18n: $(I18Nmo) $(I18Npot)

test-recording-move-plan:
$(CXX) -std=c++17 -Wall -Wextra \
recordingmutation.cpp \
tests/test_recording_move_plan.cpp \
-o /tmp/test_recording_move_plan
/tmp/test_recording_move_plan

test-recording-move-analysis:
$(CXX) -std=c++17 -Wall -Wextra \
recordingmutation.cpp \
recordingmoveanalysis.cpp \
tests/test_recording_move_analysis.cpp \
-o /tmp/test_recording_move_analysis
/tmp/test_recording_move_analysis

test-recording-move-preflight:
$(CXX) -std=c++17 -Wall -Wextra \
recordingmutation.cpp \
recordingmoveanalysis.cpp \
recordingmovepreflight.cpp \
tests/test_recording_move_preflight.cpp \
-o /tmp/test_recording_move_preflight
/tmp/test_recording_move_preflight

test-recording-move-execution-gate:
$(CXX) -std=c++17 -Wall -Wextra \
recordingmutation.cpp \
recordingmoveanalysis.cpp \
recordingmoveexecution.cpp \
tests/test_recording_move_execution_gate.cpp \
-o /tmp/test_recording_move_execution_gate
/tmp/test_recording_move_execution_gate

test-recording-rename-plan:
$(CXX) -std=c++17 -Wall -Wextra \
recordingrenameplan.cpp \
tests/test_recording_rename_plan.cpp \
-o /tmp/test_recording_rename_plan
/tmp/test_recording_rename_plan

test-recording-rename-preflight:
$(CXX) -std=c++17 -Wall -Wextra \
recordingmutation.cpp \
recordingmoveanalysis.cpp \
recordingrenameplan.cpp \
recordingrenamepreflight.cpp \
tests/test_recording_rename_preflight.cpp \
-o /tmp/test_recording_rename_preflight
/tmp/test_recording_rename_preflight

install-i18n: $(I18Nmsgs)

$(SOFILE): $(OBJS)
Expand All @@ -94,12 +144,7 @@ dist: $(I18Npo) clean
@cp -a * $(TMPDIR)/$(ARCHIVE)
@-rm -rf $(TMPDIR)/$(ARCHIVE)/debian
@tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE)
@-rm -rf $(TMPDIR)/$(ARCHIVE)
@echo Distribution package created as $(PACKAGE).tgz

clean:
@-rm -f $(PODIR)/*.mo $(PODIR)/*.pot
@-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~ ._*

archive:
git archive --format=tar.gz --prefix=vdr-plugin-restfulapi-${VERSION}/ --output=../vdr-plugin-restfulapi-${VERSION}.tar.gz master
@-rm -f $(PODIR)/*.mo $(PODIR)/*.pot $(OBJS) $(DEPFILE) $(SOFILE) *~
198 changes: 198 additions & 0 deletions RECORDING_MOVE_API.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# Safe Native Recording Move API

This document describes the safe recording move workflow implemented by the RESTfulAPI plugin.

The implementation follows native VDR behavior. It does not stop playback, stop recordings, modify timers, cancel recording handler operations, or bypass VDR recording list updates.

## Workflow

Recording move uses a three-step optimistic-locking workflow:

1. Preview the operation and obtain state fingerprints.
2. Optionally validate that the fingerprints are still current.
3. Execute the operation with the confirmed fingerprints.

Preview and validation are read-only. They do not modify the recording, timers, playback state, filesystem, or VDR recording lists.

## Path contract

Both `file` and `target_file` are complete native VDR filesystem identities.

Example:

```text
/srv/vdr/video/Example/2026-07-14.08.53.3-0.rec
```

The target must be absolute, must differ from the source, and must not already exist in the filesystem or VDR recording list.

## Preview

```http
POST /recordings/move/preview.json
Content-Type: application/json
```

Request:

```json
{
"file": "/srv/vdr/video/Example/2026-07-14.08.53.3-0.rec",
"target_file": "/srv/vdr/video/Archive/Example/2026-07-14.08.53.3-0.rec"
}
```

Executable response:

```json
{
"executable": true,
"recording_file": "/srv/vdr/video/Example/2026-07-14.08.53.3-0.rec",
"target_file": "/srv/vdr/video/Archive/Example/2026-07-14.08.53.3-0.rec",
"constraints": [],
"blockers": [],
"warnings": [],
"steps": [
"move-recording",
"refresh-recordings",
"notify-change"
],
"revision_recording_file": "/srv/vdr/video/Example/2026-07-14.08.53.3-0.rec",
"revision_target_file": "/srv/vdr/video/Archive/Example/2026-07-14.08.53.3-0.rec",
"revision_recordings_state": 123456789,
"revision_timers_state": 987654321
}
```

## Validate

```http
POST /recordings/move/validate.json
Content-Type: application/json
```

Request:

```json
{
"file": "/srv/vdr/video/Example/2026-07-14.08.53.3-0.rec",
"target_file": "/srv/vdr/video/Archive/Example/2026-07-14.08.53.3-0.rec",
"revision_recordings_state": "123456789",
"revision_timers_state": "987654321"
}
```

Validation status values:

- `ready`: source, target, recording state, and timer state still match the preview revision.
- `conflict`: source, target, replay, handler, or timer state changed after preview.
- `blocked`: the current state or policy does not allow execution.

## Execute

```http
POST /recordings/move.json
Content-Type: application/json
```

Request:

```json
{
"file": "/srv/vdr/video/Example/2026-07-14.08.53.3-0.rec",
"target_file": "/srv/vdr/video/Archive/Example/2026-07-14.08.53.3-0.rec",
"revision_recordings_state": "123456789",
"revision_timers_state": "987654321"
}
```

Successful response:

```json
{
"status": "moved",
"recording_file": "/srv/vdr/video/Example/2026-07-14.08.53.3-0.rec",
"target_file": "/srv/vdr/video/Archive/Example/2026-07-14.08.53.3-0.rec",
"message": "Recording moved to the requested target."
}
```

Idempotent retry after the source has already been moved to the requested target:

```json
{
"status": "already-moved",
"recording_file": "/srv/vdr/video/Example/2026-07-14.08.53.3-0.rec",
"target_file": "/srv/vdr/video/Archive/Example/2026-07-14.08.53.3-0.rec",
"message": "Recording is already present at the requested target."
}
```

## Protected states

The operation is blocked when safety cannot be established or VDR is actively using the recording:

- `replay-active`
- `recording-handler-busy`
- `local-timer-active`
- `remote-timer-active`
- `unknown-recording-handler-state`
- `unknown-local-timer-state`
- `unknown-remote-timer-state`
- `unknown-search-timer-state`

Move-specific blockers include:

- `move-target-missing`
- `move-target-invalid`
- `move-target-same-as-source`
- `move-target-exists`

The execution endpoint does not automatically resolve these states.

## Execution behavior

The executor:

1. Revalidates the optimistic-locking revision.
2. Acquires VDR timer and recording write locks.
3. Rechecks source, target, replay, handler, recording control, and timer association.
4. Uses the native VDR directory move helper.
5. Verifies that the source disappeared and the target exists.
6. Replaces the source identity with the target identity in the VDR recording list.
7. Runs the native `recordingaction rename` hook.
8. Forces video disk usage refresh and recording change notification.

## HTTP status codes

| Status | Meaning |
|---|---|
| `200` | The recording was moved or is already present at the requested target. |
| `400` | Source, target, or required revision values are missing or invalid. |
| `404` | The source recording disappeared before execution. |
| `409` | Source, target, recording, replay, handler, or timer state changed after preview. |
| `423` | The operation is blocked by the current VDR state or policy. |
| `500` | The native VDR mutation or postcondition verification failed. |
| `501` | The requested HTTP method is not supported. |

## Verified integration behavior

The following cases were verified against a running VDR installation on July 15, 2026:

- preview: `200`, executable, no filesystem mutation
- validate: `200 ready`, expected and current revisions identical
- missing revision: `400`
- stale revision: `409`
- unsupported method: `501`
- completed 285 MB recording: `200 moved`
- source directory removed and target directory present
- old VDR recording identity absent and new identity present
- native directory creation and rename logged by VDR
- native `vdr-recordingaction rename` hook executed
- immediate identical retry: `200 already-moved`
- reverse move through the same preview, validate, and execute workflow: `200 moved`
- final VDR recording list restored to the original identity

## Design boundary

This API moves a recording to one explicit native VDR filesystem identity. It is not a generic file manager, does not copy recordings, and does not silently stop active VDR operations.
Loading