The other public generators drop NSFW streams with isSFW(), as PLAYLISTS.md promises (no NSFW channels since January 30th, 2024), but categories/ and sources/ took every stream, so NSFW channels with a feed were published there. The test fixture's NSFW channel now carries its feed id, so it reaches the generators instead of being dropped as a stream without a feed.
The edit form tells users to put `~` in a field to delete its value.
report:create split the Stream ID on `@` without checking for that, so
an edit request that removes the stream ID from an existing link was
listed as invalid_channel_id, since there is no channel called `~`.
Skip the channel lookup when the stream ID is marked as deleted, as
issue:validate already does.
playlist:validate decides which streams belong to the selected files by
comparing the raw command-line arguments with the paths returned by
Storage.list(). A path such as ./streams/us.m3u never matches
streams/us.m3u, so the file's own streams are put in the duplicate buffer
and every link is reported as already being in that same file.
Resolve the arguments with getStoragePath(), as playlist:format does.
Resolved conflict in scripts/models/stream.ts: kept the branch's
labels/isNot247/isGeoBlocked handling in fromPlaylistItem while adopting
master's optional-chaining guards for data.tvg / data.http.
- Normalize the returned path to forward slashes so it matches what
Storage.list() (glob) returns, instead of leaking `\` on Windows.
- Treat the storage root itself as "not inside", so passing a directory
no longer yields an empty path that Storage.load() would open as a dir.
- Extract the containment check into isInsideDirectory() and drop the
redundant isAbsolute(filepath) branch (path.resolve already handles it).
- Restore the repository's CRLF line endings in the nested fixture.