Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 9 additions & 7 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ flowchart TD
MON["Monitor.ProcessChanges(): one batch of settled folders"] --> D
D -->|error or cancel| R1
D --> F["Process.ProcessFiles(): see Dispatch and Results"]
F -->|"cancel, or an exception while processing"| R1
F -->|cancel| R1
F -.->|"exception writing the settings or results file, S13"| CRASH["The command-line default handler prints it to stderr, skips DeleteEmptyFolders()"]:::suspect
CRASH --> R1
F --> G["Process.DeleteEmptyFolders(): DeleteEmptyFolders option, input folders only"]
Expand Down Expand Up @@ -282,9 +282,11 @@ flowchart TD
K2 --> N{"More files in the group?"}
N -->|yes| D
N -->|no| C
E -.->|exception| X2["An exception from any file ends the whole run, S2"]:::suspect
E -->|"exception escaping ProcessFile() while no cancel is requested"| E1["Log it and fail the file, no result row"]
E1 --> K
E -->|"exception after a cancel request"| X3["The exception leaves ForAll"]
X1 --> RF["Run result false"]
X2 --> RF
X3 --> RF
C -->|all groups done| RT["Run result true, whatever the error count"]
RF --> P["Log the error, modified and VerifyFailed files"]
RT --> P
Expand All @@ -302,7 +304,7 @@ flowchart TD

`Process.ProcessFile()` runs one file through the steps below, in this order. The node ids `P01` onward follow the order of the step calls in `Process.ProcessFile()`, and `ArchitectureDiagramTests` fails when they differ.

A step that returns false leaves the chain at the abort node, except `DeleteNonMkvFile()`, whose false return ends the file as a success. From `RemuxByExtension()` on, a cancel seen after a step does the same. `DeleteNonMkvFile()`, `IsSidecarWriteable()`, `MakeExtensionLowercase()` and `VerifyMediaInfo()` have no cancel check after them.
A step that returns false leaves the chain at the abort node, except `DeleteNonMkvFile()`, whose false return ends the file as a success. From `RemuxByExtension()` on, a cancel seen after a step does the same. `DeleteNonMkvFile()`, `IsSidecarWriteable()`, `MakeExtensionLowercase()` and `VerifyMediaInfo()` have no cancel check after them. A step that throws while no cancel is requested also leaves the chain at the abort node.

```mermaid
flowchart TD
Expand Down Expand Up @@ -355,15 +357,16 @@ flowchart TD
P07 -.->|"false or cancel"| AB
MAIN -.->|"any step false or cancel"| AB
CLEAN -.->|"any step false or cancel"| AB
AB["Abort the file, failedOperation names the step"] --> G5{"Skipped as read-only?"}
THR["Any step throws while no cancel is requested"] -.-> AB
AB["Abort the file, failedOperation names the step"] --> G5{"Skipped as read-only, or a step threw?"}
G5 -->|yes| RET(["Result false, return the state and final path"])
G5 -->|no| DF["DeleteFailedFile(): DeleteInvalidFiles deletes the media file, and the sidecar of an MKV file, unless cancelled, a tool run for the file failed to launch or threw, an FfMpeg verify run was killed by a signal outside Windows, or the state has PostDecodeTimestamps"]
DF --> RET
classDef suspect stroke:#c00,stroke-width:2px,stroke-dasharray:5 3
class P02,CLEAN suspect
```

`ProcessDriver.ProcessFiles()` counts the tool runs of each file that fail to launch, are cancelled, or throw, since such a run leaves the media unjudged. Outside Windows, it also counts each file's FfMpeg verify runs that a signal terminates. Such a run may be an external kill rather than corrupt media, since both exit alike. `DeleteFailedFile()` does not delete a file that had any such tool run. It also keeps a file whose state carries `PostDecodeTimestamps`.
`ProcessDriver.ProcessFiles()` counts the tool runs of each file that fail to launch, are cancelled, or throw, since such a run leaves the media unjudged. Outside Windows, it also counts each file's FfMpeg verify runs that a signal terminates. Such a run may be an external kill rather than corrupt media, since both exit alike. `DeleteFailedFile()` does not delete a file that had any such tool run. It also keeps a file whose state carries `PostDecodeTimestamps`. A delete that throws is logged, and the file counts as not deleted.

`VerifyTrackCounts()` fails a file with no video track or more than one, and only warns when there is no audio track. `VerifyMediaInfo()` fails a file when FfProbe, MkvMerge and MediaInfo disagree on the track count of any type. A rename from a remux or the lowercase step reaches the result as the new path.

Expand Down Expand Up @@ -731,7 +734,6 @@ The table lists the `StatesType` flags each step sets, and the flags it clears.

Each entry describes a branch the diagrams mark, as the code behaves today, and links the issue that tracks its fix. Update the diagram and remove the entry when the fix lands.

- **S2** ([#1069](https://github.com/ptr727/PlexCleaner/issues/1069)): `Process.ProcessFile()` catches no exception. An IO exception in any step ends the whole run with exit code 1, rather than failing one file.
- **S3** ([#1070](https://github.com/ptr727/PlexCleaner/issues/1070)): Deleting an orphan sidecar returns false, so the file counts as an error and runs `DeleteFailedFile()`. Deleting a non-MKV file in `DeleteNonMkvFile()` counts as a success.
- **S4** ([#1071](https://github.com/ptr727/PlexCleaner/issues/1071)): With the `Verify` option off, `Verify()` passes and `VerifyAndRepair()` still sets `Verified`. Enabling `Verify` later skips those files, and `Verified` also skips the interlace and caption scans. A metadata repair that sets `VerifyFailed` is retried on every other run, since the next pass clears it.
- **S5** ([#1072](https://github.com/ptr727/PlexCleaner/issues/1072)): `ReEncode()` and `DeInterlace()` replace the original before their last steps run. A failure after that leaves the rewritten file with no state flag and the file not reported as modified. That is the remux or language restore in `ReEncode()`, and the language restore in `DeInterlace()`. A cancel after a decode-error repair in `VerifyAndRepair()` leaves the repaired file the same way.
Expand Down
4 changes: 4 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ Utility to optimize media files for Direct Play in Plex, Emby, Jellyfin, etc.
- An FfMpeg verify run killed by a signal still runs the repair.
- Outside Windows, the media file and its sidecar stay in place when that repair fails.
- A failed or skipped repair leaves the file's state unchanged, so the next run verifies it again.
- An exception while processing one file fails that file, and the run continues with the next file.
- Before, it ended the run for every remaining file with exit code 1.
- The media file and its sidecar stay in place, whatever `DeleteInvalidFiles` says.
- The error summary names the failed step beside the state of each file that failed processing, such as `VerifyFailed, VerifyAndRepair`.
- A failed repair whose tool fails to launch or throws leaves the file's state unchanged, rather than marking it `VerifyFailed` and `RepairFailed`.
- The next run retries the repair.
- Version 3.22:
Expand Down
Loading
Loading