From 5aaa3312ba054253b746edac7bb44aa35c5eedaf Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Wed, 14 Oct 2026 23:18:05 +0200 Subject: [PATCH] Media drive events, night: K1 (nothing lost while a drive is gone), K2 (Media C made ready by script, idempotent), K3 (safe removal is not safe while media apps run), K5 part (restart after added/returned) --- .../K0-filesig-start.tsv | 66 ++++ .../K1-PREDICTIONS.md | 13 + .../K1-a-mark.txt | 5 + .../K1-a-plex-log.txt | 35 ++ .../K1-a-plex.txt | 47 +++ .../K1-ab-jellyfin-arr.txt | 40 ++ .../K1-c-reattach.txt | 20 + .../K1-c-torrents.txt | 16 + .../K1-detach.txt | 7 + .../K1-filesig-after.tsv | 66 ++++ .../K1-filesig-diff.txt | 0 .../K1-restart-while-absent.txt | 16 + .../K1-settings-before.txt | 16 + .../K1-state-1-before.txt | 19 + .../K1-state-after-return.txt | 19 + .../K2-1-disk-added.txt | 4 + .../K2-2-enrol.txt | 12 + .../K2-3-drill-bind-c.txt | 6 + .../K2-4-probe-media.txt | 4 + .../K2-5-plex-access.txt | 5 + .../K2-6-wire-run1.txt | 155 ++++++++ .../K2-7-wire-run2.txt | 140 +++++++ .../K3-1-create.txt | 23 ++ .../K3-2-pause.txt | 23 ++ .../K3-3-eject.txt | 22 ++ .../K3-4-restart-after-eject.txt | 26 ++ .../K3-5-detach-reattach.txt | 27 ++ .../K3-6-resume.txt | 45 +++ .../K3-PREDICTIONS.md | 8 + .../K5-1-restart-after-added.txt | 6 + .../K5-2-restart-after-return.txt | 18 + .../NIGHT-LOG.md | 9 + .../tools/filesig.py | 14 + .../tools/grun.sh | 15 + .../media-drive-events-2026-10-14/tools/k1.py | 173 ++++++++ .../media-drive-events-2026-10-14/tools/k3.py | 120 ++++++ .../tools/run_in_guest.py | 9 + .../tools/wire.py | 374 ++++++++++++++++++ 38 files changed, 1623 insertions(+) create mode 100644 documentation/audits/media-drive-events-2026-10-14/K0-filesig-start.tsv create mode 100644 documentation/audits/media-drive-events-2026-10-14/K1-PREDICTIONS.md create mode 100644 documentation/audits/media-drive-events-2026-10-14/K1-a-mark.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K1-a-plex-log.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K1-a-plex.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K1-ab-jellyfin-arr.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K1-c-reattach.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K1-c-torrents.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K1-detach.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K1-filesig-after.tsv create mode 100644 documentation/audits/media-drive-events-2026-10-14/K1-filesig-diff.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K1-restart-while-absent.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K1-settings-before.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K1-state-1-before.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K1-state-after-return.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K2-1-disk-added.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K2-2-enrol.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K2-3-drill-bind-c.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K2-4-probe-media.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K2-5-plex-access.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K2-6-wire-run1.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K2-7-wire-run2.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K3-1-create.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K3-2-pause.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K3-3-eject.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K3-4-restart-after-eject.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K3-5-detach-reattach.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K3-6-resume.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K3-PREDICTIONS.md create mode 100644 documentation/audits/media-drive-events-2026-10-14/K5-1-restart-after-added.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/K5-2-restart-after-return.txt create mode 100644 documentation/audits/media-drive-events-2026-10-14/NIGHT-LOG.md create mode 100644 documentation/audits/media-drive-events-2026-10-14/tools/filesig.py create mode 100755 documentation/audits/media-drive-events-2026-10-14/tools/grun.sh create mode 100644 documentation/audits/media-drive-events-2026-10-14/tools/k1.py create mode 100644 documentation/audits/media-drive-events-2026-10-14/tools/k3.py create mode 100644 documentation/audits/media-drive-events-2026-10-14/tools/run_in_guest.py create mode 100644 documentation/audits/media-drive-events-2026-10-14/tools/wire.py diff --git a/documentation/audits/media-drive-events-2026-10-14/K0-filesig-start.tsv b/documentation/audits/media-drive-events-2026-10-14/K0-filesig-start.tsv new file mode 100644 index 00000000..f13cf970 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K0-filesig-start.tsv @@ -0,0 +1,66 @@ +/mnt/felhom-drives/media_a/userdata/media/tv/Avatar - Seven Havens/Season 1/Avatar.Seven.Havens.S01E01.1080p.WEB.h264-GRACE.mkv 909464483 4194364 2 1791997622 +/mnt/felhom-drives/media_a/userdata/media/tv/Avatar - Seven Havens/Season 1/Avatar.Seven.Havens.S01E03.1080p.WEB.h264-GRACE.mkv 720261766 4194368 2 1791997651 +/mnt/felhom-drives/media_a/userdata/media/tv/Avatar - Seven Havens/Season 1/Avatar.Seven.Havens.S01E02.1080p.WEB.h264-GRACE.mkv 829645981 4194375 2 1791997623 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Avatar.Seven.Havens.S01E01.1080p.WEB.h264-GRACE/Avatar.Seven.Havens.S01E01.1080p.WEB.h264-GRACE.mkv 909464483 4194364 2 1791997622 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Avatar.Seven.Havens.S01E01.1080p.WEB.h264-GRACE/avatar.seven.havens.s01e01.1080p.web.h264-grace.nfo 497 4194376 1 1791997577 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Avatar.Seven.Havens.S01E01.1080p.WEB.h264-GRACE/Sample/avatar.seven.havens.s01e01.1080p.web.h264-grace.sample.mkv 35166429 4194366 1 1791997623 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Slow.Horses.S06E05.Sayonara.1080p.ATVP.WEB-DL.DDP5.1.H.264-NTb/Slow.Horses.S06E05.Sayonara.1080p.ATVP.WEB-DL.DDP5.1.H.264-NTb.mkv 3789544605 4194398 1 1791997839 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Slow.Horses.S06E05.Sayonara.1080p.ATVP.WEB-DL.DDP5.1.H.264-NTb/Slow.Horses.S06E05.Sayonara.1080p.ATVP.WEB-DL.DDP5.1.H.264-NTb.nfo 62 4194400 1 1791997834 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Slow.Horses.S06E03.1080p.WEB.H264-CAKES/slow.horses.s06e03.1080p.web.h264-cakes.mkv 3931160378 4194385 1 1791997887 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Slow.Horses.S06E03.1080p.WEB.H264-CAKES/slow.horses.s06e03.1080p.web.h264-cakes.nfo 2728 4194367 1 1791997882 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Slow.Horses.S06E03.1080p.WEB.H264-CAKES/Sample/slow.horses.s06e03.1080p.web.h264-cakes-sample.mkv 85809338 4194392 1 1791997778 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Slow.Horses.S06E02.PROPER.1080p.WEB.h264-ETHEL/slow.horses.s06e02.proper.1080p.web.h264-ethel.nfo 1023 4194397 1 1791997846 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Slow.Horses.S06E02.PROPER.1080p.WEB.h264-ETHEL/Slow.Horses.S06E02.PROPER.1080p.WEB.h264-ETHEL.mkv 3758255491 4194369 1 1791997854 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Slow.Horses.S06E02.PROPER.1080p.WEB.h264-ETHEL/Sample/slow.horses.s06e02.proper.1080p.web.h264-ethel.sample.mkv 77743753 4194383 1 1791997852 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Avatar.Seven.Havens.S01E03.1080p.WEB.h264-GRACE/Avatar.Seven.Havens.S01E03.1080p.WEB.h264-GRACE.mkv 720261766 4194368 2 1791997651 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Avatar.Seven.Havens.S01E03.1080p.WEB.h264-GRACE/avatar.seven.havens.s01e03.1080p.web.h264-grace.nfo 492 4194377 1 1791997591 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Avatar.Seven.Havens.S01E03.1080p.WEB.h264-GRACE/Sample/avatar.seven.havens.s01e03.1080p.web.h264-grace.sample.mkv 21739818 4194370 1 1791997622 +/mnt/felhom-drives/media_a/userdata/media/torrents/movies/Hope.2026.720p.AMZN.WEB-DL.DDP5.1.Atmos.H.264-N1H4L/Hope.2026.720p.AMZN.WEB-DL.DDP5.1.Atmos.H.264-N1H4L.nfo 602 4194405 1 1791997982 +/mnt/felhom-drives/media_a/userdata/media/torrents/movies/Hope.2026.720p.AMZN.WEB-DL.DDP5.1.Atmos.H.264-N1H4L/Hope.2026.720p.AMZN.WEB-DL.DDP5.1.Atmos.H.264-N1H4L.mkv 4697542077 4194391 2 1791997988 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Slow.Horses.S06E04.1080p.WEB.H264-CAKES/slow.horses.s06e04.1080p.web.h264-cakes.mkv 4039934637 4194396 1 1791997987 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Slow.Horses.S06E04.1080p.WEB.H264-CAKES/slow.horses.s06e04.1080p.web.h264-cakes.nfo 2730 4194399 1 1791997757 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Slow.Horses.S06E04.1080p.WEB.H264-CAKES/Sample/slow.horses.s06e04.1080p.web.h264-cakes-sample.mkv 94200139 4194395 1 1791997773 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Slow.Horses.S06E01.1080p.WEB.H264-CAKES/slow.horses.s06e01.1080p.web.h264-cakes.mkv 3859110437 4194387 1 1791997961 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Slow.Horses.S06E01.1080p.WEB.H264-CAKES/slow.horses.s06e01.1080p.web.h264-cakes.nfo 2693 4194388 1 1791997744 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Slow.Horses.S06E01.1080p.WEB.H264-CAKES/Sample/slow.horses.s06e01.1080p.web.h264-cakes-sample.mkv 104128605 4194390 1 1791997759 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Avatar.Seven.Havens.S01E02.1080p.WEB.h264-GRACE/avatar.seven.havens.s01e02.1080p.web.h264-grace.nfo 497 4194374 1 1791997576 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Avatar.Seven.Havens.S01E02.1080p.WEB.h264-GRACE/Avatar.Seven.Havens.S01E02.1080p.WEB.h264-GRACE.mkv 829645981 4194375 2 1791997623 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Avatar.Seven.Havens.S01E02.1080p.WEB.h264-GRACE/Sample/avatar.seven.havens.s01e02.1080p.web.h264-grace.sample.mkv 31210978 4194373 1 1791997609 +/mnt/felhom-drives/media_a/userdata/media/transmission/movies/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264.HuNSUB-TRiNiTY/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264.HuNSUB-TRiNiTY.mkv 2819947717 4194408 1 1791998428 +/mnt/felhom-drives/media_a/userdata/media/transmission/movies/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264.HuNSUB-TRiNiTY/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264.HuNSUB-TRiNiTY.nfo 8091 4194409 1 1791998340 +/mnt/felhom-drives/media_a/userdata/media/transmission/movies/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264.HuNSUB-TRiNiTY/Sample/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264.HuNSUB-TRiNiTY-Sample.mkv 22742740 4194411 1 1791998422 +/mnt/felhom-drives/media_a/userdata/media/movies/Hope (2026)/Hope.2026.720p.AMZN.WEB-DL.DDP5.1.Atmos.H.264-N1H4L.mkv 4697542077 4194391 2 1791997988 +/mnt/felhom-drives/media_b/userdata/media/torrents/movies/Matchbox.The.Movie.2026.720p.ATVP.WEB-DL.DDP5.1.Atmos.H.264-FLUX/Matchbox.The.Movie.2026.720p.ATVP.WEB-DL.DDP5.1.Atmos.H.264-FLUX.mkv 3393846457 1048578 2 1791998022 +/mnt/felhom-drives/media_b/userdata/media/torrents/movies/Matchbox.The.Movie.2026.720p.ATVP.WEB-DL.DDP5.1.Atmos.H.264-FLUX/Matchbox.The.Movie.2026.720p.ATVP.WEB-DL.DDP5.1.Atmos.H.264-FLUX.nfo 619 1048579 1 1791998019 +/mnt/felhom-drives/media_b/userdata/media/torrents/tv/American.Horror.Story.S13E01.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E01.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.nfo 68 131101 1 1791997602 +/mnt/felhom-drives/media_b/userdata/media/torrents/tv/American.Horror.Story.S13E01.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E01.13.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.mkv 1314062473 131100 2 1791997680 +/mnt/felhom-drives/media_b/userdata/media/torrents/tv/American.Horror.Story.S13E03.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E03.Rauhnacht.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.mkv 813589489 131110 2 1791997659 +/mnt/felhom-drives/media_b/userdata/media/torrents/tv/American.Horror.Story.S13E03.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E03.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.nfo 68 131113 1 1791997627 +/mnt/felhom-drives/media_b/userdata/media/torrents/tv/American.Horror.Story.S13E07.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E07.The.Baddest.Witch.of.Them.All.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.mkv 956532263 393221 2 1791997699 +/mnt/felhom-drives/media_b/userdata/media/torrents/tv/American.Horror.Story.S13E07.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E07.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.nfo 67 393223 1 1791997690 +/mnt/felhom-drives/media_b/userdata/media/torrents/tv/American.Horror.Story.S13E05.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E05.Date.Night.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.mkv 969653267 393218 2 1791997719 +/mnt/felhom-drives/media_b/userdata/media/torrents/tv/American.Horror.Story.S13E05.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E05.Date.Night.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.nfo 77 393220 1 1791997712 +/mnt/felhom-drives/media_b/userdata/media/movies/Matchbox the Movie (2026)/Matchbox.The.Movie.2026.720p.ATVP.WEB-DL.DDP5.1.Atmos.H.264-FLUX.mkv 3393846457 1048578 2 1791998022 +/mnt/felhom-drives/media_b/userdata/media/movies/WHO (2026)/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264.HuNSUB-TRiNiTY.mkv 2819947717 2097156 1 1791998428 +/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E04.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.mkv 1164079593 131107 2 1791997676 +/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E01.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.mkv 1314062473 131100 2 1791997680 +/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E06.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.mkv 913481011 131112 2 1791997757 +/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E07.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.mkv 956532263 393221 2 1791997699 +/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E03.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.mkv 813589489 131110 2 1791997659 +/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E02.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.mkv 1274167804 131103 2 1791997733 +/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E05.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.mkv 969653267 393218 2 1791997719 +/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E08.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.mkv 1149433990 262146 2 1791997741 +/mnt/felhom-drives/media_b/userdata/media/tv/Slow Horses/Season 6/Slow.Horses.S06E05.Sayonara.1080p.ATVP.WEB-DL.DDP5.1.H.264-NTb.mkv 3789544605 524295 1 1791997839 +/mnt/felhom-drives/media_b/userdata/media/tv/Slow Horses/Season 6/Slow.Horses.S06E01.1080p.WEB.H264-CAKES.mkv 3859110437 524298 1 1791997961 +/mnt/felhom-drives/media_b/userdata/media/tv/Slow Horses/Season 6/Slow.Horses.S06E04.1080p.WEB.H264-CAKES.mkv 4039934637 524299 1 1791997987 +/mnt/felhom-drives/media_b/userdata/media/tv/Slow Horses/Season 6/Slow.Horses.S06E02.PROPER.1080p.WEB.h264-ETHEL.mkv 3758255491 524296 1 1791997854 +/mnt/felhom-drives/media_b/userdata/media/tv/Slow Horses/Season 6/Slow.Horses.S06E03.1080p.WEB.H264-CAKES.mkv 3931160378 524297 1 1791997887 +/mnt/felhom-drives/media_b/userdata/media/transmission/tv/American.Horror.Story.S13E02.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E02.1.13.AM.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.mkv 1274167804 131103 2 1791997733 +/mnt/felhom-drives/media_b/userdata/media/transmission/tv/American.Horror.Story.S13E02.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E02.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.nfo 68 131104 1 1791997605 +/mnt/felhom-drives/media_b/userdata/media/transmission/tv/American.Horror.Story.S13E04.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E04.Is.That.All.There.Is.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.mkv 1164079593 131107 2 1791997676 +/mnt/felhom-drives/media_b/userdata/media/transmission/tv/American.Horror.Story.S13E04.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E04.Is.That.All.There.Is.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.nfo 67 131108 1 1791997615 +/mnt/felhom-drives/media_b/userdata/media/transmission/tv/American.Horror.Story.S13E08.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E08.Hell.No.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.mkv 1149433990 262146 2 1791997741 +/mnt/felhom-drives/media_b/userdata/media/transmission/tv/American.Horror.Story.S13E08.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E08.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.nfo 67 262147 1 1791997643 +/mnt/felhom-drives/media_b/userdata/media/transmission/tv/American.Horror.Story.S13E06.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E06.The.Return.of.Devils.Night.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.mkv 913481011 131112 2 1791997757 +/mnt/felhom-drives/media_b/userdata/media/transmission/tv/American.Horror.Story.S13E06.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E06.The.Return.of.Devils.Night.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.nfo 93 131114 1 1791997633 + diff --git a/documentation/audits/media-drive-events-2026-10-14/K1-PREDICTIONS.md b/documentation/audits/media-drive-events-2026-10-14/K1-PREDICTIONS.md new file mode 100644 index 00000000..82889db7 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K1-PREDICTIONS.md @@ -0,0 +1,13 @@ +# K1 predictions — written before the detach (2026-10-14 ~23:00 CEST) + +With Media B detached (the guest keeps a dead mount; reads give I/O errors — S5): +- a. Plex scan with autoEmptyTrash OFF: B's items stay, their parts unavailable. With it ON (the default): the scan + REMOVES the B shows/films (American Horror Story, Matchbox, Who) and the B half of Slow Horses — or, because the + folder answers I/O errors rather than „empty", Plex may refuse to treat it as gone and keep everything. Jellyfin + scan: removes B's items. After re-attach + restart + rescan: items come back as NEW entries; Plex's watch count on + the probe episode is lost unless Plex matches it again by GUID (Plex keeps view state per GUID — it may survive). +- b. Sonarr/Radarr refresh+rescan: the B root folder is inaccessible; Sonarr's own failsafe („root folder is empty — + rescan will not update") keeps the files' records; nothing is unmonitored (the option is off). With the option ON: + still nothing, if the failsafe holds. +- c. After re-attach + stop/start: qBittorrent and Transmission recheck or show „missing files" for B's torrents for a + moment, then continue without re-downloading. diff --git a/documentation/audits/media-drive-events-2026-10-14/K1-a-mark.txt b/documentation/audits/media-drive-events-2026-10-14/K1-a-mark.txt new file mode 100644 index 00000000..d9123fa6 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K1-a-mark.txt @@ -0,0 +1,5 @@ +plex probe before: viewCount=3 +plex scrobble -> 200 +jellyfin probe before: Played=False +jellyfin played -> 200 +after: plex viewCount=4 | jellyfin Played=True diff --git a/documentation/audits/media-drive-events-2026-10-14/K1-a-plex-log.txt b/documentation/audits/media-drive-events-2026-10-14/K1-a-plex-log.txt new file mode 100644 index 00000000..37d7504a --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K1-a-plex-log.txt @@ -0,0 +1,35 @@ +Oct 14, 2026 22:55:55.191 [137446263175992] DEBUG - [JobRunner] Job running: FFMPEG_EXTERNAL_LIBS='/config/Library/Application\ Support/Plex\ Media\ Server/Codecs/e613bce-97f23d579c1001d8e9cc0d2e-linux-x86_64/' X_PLEX_TOKEN=xxxxxx +Oct 14, 2026 22:55:55.195 [137446267394872] DEBUG - [JobRunner] Job running: FFMPEG_EXTERNAL_LIBS='/config/Library/Application\ Support/Plex\ Media\ Server/Codecs/e613bce-97f23d579c1001d8e9cc0d2e-linux-x86_64/' X_PLEX_TOKEN=xxxxxx +Oct 14, 2026 22:55:55.321 [137446322166584] DEBUG - Jobs: '/usr/lib/plexmediaserver/Plex Media Scanner' exit code for process 2097 is 0 (success) +Oct 14, 2026 22:55:55.325 [137446322166584] DEBUG - Jobs: '/usr/lib/plexmediaserver/Plex Media Scanner' exit code for process 2098 is 0 (success) +Oct 14, 2026 22:55:55.333 [137446322166584] DEBUG - Jobs: '/usr/lib/plexmediaserver/Plex Media Scanner' exit code for process 2099 is 0 (success) +Oct 14, 2026 22:55:55.405 [137446265285432] DEBUG - [JobRunner] Job running: FFMPEG_EXTERNAL_LIBS='/config/Library/Application\ Support/Plex\ Media\ Server/Codecs/e613bce-97f23d579c1001d8e9cc0d2e-linux-x86_64/' X_PLEX_TOKEN=xxxxxx +Oct 14, 2026 22:55:55.413 [137446267394872] DEBUG - [JobRunner] Job running: FFMPEG_EXTERNAL_LIBS='/config/Library/Application\ Support/Plex\ Media\ Server/Codecs/e613bce-97f23d579c1001d8e9cc0d2e-linux-x86_64/' X_PLEX_TOKEN=xxxxxx +Oct 14, 2026 22:55:55.443 [137446263175992] DEBUG - [JobRunner] Job running: FFMPEG_EXTERNAL_LIBS='/config/Library/Application\ Support/Plex\ Media\ Server/Codecs/e613bce-97f23d579c1001d8e9cc0d2e-linux-x86_64/' X_PLEX_TOKEN=xxxxxx +Oct 14, 2026 22:55:55.539 [137446322166584] DEBUG - Jobs: '/usr/lib/plexmediaserver/Plex Media Scanner' exit code for process 2107 is 0 (success) +Oct 14, 2026 22:55:55.556 [137446322166584] DEBUG - Jobs: '/usr/lib/plexmediaserver/Plex Media Scanner' exit code for process 2106 is 0 (success) +Oct 14, 2026 22:55:55.583 [137446322166584] DEBUG - Jobs: '/usr/lib/plexmediaserver/Plex Media Scanner' exit code for process 2112 is 0 (success) +Oct 14, 2026 22:55:55.606 [137446267394872] DEBUG - [JobRunner] Job running: FFMPEG_EXTERNAL_LIBS='/config/Library/Application\ Support/Plex\ Media\ Server/Codecs/e613bce-97f23d579c1001d8e9cc0d2e-linux-x86_64/' X_PLEX_TOKEN=xxxxxx +Oct 14, 2026 22:55:55.614 [137446265285432] DEBUG - [JobRunner] Job running: FFMPEG_EXTERNAL_LIBS='/config/Library/Application\ Support/Plex\ Media\ Server/Codecs/e613bce-97f23d579c1001d8e9cc0d2e-linux-x86_64/' X_PLEX_TOKEN=xxxxxx +Oct 14, 2026 22:55:55.639 [137446263175992] DEBUG - [JobRunner] Job running: FFMPEG_EXTERNAL_LIBS='/config/Library/Application\ Support/Plex\ Media\ Server/Codecs/e613bce-97f23d579c1001d8e9cc0d2e-linux-x86_64/' X_PLEX_TOKEN=xxxxxx +Oct 14, 2026 22:55:55.783 [137446322166584] DEBUG - Jobs: '/usr/lib/plexmediaserver/Plex Media Scanner' exit code for process 2115 is 0 (success) +Oct 14, 2026 22:55:55.790 [137446322166584] DEBUG - Jobs: '/usr/lib/plexmediaserver/Plex Media Scanner' exit code for process 2121 is 0 (success) +Oct 14, 2026 22:55:55.810 [137446322166584] DEBUG - Jobs: '/usr/lib/plexmediaserver/Plex Media Scanner' exit code for process 2116 is 0 (success) +Oct 14, 2026 22:56:01.730 [137446282160952] DEBUG - Request: [172.18.0.1:44862 (Subnet)] GET /library/sections/1/all?includeMissing=1 (2 live) #5df Signed-in Token () (Tester1 - server) +Oct 14, 2026 22:56:01.730 [137446282160952] WARN - [Req#5df] Missing X-Plex-Container-Size header. This will fail with status code 400 in the future. +Oct 14, 2026 22:56:01.735 [137446311959352] DEBUG - Completed: [172.18.0.1:44862] 200 GET /library/sections/1/all?includeMissing=1 (2 live) #5df 5ms 6611 bytes +Oct 14, 2026 22:56:01.770 [137446282160952] DEBUG - Request: [172.18.0.1:44884 (Subnet)] GET /library/sections/2/all?includeMissing=1 (2 live) #5e7 Signed-in Token () (Tester1 - server) +Oct 14, 2026 22:56:01.770 [137446282160952] WARN - [Req#5e7] Missing X-Plex-Container-Size header. This will fail with status code 400 in the future. +Oct 14, 2026 22:56:01.776 [137446311959352] DEBUG - Completed: [172.18.0.1:44884] 200 GET /library/sections/2/all?includeMissing=1 (2 live) #5e7 5ms 4955 bytes +Oct 14, 2026 22:56:01.820 [137446282160952] WARN - [Req#5f3] Missing X-Plex-Container-Size header. This will fail with status code 400 in the future. +Oct 14, 2026 22:56:01.829 [137446282160952] WARN - [Req#5f7] Missing X-Plex-Container-Size header. This will fail with status code 400 in the future. +PMS Plugin Logs +Plex Crash Uploader.1.log +Plex Crash Uploader.2.log +Plex Crash Uploader.log +Plex Media Scanner Analysis.1.log +Plex Media Scanner Analysis.2.log +Plex Media Scanner Analysis.3.log +Plex Media Scanner Analysis.4.log +Plex Media Scanner Analysis.5.log +Plex Media Scanner Analysis.log diff --git a/documentation/audits/media-drive-events-2026-10-14/K1-a-plex.txt b/documentation/audits/media-drive-events-2026-10-14/K1-a-plex.txt new file mode 100644 index 00000000..e43cf178 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K1-a-plex.txt @@ -0,0 +1,47 @@ +### a1. Plex, autoEmptyTrash OFF +plex autoEmptyTrash -> 200 +plex refresh section 1 -> 200 +plex refresh section 2 -> 200 +plex scan done 20:55:50 +--- 20:55:51 state + plex movie Hope items=1 parts=1 unavailable=0 + plex movie Matchbox the Movie items=1 parts=1 unavailable=0 + plex movie Who items=1 parts=1 unavailable=0 + plex show American Horror Story items=8 parts=8 unavailable=0 + plex show Avatar: Seven Havens items=3 parts=3 unavailable=0 + plex show Slow Horses items=5 parts=5 unavailable=0 + plex probe: viewCount=4 +### a2. Plex, autoEmptyTrash ON (the default) +plex autoEmptyTrash -> 200 +plex refresh section 1 -> 200 +plex refresh section 2 -> 200 +plex scan done 20:56:00 +--- 20:56:01 state + plex movie Hope items=1 parts=1 unavailable=0 + plex movie Matchbox the Movie items=1 parts=1 unavailable=0 + plex movie Who items=1 parts=1 unavailable=0 + plex show American Horror Story items=8 parts=8 unavailable=0 + plex show Avatar: Seven Havens items=3 parts=3 unavailable=0 + plex show Slow Horses items=5 parts=5 unavailable=0 + plex probe: viewCount=4 + checkFiles=1 Hope parts=1 accessible=[True] exists=['True'] + checkFiles=1 Matchbox the Movie parts=1 accessible=[False] exists=['False'] + checkFiles=1 Who parts=1 accessible=[False] exists=['False'] + checkFiles=1 American Horror Story parts=8 accessible=[None, None, None, None, None, None, None, None] exists=['None'] + checkFiles=1 Avatar: Seven Havens parts=3 accessible=[None, None, None] exists=['None'] + checkFiles=1 Slow Horses parts=5 accessible=[None, None, None, None, None] exists=['None'] + scanner: Plex Media Scanner Analysis.1.log Oct 14, 2026 19:15:45.583 [131834519904000] DEBUG - [MI] * fc9137b652c38e8de1b20c288b8f4926fbd4377b [mkv h264/eac3] Duration: 1472736ms, 1920x1080 (1.777778) 23.976fps, bitrate=4962kbps, audio channe + scanner: Plex Media Scanner Analysis.1.log Oct 14, 2026 19:15:45.583 [131834519904000] DEBUG - [MI] Closing input file: "/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E06.1080p.DSNP.WEB-D + scanner: Plex Media Scanner Analysis.1.log Oct 14, 2026 19:15:45.586 [131834519904000] DEBUG - Updating part with ID=34 [/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E06.1080p.DSNP.WEB-D + scanner: Plex Media Scanner Analysis.1.log Oct 14, 2026 19:15:45.602 [131834519904000] DEBUG - [ID 35] Media part analysis: /mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E07.1080p.DSNP.WE + scanner: Plex Media Scanner Analysis.1.log Oct 14, 2026 19:15:45.602 [131834519904000] DEBUG - [MI] Opening input file: "/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E07.1080p.DSNP.WEB-D + scanner: Plex Media Scanner Analysis.1.log Oct 14, 2026 19:15:45.602 [131834519904000] DEBUG - [FFMPEG] - Opening '/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E07.1080p.DSNP.WEB-DL.DDP5 + scanner: Plex Media Scanner Analysis.1.log Oct 14, 2026 19:15:45.644 [131834519904000] DEBUG - [MI] * c53e1ba1643469bcce4cfb4fc900238d99edb69d [mkv h264/eac3] Duration: 1385728ms, 1920x1080 (1.777778) 23.976fps, bitrate=5522kbps, audio channe + scanner: Plex Media Scanner Analysis.1.log Oct 14, 2026 19:15:45.645 [131834519904000] DEBUG - [MI] Closing input file: "/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E07.1080p.DSNP.WEB-D + scanner: Plex Media Scanner Analysis.1.log Oct 14, 2026 19:15:45.657 [131834519904000] DEBUG - Updating part with ID=35 [/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E07.1080p.DSNP.WEB-D + scanner: Plex Media Scanner Analysis.1.log Oct 14, 2026 19:15:45.690 [131834519904000] DEBUG - [ID 36] Media part analysis: /mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E08.1080p.DSNP.WE + scanner: Plex Media Scanner Analysis.1.log Oct 14, 2026 19:15:45.690 [131834519904000] DEBUG - [MI] Opening input file: "/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E08.1080p.DSNP.WEB-D + scanner: Plex Media Scanner Analysis.1.log Oct 14, 2026 19:15:45.690 [131834519904000] DEBUG - [FFMPEG] - Opening '/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E08.1080p.DSNP.WEB-DL.DDP5 + scanner: Plex Media Scanner Analysis.1.log Oct 14, 2026 19:15:45.733 [131834519904000] DEBUG - [MI] * 03ec86569979824516a2e8ae2033abdaf107fd94 [mkv h264/eac3] Duration: 1828608ms, 1920x1080 (1.777778) 23.976fps, bitrate=5028kbps, audio channe + scanner: Plex Media Scanner Analysis.1.log Oct 14, 2026 19:15:45.733 [131834519904000] DEBUG - [MI] Closing input file: "/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E08.1080p.DSNP.WEB-D + scanner: Plex Media Scanner Analysis.1.log Oct 14, 2026 19:15:45.736 [131834519904000] DEBUG - Updating part with ID=36 [/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E08.1080p.DSNP.WEB-D diff --git a/documentation/audits/media-drive-events-2026-10-14/K1-ab-jellyfin-arr.txt b/documentation/audits/media-drive-events-2026-10-14/K1-ab-jellyfin-arr.txt new file mode 100644 index 00000000..e48f324a --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K1-ab-jellyfin-arr.txt @@ -0,0 +1,40 @@ +### a3. Jellyfin scan +jellyfin /Library/Refresh -> 204 +jellyfin scan done 20:56:43 +--- 20:56:44 state + jellyfin: {'Series': 3, 'Movie': 3, 'Episode': 16} ['Amerikai Horror Story', 'Avatar: Seven Havens', 'Hope - Remény', 'Matchbox', 'Roger nyúl a pácban', 'Utolsó befutók'] + jellyfin probe: Played=True +### b1. Sonarr/Radarr refresh + rescan, unmonitor OFF (default) +sonarr RefreshSeries -> 201 completed +sonarr RescanSeries -> 201 completed +radarr RefreshMovie -> 201 completed +radarr RescanMovie -> 201 completed +--- 20:57:00 state + sonarr Avatar: Seven Havens monitored=True files=3 path=media_a + sonarr American Horror Story monitored=True files=8 path=media_b + sonarr Slow Horses monitored=True files=5 path=media_b + Avatar: Seven Havens episodes monitored=13/13 hasFile=3 + American Horror Story episodes monitored=13/166 hasFile=8 + Slow Horses episodes monitored=6/39 hasFile=5 + radarr Hope monitored=True files=True path=media_a + radarr Matchbox the Movie monitored=True files=True path=media_b + radarr WHO monitored=True files=True path=media_b +### b2. the same with unmonitor ON +sonarr autoUnmonitorPreviouslyDownloadedEpisodes=True -> 202 +radarr autoUnmonitorPreviouslyDownloadedMovies=True -> 202 +sonarr RefreshSeries -> 201 completed +sonarr RescanSeries -> 201 completed +radarr RefreshMovie -> 201 completed +radarr RescanMovie -> 201 completed +--- 20:57:17 state + sonarr Avatar: Seven Havens monitored=True files=3 path=media_a + sonarr American Horror Story monitored=True files=8 path=media_b + sonarr Slow Horses monitored=True files=5 path=media_b + Avatar: Seven Havens episodes monitored=13/13 hasFile=3 + American Horror Story episodes monitored=13/166 hasFile=8 + Slow Horses episodes monitored=6/39 hasFile=5 + radarr Hope monitored=True files=True path=media_a + radarr Matchbox the Movie monitored=True files=True path=media_b + radarr WHO monitored=True files=True path=media_b +sonarr autoUnmonitorPreviouslyDownloadedEpisodes=False -> 202 +radarr autoUnmonitorPreviouslyDownloadedMovies=False -> 202 diff --git a/documentation/audits/media-drive-events-2026-10-14/K1-c-reattach.txt b/documentation/audits/media-drive-events-2026-10-14/K1-c-reattach.txt new file mode 100644 index 00000000..0cc0836f --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K1-c-reattach.txt @@ -0,0 +1,20 @@ +2026-10-14T22:59:00+02:00 +scsi3: nvme-scratch:341/vm-341-disk-4.raw,backup=0,size=100G +2026/10/14 20:59:32 [INFO] [gate] drive RETURNED /mnt/felhom-drives/media_b — re-attached + restarted gate-stopped apps +2026/10/14 20:59:32 [INFO] Event pushed: storage_reconnected (info) [hu-only] — Meghajtó újra csatlakoztatva: Media B +`-/mnt/felhom-drives/media_b /dev/sdf[/felhom-data] +|-/mnt/felhom-drives/media_b /dev/sdf[/felhom-data] + +qbittorrent stop -> 200 +transmission stop -> 200 +sonarr stop -> 200 +radarr stop -> 200 +plex stop -> 200 +jellyfin stop -> 200 +qbittorrent start -> 200 +transmission start -> 200 +sonarr start -> 200 +radarr start -> 200 +plex start -> 200 +jellyfin start -> 200 +stop+start of six apps took 40s (incl. 10s pause) diff --git a/documentation/audits/media-drive-events-2026-10-14/K1-c-torrents.txt b/documentation/audits/media-drive-events-2026-10-14/K1-c-torrents.txt new file mode 100644 index 00000000..cf11c9be --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K1-c-torrents.txt @@ -0,0 +1,16 @@ +21:00:49 qbit: {'tv-drive-b:queuedUP': 2, 'tv-drive-a:stalledUP': 1, 'movies-drive-a:queuedUP': 1, 'tv-drive-a:queuedUP': 4, 'tv-drive-b:stalledUP': 2, 'movies-drive-b:queuedUP': 1} + transmission: {'media_a:status6': 4, 'media_b:status6': 4} +21:01:03 qbit: {'tv-drive-b:queuedUP': 2, 'tv-drive-a:stalledUP': 1, 'movies-drive-a:queuedUP': 1, 'tv-drive-a:queuedUP': 4, 'tv-drive-b:stalledUP': 2, 'movies-drive-b:queuedUP': 1} + transmission: {'media_a:status6': 4, 'media_b:status6': 4} +21:01:17 qbit: {'tv-drive-b:queuedUP': 3, 'tv-drive-a:queuedUP': 3, 'movies-drive-a:queuedUP': 1, 'tv-drive-a:stalledUP': 2, 'movies-drive-b:queuedUP': 1, 'tv-drive-b:stalledUP': 1} + transmission: {'media_a:status6': 4, 'media_b:status6': 4} +21:01:30 qbit: {'tv-drive-b:queuedUP': 3, 'tv-drive-a:queuedUP': 3, 'movies-drive-a:queuedUP': 1, 'tv-drive-a:stalledUP': 2, 'movies-drive-b:queuedUP': 1, 'tv-drive-b:stalledUP': 1} + transmission: {'media_a:status6': 4, 'media_b:status6': 4} +21:01:44 qbit: {'tv-drive-b:stalledUP': 1, 'tv-drive-a:queuedUP': 4, 'movies-drive-a:queuedUP': 1, 'tv-drive-a:stalledUP': 1, 'tv-drive-b:queuedUP': 3, 'movies-drive-b:stalledUP': 1} + transmission: {'media_a:status6': 4, 'media_b:status6': 4} +21:01:58 qbit: {'tv-drive-b:stalledUP': 1, 'tv-drive-a:queuedUP': 4, 'movies-drive-a:queuedUP': 1, 'tv-drive-a:stalledUP': 1, 'tv-drive-b:queuedUP': 3, 'movies-drive-b:stalledUP': 1} + transmission: {'media_a:status6': 4, 'media_b:status6': 4} +21:02:12 qbit: {'tv-drive-b:queuedUP': 4, 'tv-drive-a:queuedUP': 3, 'movies-drive-a:stalledUP': 1, 'tv-drive-a:stalledUP': 2, 'movies-drive-b:queuedUP': 1} + transmission: {'media_a:status6': 4, 'media_b:status6': 4} +21:02:25 qbit: {'tv-drive-b:queuedUP': 4, 'tv-drive-a:queuedUP': 3, 'movies-drive-a:stalledUP': 1, 'tv-drive-a:stalledUP': 2, 'movies-drive-b:queuedUP': 1} + transmission: {'media_a:status6': 4, 'media_b:status6': 4} diff --git a/documentation/audits/media-drive-events-2026-10-14/K1-detach.txt b/documentation/audits/media-drive-events-2026-10-14/K1-detach.txt new file mode 100644 index 00000000..ec032d33 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K1-detach.txt @@ -0,0 +1,7 @@ +2026-10-14T22:54:48+02:00 +update VM 341: -delete scsi3 +unused0: nvme-scratch:341/vm-341-disk-4.raw +2026/10/14 20:55:01 [INFO] [settings] Storage path /mnt/felhom-drives/media_b disconnected=true +2026/10/14 20:55:01 [WARN] [gate] drive ABSENT /mnt/felhom-drives/media_b — stopped+blocked 0 app(s): [] +2026/10/14 20:55:01 [INFO] Event pushed: storage_disconnected (error) [hu-only] — Meghajtó váratlanul leválasztva: Media B + 20:55:33 up 5:27, 0 users, load average: 0.60, 0.56, 0.55 diff --git a/documentation/audits/media-drive-events-2026-10-14/K1-filesig-after.tsv b/documentation/audits/media-drive-events-2026-10-14/K1-filesig-after.tsv new file mode 100644 index 00000000..f13cf970 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K1-filesig-after.tsv @@ -0,0 +1,66 @@ +/mnt/felhom-drives/media_a/userdata/media/tv/Avatar - Seven Havens/Season 1/Avatar.Seven.Havens.S01E01.1080p.WEB.h264-GRACE.mkv 909464483 4194364 2 1791997622 +/mnt/felhom-drives/media_a/userdata/media/tv/Avatar - Seven Havens/Season 1/Avatar.Seven.Havens.S01E03.1080p.WEB.h264-GRACE.mkv 720261766 4194368 2 1791997651 +/mnt/felhom-drives/media_a/userdata/media/tv/Avatar - Seven Havens/Season 1/Avatar.Seven.Havens.S01E02.1080p.WEB.h264-GRACE.mkv 829645981 4194375 2 1791997623 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Avatar.Seven.Havens.S01E01.1080p.WEB.h264-GRACE/Avatar.Seven.Havens.S01E01.1080p.WEB.h264-GRACE.mkv 909464483 4194364 2 1791997622 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Avatar.Seven.Havens.S01E01.1080p.WEB.h264-GRACE/avatar.seven.havens.s01e01.1080p.web.h264-grace.nfo 497 4194376 1 1791997577 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Avatar.Seven.Havens.S01E01.1080p.WEB.h264-GRACE/Sample/avatar.seven.havens.s01e01.1080p.web.h264-grace.sample.mkv 35166429 4194366 1 1791997623 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Slow.Horses.S06E05.Sayonara.1080p.ATVP.WEB-DL.DDP5.1.H.264-NTb/Slow.Horses.S06E05.Sayonara.1080p.ATVP.WEB-DL.DDP5.1.H.264-NTb.mkv 3789544605 4194398 1 1791997839 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Slow.Horses.S06E05.Sayonara.1080p.ATVP.WEB-DL.DDP5.1.H.264-NTb/Slow.Horses.S06E05.Sayonara.1080p.ATVP.WEB-DL.DDP5.1.H.264-NTb.nfo 62 4194400 1 1791997834 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Slow.Horses.S06E03.1080p.WEB.H264-CAKES/slow.horses.s06e03.1080p.web.h264-cakes.mkv 3931160378 4194385 1 1791997887 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Slow.Horses.S06E03.1080p.WEB.H264-CAKES/slow.horses.s06e03.1080p.web.h264-cakes.nfo 2728 4194367 1 1791997882 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Slow.Horses.S06E03.1080p.WEB.H264-CAKES/Sample/slow.horses.s06e03.1080p.web.h264-cakes-sample.mkv 85809338 4194392 1 1791997778 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Slow.Horses.S06E02.PROPER.1080p.WEB.h264-ETHEL/slow.horses.s06e02.proper.1080p.web.h264-ethel.nfo 1023 4194397 1 1791997846 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Slow.Horses.S06E02.PROPER.1080p.WEB.h264-ETHEL/Slow.Horses.S06E02.PROPER.1080p.WEB.h264-ETHEL.mkv 3758255491 4194369 1 1791997854 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Slow.Horses.S06E02.PROPER.1080p.WEB.h264-ETHEL/Sample/slow.horses.s06e02.proper.1080p.web.h264-ethel.sample.mkv 77743753 4194383 1 1791997852 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Avatar.Seven.Havens.S01E03.1080p.WEB.h264-GRACE/Avatar.Seven.Havens.S01E03.1080p.WEB.h264-GRACE.mkv 720261766 4194368 2 1791997651 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Avatar.Seven.Havens.S01E03.1080p.WEB.h264-GRACE/avatar.seven.havens.s01e03.1080p.web.h264-grace.nfo 492 4194377 1 1791997591 +/mnt/felhom-drives/media_a/userdata/media/torrents/tv/Avatar.Seven.Havens.S01E03.1080p.WEB.h264-GRACE/Sample/avatar.seven.havens.s01e03.1080p.web.h264-grace.sample.mkv 21739818 4194370 1 1791997622 +/mnt/felhom-drives/media_a/userdata/media/torrents/movies/Hope.2026.720p.AMZN.WEB-DL.DDP5.1.Atmos.H.264-N1H4L/Hope.2026.720p.AMZN.WEB-DL.DDP5.1.Atmos.H.264-N1H4L.nfo 602 4194405 1 1791997982 +/mnt/felhom-drives/media_a/userdata/media/torrents/movies/Hope.2026.720p.AMZN.WEB-DL.DDP5.1.Atmos.H.264-N1H4L/Hope.2026.720p.AMZN.WEB-DL.DDP5.1.Atmos.H.264-N1H4L.mkv 4697542077 4194391 2 1791997988 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Slow.Horses.S06E04.1080p.WEB.H264-CAKES/slow.horses.s06e04.1080p.web.h264-cakes.mkv 4039934637 4194396 1 1791997987 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Slow.Horses.S06E04.1080p.WEB.H264-CAKES/slow.horses.s06e04.1080p.web.h264-cakes.nfo 2730 4194399 1 1791997757 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Slow.Horses.S06E04.1080p.WEB.H264-CAKES/Sample/slow.horses.s06e04.1080p.web.h264-cakes-sample.mkv 94200139 4194395 1 1791997773 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Slow.Horses.S06E01.1080p.WEB.H264-CAKES/slow.horses.s06e01.1080p.web.h264-cakes.mkv 3859110437 4194387 1 1791997961 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Slow.Horses.S06E01.1080p.WEB.H264-CAKES/slow.horses.s06e01.1080p.web.h264-cakes.nfo 2693 4194388 1 1791997744 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Slow.Horses.S06E01.1080p.WEB.H264-CAKES/Sample/slow.horses.s06e01.1080p.web.h264-cakes-sample.mkv 104128605 4194390 1 1791997759 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Avatar.Seven.Havens.S01E02.1080p.WEB.h264-GRACE/avatar.seven.havens.s01e02.1080p.web.h264-grace.nfo 497 4194374 1 1791997576 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Avatar.Seven.Havens.S01E02.1080p.WEB.h264-GRACE/Avatar.Seven.Havens.S01E02.1080p.WEB.h264-GRACE.mkv 829645981 4194375 2 1791997623 +/mnt/felhom-drives/media_a/userdata/media/transmission/tv/Avatar.Seven.Havens.S01E02.1080p.WEB.h264-GRACE/Sample/avatar.seven.havens.s01e02.1080p.web.h264-grace.sample.mkv 31210978 4194373 1 1791997609 +/mnt/felhom-drives/media_a/userdata/media/transmission/movies/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264.HuNSUB-TRiNiTY/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264.HuNSUB-TRiNiTY.mkv 2819947717 4194408 1 1791998428 +/mnt/felhom-drives/media_a/userdata/media/transmission/movies/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264.HuNSUB-TRiNiTY/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264.HuNSUB-TRiNiTY.nfo 8091 4194409 1 1791998340 +/mnt/felhom-drives/media_a/userdata/media/transmission/movies/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264.HuNSUB-TRiNiTY/Sample/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264.HuNSUB-TRiNiTY-Sample.mkv 22742740 4194411 1 1791998422 +/mnt/felhom-drives/media_a/userdata/media/movies/Hope (2026)/Hope.2026.720p.AMZN.WEB-DL.DDP5.1.Atmos.H.264-N1H4L.mkv 4697542077 4194391 2 1791997988 +/mnt/felhom-drives/media_b/userdata/media/torrents/movies/Matchbox.The.Movie.2026.720p.ATVP.WEB-DL.DDP5.1.Atmos.H.264-FLUX/Matchbox.The.Movie.2026.720p.ATVP.WEB-DL.DDP5.1.Atmos.H.264-FLUX.mkv 3393846457 1048578 2 1791998022 +/mnt/felhom-drives/media_b/userdata/media/torrents/movies/Matchbox.The.Movie.2026.720p.ATVP.WEB-DL.DDP5.1.Atmos.H.264-FLUX/Matchbox.The.Movie.2026.720p.ATVP.WEB-DL.DDP5.1.Atmos.H.264-FLUX.nfo 619 1048579 1 1791998019 +/mnt/felhom-drives/media_b/userdata/media/torrents/tv/American.Horror.Story.S13E01.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E01.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.nfo 68 131101 1 1791997602 +/mnt/felhom-drives/media_b/userdata/media/torrents/tv/American.Horror.Story.S13E01.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E01.13.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.mkv 1314062473 131100 2 1791997680 +/mnt/felhom-drives/media_b/userdata/media/torrents/tv/American.Horror.Story.S13E03.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E03.Rauhnacht.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.mkv 813589489 131110 2 1791997659 +/mnt/felhom-drives/media_b/userdata/media/torrents/tv/American.Horror.Story.S13E03.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E03.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.nfo 68 131113 1 1791997627 +/mnt/felhom-drives/media_b/userdata/media/torrents/tv/American.Horror.Story.S13E07.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E07.The.Baddest.Witch.of.Them.All.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.mkv 956532263 393221 2 1791997699 +/mnt/felhom-drives/media_b/userdata/media/torrents/tv/American.Horror.Story.S13E07.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E07.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.nfo 67 393223 1 1791997690 +/mnt/felhom-drives/media_b/userdata/media/torrents/tv/American.Horror.Story.S13E05.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E05.Date.Night.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.mkv 969653267 393218 2 1791997719 +/mnt/felhom-drives/media_b/userdata/media/torrents/tv/American.Horror.Story.S13E05.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E05.Date.Night.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.nfo 77 393220 1 1791997712 +/mnt/felhom-drives/media_b/userdata/media/movies/Matchbox the Movie (2026)/Matchbox.The.Movie.2026.720p.ATVP.WEB-DL.DDP5.1.Atmos.H.264-FLUX.mkv 3393846457 1048578 2 1791998022 +/mnt/felhom-drives/media_b/userdata/media/movies/WHO (2026)/Who.2026.720p.DSNP.WEB-DL.DDP5.1.H.264.HuNSUB-TRiNiTY.mkv 2819947717 2097156 1 1791998428 +/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E04.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.mkv 1164079593 131107 2 1791997676 +/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E01.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.mkv 1314062473 131100 2 1791997680 +/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E06.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.mkv 913481011 131112 2 1791997757 +/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E07.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.mkv 956532263 393221 2 1791997699 +/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E03.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.mkv 813589489 131110 2 1791997659 +/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E02.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.mkv 1274167804 131103 2 1791997733 +/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E05.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.mkv 969653267 393218 2 1791997719 +/mnt/felhom-drives/media_b/userdata/media/tv/American Horror Story/Season 13/American.Horror.Story.S13E08.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.mkv 1149433990 262146 2 1791997741 +/mnt/felhom-drives/media_b/userdata/media/tv/Slow Horses/Season 6/Slow.Horses.S06E05.Sayonara.1080p.ATVP.WEB-DL.DDP5.1.H.264-NTb.mkv 3789544605 524295 1 1791997839 +/mnt/felhom-drives/media_b/userdata/media/tv/Slow Horses/Season 6/Slow.Horses.S06E01.1080p.WEB.H264-CAKES.mkv 3859110437 524298 1 1791997961 +/mnt/felhom-drives/media_b/userdata/media/tv/Slow Horses/Season 6/Slow.Horses.S06E04.1080p.WEB.H264-CAKES.mkv 4039934637 524299 1 1791997987 +/mnt/felhom-drives/media_b/userdata/media/tv/Slow Horses/Season 6/Slow.Horses.S06E02.PROPER.1080p.WEB.h264-ETHEL.mkv 3758255491 524296 1 1791997854 +/mnt/felhom-drives/media_b/userdata/media/tv/Slow Horses/Season 6/Slow.Horses.S06E03.1080p.WEB.H264-CAKES.mkv 3931160378 524297 1 1791997887 +/mnt/felhom-drives/media_b/userdata/media/transmission/tv/American.Horror.Story.S13E02.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E02.1.13.AM.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.mkv 1274167804 131103 2 1791997733 +/mnt/felhom-drives/media_b/userdata/media/transmission/tv/American.Horror.Story.S13E02.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E02.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.nfo 68 131104 1 1791997605 +/mnt/felhom-drives/media_b/userdata/media/transmission/tv/American.Horror.Story.S13E04.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E04.Is.That.All.There.Is.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.mkv 1164079593 131107 2 1791997676 +/mnt/felhom-drives/media_b/userdata/media/transmission/tv/American.Horror.Story.S13E04.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E04.Is.That.All.There.Is.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.nfo 67 131108 1 1791997615 +/mnt/felhom-drives/media_b/userdata/media/transmission/tv/American.Horror.Story.S13E08.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E08.Hell.No.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.mkv 1149433990 262146 2 1791997741 +/mnt/felhom-drives/media_b/userdata/media/transmission/tv/American.Horror.Story.S13E08.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E08.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB.nfo 67 262147 1 1791997643 +/mnt/felhom-drives/media_b/userdata/media/transmission/tv/American.Horror.Story.S13E06.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E06.The.Return.of.Devils.Night.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.mkv 913481011 131112 2 1791997757 +/mnt/felhom-drives/media_b/userdata/media/transmission/tv/American.Horror.Story.S13E06.1080p.DSNP.WEB-DL.DDP5.1.H.264-PlayWEB/American.Horror.Story.S13E06.The.Return.of.Devils.Night.1080p.DSNP.WEB-DL.DD5.1.H.264-playWEB.nfo 93 131114 1 1791997633 + diff --git a/documentation/audits/media-drive-events-2026-10-14/K1-filesig-diff.txt b/documentation/audits/media-drive-events-2026-10-14/K1-filesig-diff.txt new file mode 100644 index 00000000..e69de29b diff --git a/documentation/audits/media-drive-events-2026-10-14/K1-restart-while-absent.txt b/documentation/audits/media-drive-events-2026-10-14/K1-restart-while-absent.txt new file mode 100644 index 00000000..8d244736 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K1-restart-while-absent.txt @@ -0,0 +1,16 @@ +jellyfin stop -> 200 +jellyfin start -> 200 {'ok': True, 'data': {'state': 'starting'}, 'message': 'Stack jellyfin start requested — state now: starting'} +running healthy started=2026-10-14T20:57:47.941764769Z err= +ls: cannot access '/mnt/felhom-drives/media_b/userdata/media/tv': Input/output error +ls: reading directory '/mnt/felhom-drives/media_b': Input/output error +total 0 +2026/10/14 20:57:47 [INFO] [stacks] Starting stack: jellyfin +2026/10/14 20:57:48 [INFO] [stacks] Stack jellyfin started successfully (took 0.3s) +2026/10/14 20:57:51 [INFO] [stacks] Stack jellyfin post-start status: +2026/10/14 20:57:51 [INFO] [stacks] jellyfin jellyfin/jellyfin:10.11.11 running Up 3 seconds (health: starting) + +jellyfin /Library/Refresh -> 204 +jellyfin scan done 20:58:40 +--- 20:58:41 state + jellyfin: {'Series': 3, 'Movie': 3, 'Episode': 16} ['Amerikai Horror Story', 'Avatar: Seven Havens', 'Hope - Remény', 'Matchbox', 'Roger nyúl a pácban', 'Utolsó befutók'] + jellyfin probe: Played=True diff --git a/documentation/audits/media-drive-events-2026-10-14/K1-settings-before.txt b/documentation/audits/media-drive-events-2026-10-14/K1-settings-before.txt new file mode 100644 index 00000000..ed39f669 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K1-settings-before.txt @@ -0,0 +1,16 @@ +plex prefs: (absent = default) + plex FSEventLibraryUpdatesEnabled value= False default= False + plex FSEventLibraryPartialScanEnabled value= False default= False + plex ScheduledLibraryUpdatesEnabled value= False default= False + plex ScheduledLibraryUpdateInterval value= 3600 default= 3600 + plex autoEmptyTrash value= True default= True + sonarr {'autoUnmonitorPreviouslyDownloadedEpisodes': False, 'recycleBin': '', 'deleteEmptyFolders': False} + radarr {'autoUnmonitorPreviouslyDownloadedMovies': False, 'recycleBin': '', 'deleteEmptyFolders': False} + 2 false +config +data +log +metadata +plugins +root + diff --git a/documentation/audits/media-drive-events-2026-10-14/K1-state-1-before.txt b/documentation/audits/media-drive-events-2026-10-14/K1-state-1-before.txt new file mode 100644 index 00000000..2d003499 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K1-state-1-before.txt @@ -0,0 +1,19 @@ +--- 20:53:46 state + plex movie Hope items=1 parts=1 unavailable=0 + plex movie Matchbox the Movie items=1 parts=1 unavailable=0 + plex movie Who items=1 parts=1 unavailable=0 + plex show American Horror Story items=8 parts=8 unavailable=0 + plex show Avatar: Seven Havens items=3 parts=3 unavailable=0 + plex show Slow Horses items=5 parts=5 unavailable=0 + plex probe: viewCount=1 + jellyfin: {'Series': 3, 'Movie': 3, 'Episode': 16} ['Amerikai Horror Story', 'Avatar: Seven Havens', 'Hope - Remény', 'Matchbox', 'Roger nyúl a pácban', 'Utolsó befutók'] + jellyfin probe: episode gone + sonarr Avatar: Seven Havens monitored=True files=3 path=media_a + sonarr American Horror Story monitored=True files=8 path=media_b + sonarr Slow Horses monitored=True files=5 path=media_b + Avatar: Seven Havens episodes monitored=13/13 hasFile=3 + American Horror Story episodes monitored=13/166 hasFile=8 + Slow Horses episodes monitored=6/39 hasFile=5 + radarr Hope monitored=True files=True path=media_a + radarr Matchbox the Movie monitored=True files=True path=media_b + radarr WHO monitored=True files=True path=media_b diff --git a/documentation/audits/media-drive-events-2026-10-14/K1-state-after-return.txt b/documentation/audits/media-drive-events-2026-10-14/K1-state-after-return.txt new file mode 100644 index 00000000..09124d16 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K1-state-after-return.txt @@ -0,0 +1,19 @@ +--- 21:02:50 state + plex movie Hope items=1 parts=1 unavailable=0 + plex movie Matchbox the Movie items=1 parts=1 unavailable=0 + plex movie Who items=1 parts=1 unavailable=0 + plex show American Horror Story items=8 parts=8 unavailable=0 + plex show Avatar: Seven Havens items=3 parts=3 unavailable=0 + plex show Slow Horses items=5 parts=5 unavailable=0 + plex probe: viewCount=4 + jellyfin: {'Series': 3, 'Movie': 3, 'Episode': 16} ['Amerikai Horror Story', 'Avatar: Seven Havens', 'Hope - Remény', 'Matchbox', 'Roger nyúl a pácban', 'Utolsó befutók'] + jellyfin probe: Played=True + sonarr Avatar: Seven Havens monitored=True files=3 path=media_a + sonarr American Horror Story monitored=True files=8 path=media_b + sonarr Slow Horses monitored=True files=5 path=media_b + Avatar: Seven Havens episodes monitored=13/13 hasFile=3 + American Horror Story episodes monitored=13/166 hasFile=8 + Slow Horses episodes monitored=6/39 hasFile=5 + radarr Hope monitored=True files=True path=media_a + radarr Matchbox the Movie monitored=True files=True path=media_b + radarr WHO monitored=True files=True path=media_b diff --git a/documentation/audits/media-drive-events-2026-10-14/K2-1-disk-added.txt b/documentation/audits/media-drive-events-2026-10-14/K2-1-disk-added.txt new file mode 100644 index 00000000..ffbdd91e --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K2-1-disk-added.txt @@ -0,0 +1,4 @@ +/dev/nvme0n1 938 192 699 22% /mnt/hdd_1 +scsi4: successfully created disk 'nvme-scratch:341/vm-341-disk-5.raw,backup=0,size=50G' +scsi4: nvme-scratch:341/vm-341-disk-5.raw,backup=0,size=50G +/dev/nvme0n1 938 192 699 22% /mnt/hdd_1 diff --git a/documentation/audits/media-drive-events-2026-10-14/K2-2-enrol.txt b/documentation/audits/media-drive-events-2026-10-14/K2-2-enrol.txt new file mode 100644 index 00000000..65579d71 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K2-2-enrol.txt @@ -0,0 +1,12 @@ +23:03:23 candidates -> 200 ['/dev/sde'] +23:03:23 POST /api/storage/init {"device": "/dev/sde", "fstype": "ext4", "mount_name": "media_c", "label": "Media C", "set_default": false, "confirmed": false, "durable_id": ""} -> 200 {"data": {"phase": "formatting", "started": true}, "ok": true} +23:03:25 status -> 200 {"device": "/dev/sde", "durable_id": "", "error": "", "opsign": "", "phase": "mounting", "reason": "", "started_at": "2026-10-14T21:03:23.232663968Z", "updated_at": "2026-10-14T21:03:23.597372998Z", "where": "/mnt/media_c"} +23:03:27 status -> 200 {"device": "/dev/sde", "durable_id": "", "error": "", "opsign": "", "phase": "done", "reason": "", "started_at": "2026-10-14T21:03:23.232663968Z", "updated_at": "2026-10-14T21:03:25.736060363Z", "where": "/mnt/felhom-drives/media_c"} +RESULT done +`-/mnt/felhom-drives/media_c /dev/sde[/felhom-data] +`-/mnt/felhom-drives/media_c /dev/sde[/felhom-data] +audiobooks books comics grimmory metube movies music nzbget photos podcasts tv usenet 2026/10/14 21:03:23 [INFO] [web] storage init "/mnt/media_c" started (device /dev/sde, fs ext4) +2026/10/14 21:03:25 [INFO] [settings] Added storage path: /mnt/felhom-drives/media_c +2026/10/14 21:03:25 [INFO] [web] storage init done: /mnt/felhom-drives/media_c registered (2503ms) +2026/10/14 21:03:26 [INFO] [web] FileBrowser mounts synced (recreated) — 4 storage path(s), config updated + diff --git a/documentation/audits/media-drive-events-2026-10-14/K2-3-drill-bind-c.txt b/documentation/audits/media-drive-events-2026-10-14/K2-3-drill-bind-c.txt new file mode 100644 index 00000000..f4682000 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K2-3-drill-bind-c.txt @@ -0,0 +1,6 @@ +/opt/docker/stacks/sonarr/docker-compose.yml:1 +/opt/docker/stacks/plex/docker-compose.yml:1 +2026/10/14 21:03:59 [INFO] [sync] Updated transmission/docker-compose.yml +2026/10/14 21:03:59 [INFO] [sync] catalog moved b0fd3a033b4a -> 925f3fd9766b (0 new, 6 stack dir(s) changed) +2026/10/14 21:03:59 [INFO] [sync] Catalog sync complete + diff --git a/documentation/audits/media-drive-events-2026-10-14/K2-4-probe-media.txt b/documentation/audits/media-drive-events-2026-10-14/K2-4-probe-media.txt new file mode 100644 index 00000000..b4503705 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K2-4-probe-media.txt @@ -0,0 +1,4 @@ +-rw-r--r-- 1 root root 145093 Oct 14 23:06 /tmp/probe.mkv +/mnt/felhom-drives/media_c/userdata/media/tv/Felhom Probe Show (2026)/Season 01/Felhom Probe Show (2026) - S01E01.mkv 145093 +/mnt/felhom-drives/media_c/userdata/media/movies/Felhom Probe Film (2026)/Felhom Probe Film (2026).mkv 145093 + diff --git a/documentation/audits/media-drive-events-2026-10-14/K2-5-plex-access.txt b/documentation/audits/media-drive-events-2026-10-14/K2-5-plex-access.txt new file mode 100644 index 00000000..ed683f56 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K2-5-plex-access.txt @@ -0,0 +1,5 @@ +no token, from another container: GET /library/sections -> 401 +no token, from another container: GET /:/prefs -> 401 +no token, from another container: GET /identity -> 200 +no token, from inside the Plex container (127.0.0.1): GET /library/sections -> 401 +Preferences.xml keys of interest: PlexOnlineToken=" customConnections=" PlexOnlineUsername=" diff --git a/documentation/audits/media-drive-events-2026-10-14/K2-6-wire-run1.txt b/documentation/audits/media-drive-events-2026-10-14/K2-6-wire-run1.txt new file mode 100644 index 00000000..012a8004 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K2-6-wire-run1.txt @@ -0,0 +1,155 @@ +== folders on media_a: all present +== folders on media_b: all present +== folders on media_c: made ['torrents', 'torrents/tv', 'torrents/movies', 'transmission', 'transmission/tv', 'transmission/movies'] +== qBittorrent http://172.18.0.2:8080 (password: the deploy secret, read from the container's environment) + POST http://172.18.0.2:8080/api/v2/auth/login -> 204 '' + session cookie name: QBT_SID_8080 + GET http://172.18.0.2:8080/api/v2/app/preferences -> 200 {add_stopped_enabled, add_to_top_of_queue, add_trackers, add_trackers_enabled, add_trackers_from_url_enabled, add_trackers_url, add_trackers_url_list, alt_dl_limit, alt_up_limit, alternative_webui_enabled, alternative_webui_path, announce_ip, announce_port, announce_to_all_tiers, announce_to_all_trac} + before: auto_tmm_enabled=True save_path=/mnt/felhom-drives/media_a/userdata/media/torrents temp_path_enabled=True temp_path=/mnt/felhom-drives/media_a/userdata/media/torrents/.temp + POST http://172.18.0.2:8080/api/v2/app/setPreferences -> 200 '' + GET http://172.18.0.2:8080/api/v2/app/preferences -> 200 {add_stopped_enabled, add_to_top_of_queue, add_trackers, add_trackers_enabled, add_trackers_from_url_enabled, add_trackers_url, add_trackers_url_list, alt_dl_limit, alt_up_limit, alternative_webui_enabled, alternative_webui_path, announce_ip, announce_port, announce_to_all_tiers, announce_to_all_trac} + after : auto_tmm_enabled=True (a category's save path applies only in automatic mode) + GET http://172.18.0.2:8080/api/v2/torrents/categories -> 200 {movies-drive-a, movies-drive-b, tv-drive-a, tv-drive-b} + POST http://172.18.0.2:8080/api/v2/torrents/editCategory -> 200 '' + POST http://172.18.0.2:8080/api/v2/torrents/editCategory -> 200 '' + POST http://172.18.0.2:8080/api/v2/torrents/editCategory -> 200 '' + POST http://172.18.0.2:8080/api/v2/torrents/editCategory -> 200 '' + POST http://172.18.0.2:8080/api/v2/torrents/createCategory -> 200 '' + POST http://172.18.0.2:8080/api/v2/torrents/createCategory -> 200 '' + GET http://172.18.0.2:8080/api/v2/torrents/categories -> 200 {movies-drive-a, movies-drive-b, movies-drive-c, tv-drive-a, tv-drive-b, tv-drive-c} + category movies-drive-a: savePath=/mnt/felhom-drives/media_a/userdata/media/torrents/movies downloadPath=/mnt/felhom-drives/media_a/userdata/media/torrents/.temp + category movies-drive-b: savePath=/mnt/felhom-drives/media_b/userdata/media/torrents/movies downloadPath=/mnt/felhom-drives/media_b/userdata/media/torrents/.temp + category movies-drive-c: savePath=/mnt/felhom-drives/media_c/userdata/media/torrents/movies downloadPath=/mnt/felhom-drives/media_c/userdata/media/torrents/.temp + category tv-drive-a: savePath=/mnt/felhom-drives/media_a/userdata/media/torrents/tv downloadPath=/mnt/felhom-drives/media_a/userdata/media/torrents/.temp + category tv-drive-b: savePath=/mnt/felhom-drives/media_b/userdata/media/torrents/tv downloadPath=/mnt/felhom-drives/media_b/userdata/media/torrents/.temp + category tv-drive-c: savePath=/mnt/felhom-drives/media_c/userdata/media/torrents/tv downloadPath=/mnt/felhom-drives/media_c/userdata/media/torrents/.temp +== Transmission http://172.18.0.3:9091/transmission/rpc (password: the deploy secret, read from the container's environment) + POST http://172.18.0.3:9091/transmission/rpc -> 409 '

409: Conflict

Your request had an invalid session_id header.

To fix this, follow these steps:

  1. + POST http://172.18.0.3:9091/transmission/rpc -> 200 {arguments, result} + before: {"download-dir": "/mnt/felhom-drives/media_a/userdata/media/transmission/complete", "incomplete-dir": "/mnt/felhom-drives/media_a/userdata/media/transmission/incomplete", "incomplete-dir-enabled": false, "rename-partial-files": true} + POST http://172.18.0.3:9091/transmission/rpc -> 409 '

    409: Conflict

    Your request had an invalid session_id header.

    To fix this, follow these steps:

    1. + POST http://172.18.0.3:9091/transmission/rpc -> 200 {arguments, result} + POST http://172.18.0.3:9091/transmission/rpc -> 409 '

      409: Conflict

      Your request had an invalid session_id header.

      To fix this, follow these steps:

      1. + POST http://172.18.0.3:9091/transmission/rpc -> 200 {arguments, result} + after : {"download-dir": "/mnt/felhom-drives/media_a/userdata/media/transmission/complete", "incomplete-dir-enabled": false, "rename-partial-files": true} (one incomplete folder for all torrents would be on drive A) + POST http://172.18.0.3:9091/transmission/rpc -> 409 '

        409: Conflict

        Your request had an invalid session_id header.

        To fix this, follow these steps:

        1. + POST http://172.18.0.3:9091/transmission/rpc -> 200 {arguments, result} + probe Host: transmission:9091 -> HTTP 200 {'arguments': {'version': '4.1.3 (838877323f)'}, 'result': 'success'} +== sonarr http://172.18.0.5:8989/api/v3 (key: in /config/config.xml, read from the config volume) + GET http://172.18.0.5:8989/api/v3/tag -> 200 [list of 3] + POST http://172.18.0.5:8989/api/v3/tag -> 201 {id, label} + tags: {'driva-a': 3, 'drive-a': 1, 'drive-b': 2, 'drive-c': 4} + GET http://172.18.0.5:8989/api/v3/rootfolder -> 200 [list of 2] + POST http://172.18.0.5:8989/api/v3/rootfolder -> 201 {accessible, freeSpace, id, path, unmappedFolders} + GET http://172.18.0.5:8989/api/v3/rootfolder -> 200 [list of 3] + root folder: /mnt/felhom-drives/media_a/userdata/media/tv accessible=True freeSpace=69873070080 + root folder: /mnt/felhom-drives/media_b/userdata/media/tv accessible=True freeSpace=65554092032 + root folder: /mnt/felhom-drives/media_c/userdata/media/tv accessible=True freeSpace=49817907200 + GET http://172.18.0.5:8989/api/v3/downloadclient/schema -> 200 [list of 18] + GET http://172.18.0.5:8989/api/v3/downloadclient -> 200 [list of 4] + client qBittorrent drive-a: exists — left as it is + client Transmission drive-a: exists — left as it is + client qBittorrent drive-b: exists — left as it is + client Transmission drive-b: exists — left as it is + POST http://172.18.0.5:8989/api/v3/downloadclient -> 201 {configContract, enable, fields, id, implementation, implementationName, infoLink, name, priority, protocol, removeCompletedDownloads, removeFailedDownloads, tags} + POST http://172.18.0.5:8989/api/v3/downloadclient -> 201 {configContract, enable, fields, id, implementation, implementationName, infoLink, name, priority, protocol, removeCompletedDownloads, removeFailedDownloads, tags} + GET http://172.18.0.5:8989/api/v3/downloadclient -> 200 [list of 6] + client qBittorrent drive-a: impl=QBittorrent enable=True tags=[1] host=qbittorrent category=tv-drive-a directory=None + client qBittorrent drive-b: impl=QBittorrent enable=True tags=[2] host=qbittorrent category=tv-drive-b directory=None + client qBittorrent drive-c: impl=QBittorrent enable=True tags=[4] host=qbittorrent category=tv-drive-c directory=None + client Transmission drive-a: impl=Transmission enable=True tags=[1] host=transmission category= directory=/mnt/felhom-drives/media_a/userdata/media/transmission/tv + client Transmission drive-b: impl=Transmission enable=True tags=[2] host=transmission category= directory=/mnt/felhom-drives/media_b/userdata/media/transmission/tv + client Transmission drive-c: impl=Transmission enable=True tags=[4] host=transmission category= directory=/mnt/felhom-drives/media_c/userdata/media/transmission/tv + GET http://172.18.0.5:8989/api/v3/config/mediamanagement -> 200 {autoUnmonitorPreviouslyDownloadedEpisodes, chmodFolder, chownGroup, copyUsingHardlinks, createEmptySeriesFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, episodeTitleRequired, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, recycleBin, recy} + before: minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True + PUT http://172.18.0.5:8989/api/v3/config/mediamanagement/1 -> 202 {autoUnmonitorPreviouslyDownloadedEpisodes, chmodFolder, chownGroup, copyUsingHardlinks, createEmptySeriesFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, episodeTitleRequired, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, recycleBin, recy} + GET http://172.18.0.5:8989/api/v3/config/mediamanagement -> 200 {autoUnmonitorPreviouslyDownloadedEpisodes, chmodFolder, chownGroup, copyUsingHardlinks, createEmptySeriesFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, episodeTitleRequired, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, recycleBin, recy} + after : minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True (default kept) +== radarr http://172.18.0.7:7878/api/v3 (key: in /config/config.xml, read from the config volume) + GET http://172.18.0.7:7878/api/v3/tag -> 200 [list of 2] + POST http://172.18.0.7:7878/api/v3/tag -> 201 {id, label} + tags: {'drive-a': 1, 'drive-b': 2, 'drive-c': 3} + GET http://172.18.0.7:7878/api/v3/rootfolder -> 200 [list of 2] + POST http://172.18.0.7:7878/api/v3/rootfolder -> 201 {accessible, freeSpace, id, path, unmappedFolders} + GET http://172.18.0.7:7878/api/v3/rootfolder -> 200 [list of 3] + root folder: /mnt/felhom-drives/media_a/userdata/media/movies accessible=True freeSpace=69873070080 + root folder: /mnt/felhom-drives/media_b/userdata/media/movies accessible=True freeSpace=65554092032 + root folder: /mnt/felhom-drives/media_c/userdata/media/movies accessible=True freeSpace=49817907200 + GET http://172.18.0.7:7878/api/v3/downloadclient/schema -> 200 [list of 19] + GET http://172.18.0.7:7878/api/v3/downloadclient -> 200 [list of 4] + client qBittorrent drive-a: exists — left as it is + client Transmission drive-a: exists — left as it is + client qBittorrent drive-b: exists — left as it is + client Transmission drive-b: exists — left as it is + POST http://172.18.0.7:7878/api/v3/downloadclient -> 201 {configContract, enable, fields, id, implementation, implementationName, infoLink, name, priority, protocol, removeCompletedDownloads, removeFailedDownloads, tags} + POST http://172.18.0.7:7878/api/v3/downloadclient -> 201 {configContract, enable, fields, id, implementation, implementationName, infoLink, name, priority, protocol, removeCompletedDownloads, removeFailedDownloads, tags} + GET http://172.18.0.7:7878/api/v3/downloadclient -> 200 [list of 6] + client qBittorrent drive-a: impl=QBittorrent enable=True tags=[1] host=qbittorrent category=movies-drive-a directory=None + client qBittorrent drive-b: impl=QBittorrent enable=True tags=[2] host=qbittorrent category=movies-drive-b directory=None + client qBittorrent drive-c: impl=QBittorrent enable=True tags=[3] host=qbittorrent category=movies-drive-c directory=None + client Transmission drive-a: impl=Transmission enable=True tags=[1] host=transmission category= directory=/mnt/felhom-drives/media_a/userdata/media/transmission/movies + client Transmission drive-b: impl=Transmission enable=True tags=[2] host=transmission category= directory=/mnt/felhom-drives/media_b/userdata/media/transmission/movies + client Transmission drive-c: impl=Transmission enable=True tags=[3] host=transmission category= directory=/mnt/felhom-drives/media_c/userdata/media/transmission/movies + GET http://172.18.0.7:7878/api/v3/config/mediamanagement -> 200 {autoRenameFolders, autoUnmonitorPreviouslyDownloadedMovies, chmodFolder, chownGroup, copyUsingHardlinks, createEmptyMovieFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, pathsDefaultStatic, re} + before: minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True + PUT http://172.18.0.7:7878/api/v3/config/mediamanagement/1 -> 202 {autoRenameFolders, autoUnmonitorPreviouslyDownloadedMovies, chmodFolder, chownGroup, copyUsingHardlinks, createEmptyMovieFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, pathsDefaultStatic, re} + GET http://172.18.0.7:7878/api/v3/config/mediamanagement -> 200 {autoRenameFolders, autoUnmonitorPreviouslyDownloadedMovies, chmodFolder, chownGroup, copyUsingHardlinks, createEmptyMovieFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, pathsDefaultStatic, re} + after : minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True (default kept) +== done; Transmission reachable by its container name: True +== prowlarr http://172.18.0.13:9696/api/v1 (key: in /config/config.xml, read from the config volume) + GET http://172.18.0.13:9696/api/v1/applications/schema -> 200 [list of 7] + GET http://172.18.0.13:9696/api/v1/applications -> 200 [list of 2] + application Sonarr: exists — left as it is + application Radarr: exists — left as it is + GET http://172.18.0.13:9696/api/v1/applications -> 200 [list of 2] + application Radarr: syncLevel=fullSync prowlarrUrl=http://prowlarr:9696 baseUrl=http://radarr:7878 syncCategories=[2000, 2010, 2020, 2030, 2040, 2045, 2050, 2060, 2070, 2080, 2090] + application Sonarr: syncLevel=fullSync prowlarrUrl=http://prowlarr:9696 baseUrl=http://sonarr:8989 syncCategories=[5000, 5010, 5020, 5030, 5040, 5045, 5050, 5090] + GET http://172.18.0.13:9696/api/v1/indexer -> 200 [list of 1] + indexers in Prowlarr: ['nCore'] + POST http://172.18.0.13:9696/api/v1/command -> 201 {body, clientUserAgent, commandName, id, name, priority, queued, sendUpdatesToClient, started, stateChangeTime, status, trigger, updateScheduledTask} + GET http://172.18.0.13:9696/api/v1/command/53 -> 200 {body, clientUserAgent, commandName, duration, ended, id, message, name, priority, queued, sendUpdatesToClient, started, stateChangeTime, status, trigger, updateScheduledTask} + ApplicationIndexerSync -> status=completed result=None message=Completed + GET http://172.18.0.5:8989/api/v3/indexer -> 200 [list of 1] + sonarr now holds 1 indexer(s): [('nCore (Prowlarr)', 'Torznab')] + GET http://172.18.0.7:7878/api/v3/indexer -> 200 [list of 1] + radarr now holds 1 indexer(s): [('nCore (Prowlarr)', 'Torznab')] +== prowlarr part done +== plex http://172.18.0.12:32400 (token: the server's own PlexOnlineToken in Preferences.xml) + GET http://172.18.0.12:32400/library/sections -> 200 {MediaContainer} + PUT http://172.18.0.12:32400/library/sections/1?name=Movies&type=movie&agent=tv.plex.agents.movie&scanner=Plex+Movie&language=en-US&location=%2Fmnt%2Ffelhom-drives%2Fmedia_a%2Fuserdata%2Fmedia%2Fmovies&location=%2Fmnt%2Ffelhom-drives%2Fmedia_b%2Fuserdata%2Fmedia%2Fmovies&location=%2Fmnt%2Ffelhom-drives%2Fmedia_c%2Fuserdata%2Fmedia%2Fmovies -> 200 '' + library Movies: added ['/mnt/felhom-drives/media_c/userdata/media/movies'] -> 200 + GET http://172.18.0.12:32400/library/sections/1/refresh -> 200 '' + library Movies: scan started -> 200 + PUT http://172.18.0.12:32400/library/sections/2?name=TV+Shows&type=show&agent=tv.plex.agents.series&scanner=Plex+TV+Series&language=en-US&location=%2Fmnt%2Ffelhom-drives%2Fmedia_a%2Fuserdata%2Fmedia%2Ftv&location=%2Fmnt%2Ffelhom-drives%2Fmedia_b%2Fuserdata%2Fmedia%2Ftv&location=%2Fmnt%2Ffelhom-drives%2Fmedia_c%2Fuserdata%2Fmedia%2Ftv -> 200 '' + library TV Shows: added ['/mnt/felhom-drives/media_c/userdata/media/tv'] -> 200 + GET http://172.18.0.12:32400/library/sections/2/refresh -> 200 '' + library TV Shows: scan started -> 200 + GET http://172.18.0.12:32400/:/prefs -> 200 {MediaContainer} + PUT http://172.18.0.12:32400/:/prefs?FSEventLibraryUpdatesEnabled=1 -> 200 '' + pref FSEventLibraryUpdatesEnabled (R-1037: scan when a change is detected) -> 200 + GET http://172.18.0.12:32400/:/prefs -> 200 {MediaContainer} + PUT http://172.18.0.12:32400/:/prefs?FSEventLibraryPartialScanEnabled=1 -> 200 '' + pref FSEventLibraryPartialScanEnabled (R-1037: scan when a change is detected) -> 200 + GET http://172.18.0.12:32400/library/sections -> 200 {MediaContainer} + GET http://172.18.0.12:32400/library/sections/1/all -> 200 {MediaContainer} + library Movies: folders=['media_a', 'media_b', 'media_c'] items=['Felhom Probe Film', 'Hope', 'Matchbox the Movie', 'Who'] + GET http://172.18.0.12:32400/library/sections/2/all -> 200 {MediaContainer} + library TV Shows: folders=['media_a', 'media_b', 'media_c'] items=['American Horror Story', 'Avatar: Seven Havens', 'Felhom Probe Show', 'Slow Horses'] + POST http://172.18.0.14:8096/Users/AuthenticateByName -> 200 {AccessToken, ServerId, SessionInfo, User} +== jellyfin http://172.18.0.14:8096 (an admin session: the box made the first admin itself, so it holds the password) + GET http://172.18.0.14:8096/Auth/Keys -> 200 {Items, StartIndex, TotalRecordCount} + POST http://172.18.0.14:8096/Auth/Keys?app=felhom -> 204 '' + API key for the box (POST /Auth/Keys?app=felhom) -> 204 (needs an admin session; without one, one household step) + GET http://172.18.0.14:8096/Library/VirtualFolders -> 200 [list of 2] + POST http://172.18.0.14:8096/Library/VirtualFolders/Paths?refreshLibrary=false -> 204 '' + library Movies: added /mnt/felhom-drives/media_c/userdata/media/movies -> 204 + POST http://172.18.0.14:8096/Library/VirtualFolders/Paths?refreshLibrary=false -> 204 '' + library TV Shows: added /mnt/felhom-drives/media_c/userdata/media/tv -> 204 + POST http://172.18.0.14:8096/Library/Refresh -> 204 '' + scan -> 204 + GET http://172.18.0.14:8096/Library/VirtualFolders -> 200 [list of 2] + library Movies: folders=['media_a', 'media_b', 'media_c'] + library TV Shows: folders=['media_a', 'media_b', 'media_c'] + GET http://172.18.0.14:8096/Items?Recursive=true&IncludeItemTypes=Series,Movie -> 200 {Items, StartIndex, TotalRecordCount} + items: ['Amerikai Horror Story', 'Avatar: Seven Havens', 'Felhom Probe Film (2026)', 'Felhom Probe Show (2026)', 'Hope - Remény', 'Matchbox', 'Roger nyúl a pácban', 'Utolsó befutók'] +== all parts done diff --git a/documentation/audits/media-drive-events-2026-10-14/K2-7-wire-run2.txt b/documentation/audits/media-drive-events-2026-10-14/K2-7-wire-run2.txt new file mode 100644 index 00000000..78eaf245 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K2-7-wire-run2.txt @@ -0,0 +1,140 @@ +== folders on media_a: all present +== folders on media_b: all present +== folders on media_c: all present +== qBittorrent http://172.18.0.2:8080 (password: the deploy secret, read from the container's environment) + POST http://172.18.0.2:8080/api/v2/auth/login -> 204 '' + session cookie name: QBT_SID_8080 + GET http://172.18.0.2:8080/api/v2/app/preferences -> 200 {add_stopped_enabled, add_to_top_of_queue, add_trackers, add_trackers_enabled, add_trackers_from_url_enabled, add_trackers_url, add_trackers_url_list, alt_dl_limit, alt_up_limit, alternative_webui_enabled, alternative_webui_path, announce_ip, announce_port, announce_to_all_tiers, announce_to_all_trac} + before: auto_tmm_enabled=True save_path=/mnt/felhom-drives/media_a/userdata/media/torrents temp_path_enabled=True temp_path=/mnt/felhom-drives/media_a/userdata/media/torrents/.temp + POST http://172.18.0.2:8080/api/v2/app/setPreferences -> 200 '' + GET http://172.18.0.2:8080/api/v2/app/preferences -> 200 {add_stopped_enabled, add_to_top_of_queue, add_trackers, add_trackers_enabled, add_trackers_from_url_enabled, add_trackers_url, add_trackers_url_list, alt_dl_limit, alt_up_limit, alternative_webui_enabled, alternative_webui_path, announce_ip, announce_port, announce_to_all_tiers, announce_to_all_trac} + after : auto_tmm_enabled=True (a category's save path applies only in automatic mode) + GET http://172.18.0.2:8080/api/v2/torrents/categories -> 200 {movies-drive-a, movies-drive-b, movies-drive-c, tv-drive-a, tv-drive-b, tv-drive-c} + POST http://172.18.0.2:8080/api/v2/torrents/editCategory -> 200 '' + POST http://172.18.0.2:8080/api/v2/torrents/editCategory -> 200 '' + POST http://172.18.0.2:8080/api/v2/torrents/editCategory -> 200 '' + POST http://172.18.0.2:8080/api/v2/torrents/editCategory -> 200 '' + POST http://172.18.0.2:8080/api/v2/torrents/editCategory -> 200 '' + POST http://172.18.0.2:8080/api/v2/torrents/editCategory -> 200 '' + GET http://172.18.0.2:8080/api/v2/torrents/categories -> 200 {movies-drive-a, movies-drive-b, movies-drive-c, tv-drive-a, tv-drive-b, tv-drive-c} + category movies-drive-a: savePath=/mnt/felhom-drives/media_a/userdata/media/torrents/movies downloadPath=/mnt/felhom-drives/media_a/userdata/media/torrents/.temp + category movies-drive-b: savePath=/mnt/felhom-drives/media_b/userdata/media/torrents/movies downloadPath=/mnt/felhom-drives/media_b/userdata/media/torrents/.temp + category movies-drive-c: savePath=/mnt/felhom-drives/media_c/userdata/media/torrents/movies downloadPath=/mnt/felhom-drives/media_c/userdata/media/torrents/.temp + category tv-drive-a: savePath=/mnt/felhom-drives/media_a/userdata/media/torrents/tv downloadPath=/mnt/felhom-drives/media_a/userdata/media/torrents/.temp + category tv-drive-b: savePath=/mnt/felhom-drives/media_b/userdata/media/torrents/tv downloadPath=/mnt/felhom-drives/media_b/userdata/media/torrents/.temp + category tv-drive-c: savePath=/mnt/felhom-drives/media_c/userdata/media/torrents/tv downloadPath=/mnt/felhom-drives/media_c/userdata/media/torrents/.temp +== Transmission http://172.18.0.3:9091/transmission/rpc (password: the deploy secret, read from the container's environment) + POST http://172.18.0.3:9091/transmission/rpc -> 409 '

          409: Conflict

          Your request had an invalid session_id header.

          To fix this, follow these steps:

          1. + POST http://172.18.0.3:9091/transmission/rpc -> 200 {arguments, result} + before: {"download-dir": "/mnt/felhom-drives/media_a/userdata/media/transmission/complete", "incomplete-dir": "/mnt/felhom-drives/media_a/userdata/media/transmission/incomplete", "incomplete-dir-enabled": false, "rename-partial-files": true} + POST http://172.18.0.3:9091/transmission/rpc -> 409 '

            409: Conflict

            Your request had an invalid session_id header.

            To fix this, follow these steps:

            1. + POST http://172.18.0.3:9091/transmission/rpc -> 200 {arguments, result} + POST http://172.18.0.3:9091/transmission/rpc -> 409 '

              409: Conflict

              Your request had an invalid session_id header.

              To fix this, follow these steps:

              1. + POST http://172.18.0.3:9091/transmission/rpc -> 200 {arguments, result} + after : {"download-dir": "/mnt/felhom-drives/media_a/userdata/media/transmission/complete", "incomplete-dir-enabled": false, "rename-partial-files": true} (one incomplete folder for all torrents would be on drive A) + POST http://172.18.0.3:9091/transmission/rpc -> 409 '

                409: Conflict

                Your request had an invalid session_id header.

                To fix this, follow these steps:

                1. + POST http://172.18.0.3:9091/transmission/rpc -> 200 {arguments, result} + probe Host: transmission:9091 -> HTTP 200 {'arguments': {'version': '4.1.3 (838877323f)'}, 'result': 'success'} +== sonarr http://172.18.0.5:8989/api/v3 (key: in /config/config.xml, read from the config volume) + GET http://172.18.0.5:8989/api/v3/tag -> 200 [list of 4] + tags: {'driva-a': 3, 'drive-a': 1, 'drive-b': 2, 'drive-c': 4} + GET http://172.18.0.5:8989/api/v3/rootfolder -> 200 [list of 3] + GET http://172.18.0.5:8989/api/v3/rootfolder -> 200 [list of 3] + root folder: /mnt/felhom-drives/media_a/userdata/media/tv accessible=True freeSpace=69873070080 + root folder: /mnt/felhom-drives/media_b/userdata/media/tv accessible=True freeSpace=65554092032 + root folder: /mnt/felhom-drives/media_c/userdata/media/tv accessible=True freeSpace=49817907200 + GET http://172.18.0.5:8989/api/v3/downloadclient/schema -> 200 [list of 18] + GET http://172.18.0.5:8989/api/v3/downloadclient -> 200 [list of 6] + client qBittorrent drive-a: exists — left as it is + client Transmission drive-a: exists — left as it is + client qBittorrent drive-b: exists — left as it is + client Transmission drive-b: exists — left as it is + client qBittorrent drive-c: exists — left as it is + client Transmission drive-c: exists — left as it is + GET http://172.18.0.5:8989/api/v3/downloadclient -> 200 [list of 6] + client qBittorrent drive-a: impl=QBittorrent enable=True tags=[1] host=qbittorrent category=tv-drive-a directory=None + client qBittorrent drive-b: impl=QBittorrent enable=True tags=[2] host=qbittorrent category=tv-drive-b directory=None + client qBittorrent drive-c: impl=QBittorrent enable=True tags=[4] host=qbittorrent category=tv-drive-c directory=None + client Transmission drive-a: impl=Transmission enable=True tags=[1] host=transmission category= directory=/mnt/felhom-drives/media_a/userdata/media/transmission/tv + client Transmission drive-b: impl=Transmission enable=True tags=[2] host=transmission category= directory=/mnt/felhom-drives/media_b/userdata/media/transmission/tv + client Transmission drive-c: impl=Transmission enable=True tags=[4] host=transmission category= directory=/mnt/felhom-drives/media_c/userdata/media/transmission/tv + GET http://172.18.0.5:8989/api/v3/config/mediamanagement -> 200 {autoUnmonitorPreviouslyDownloadedEpisodes, chmodFolder, chownGroup, copyUsingHardlinks, createEmptySeriesFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, episodeTitleRequired, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, recycleBin, recy} + before: minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True + PUT http://172.18.0.5:8989/api/v3/config/mediamanagement/1 -> 202 {autoUnmonitorPreviouslyDownloadedEpisodes, chmodFolder, chownGroup, copyUsingHardlinks, createEmptySeriesFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, episodeTitleRequired, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, recycleBin, recy} + GET http://172.18.0.5:8989/api/v3/config/mediamanagement -> 200 {autoUnmonitorPreviouslyDownloadedEpisodes, chmodFolder, chownGroup, copyUsingHardlinks, createEmptySeriesFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, episodeTitleRequired, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, recycleBin, recy} + after : minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True (default kept) +== radarr http://172.18.0.7:7878/api/v3 (key: in /config/config.xml, read from the config volume) + GET http://172.18.0.7:7878/api/v3/tag -> 200 [list of 3] + tags: {'drive-a': 1, 'drive-b': 2, 'drive-c': 3} + GET http://172.18.0.7:7878/api/v3/rootfolder -> 200 [list of 3] + GET http://172.18.0.7:7878/api/v3/rootfolder -> 200 [list of 3] + root folder: /mnt/felhom-drives/media_a/userdata/media/movies accessible=True freeSpace=69873070080 + root folder: /mnt/felhom-drives/media_b/userdata/media/movies accessible=True freeSpace=65554092032 + root folder: /mnt/felhom-drives/media_c/userdata/media/movies accessible=True freeSpace=49817907200 + GET http://172.18.0.7:7878/api/v3/downloadclient/schema -> 200 [list of 19] + GET http://172.18.0.7:7878/api/v3/downloadclient -> 200 [list of 6] + client qBittorrent drive-a: exists — left as it is + client Transmission drive-a: exists — left as it is + client qBittorrent drive-b: exists — left as it is + client Transmission drive-b: exists — left as it is + client qBittorrent drive-c: exists — left as it is + client Transmission drive-c: exists — left as it is + GET http://172.18.0.7:7878/api/v3/downloadclient -> 200 [list of 6] + client qBittorrent drive-a: impl=QBittorrent enable=True tags=[1] host=qbittorrent category=movies-drive-a directory=None + client qBittorrent drive-b: impl=QBittorrent enable=True tags=[2] host=qbittorrent category=movies-drive-b directory=None + client qBittorrent drive-c: impl=QBittorrent enable=True tags=[3] host=qbittorrent category=movies-drive-c directory=None + client Transmission drive-a: impl=Transmission enable=True tags=[1] host=transmission category= directory=/mnt/felhom-drives/media_a/userdata/media/transmission/movies + client Transmission drive-b: impl=Transmission enable=True tags=[2] host=transmission category= directory=/mnt/felhom-drives/media_b/userdata/media/transmission/movies + client Transmission drive-c: impl=Transmission enable=True tags=[3] host=transmission category= directory=/mnt/felhom-drives/media_c/userdata/media/transmission/movies + GET http://172.18.0.7:7878/api/v3/config/mediamanagement -> 200 {autoRenameFolders, autoUnmonitorPreviouslyDownloadedMovies, chmodFolder, chownGroup, copyUsingHardlinks, createEmptyMovieFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, pathsDefaultStatic, re} + before: minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True + PUT http://172.18.0.7:7878/api/v3/config/mediamanagement/1 -> 202 {autoRenameFolders, autoUnmonitorPreviouslyDownloadedMovies, chmodFolder, chownGroup, copyUsingHardlinks, createEmptyMovieFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, pathsDefaultStatic, re} + GET http://172.18.0.7:7878/api/v3/config/mediamanagement -> 200 {autoRenameFolders, autoUnmonitorPreviouslyDownloadedMovies, chmodFolder, chownGroup, copyUsingHardlinks, createEmptyMovieFolders, deleteEmptyFolders, downloadPropersAndRepacks, enableMediaInfo, extraFileExtensions, fileDate, id, importExtraFiles, minimumFreeSpaceWhenImporting, pathsDefaultStatic, re} + after : minimumFreeSpaceWhenImporting=10240 MB, copyUsingHardlinks=True (default kept) +== done; Transmission reachable by its container name: True +== prowlarr http://172.18.0.13:9696/api/v1 (key: in /config/config.xml, read from the config volume) + GET http://172.18.0.13:9696/api/v1/applications/schema -> 200 [list of 7] + GET http://172.18.0.13:9696/api/v1/applications -> 200 [list of 2] + application Sonarr: exists — left as it is + application Radarr: exists — left as it is + GET http://172.18.0.13:9696/api/v1/applications -> 200 [list of 2] + application Radarr: syncLevel=fullSync prowlarrUrl=http://prowlarr:9696 baseUrl=http://radarr:7878 syncCategories=[2000, 2010, 2020, 2030, 2040, 2045, 2050, 2060, 2070, 2080, 2090] + application Sonarr: syncLevel=fullSync prowlarrUrl=http://prowlarr:9696 baseUrl=http://sonarr:8989 syncCategories=[5000, 5010, 5020, 5030, 5040, 5045, 5050, 5090] + GET http://172.18.0.13:9696/api/v1/indexer -> 200 [list of 1] + indexers in Prowlarr: ['nCore'] + POST http://172.18.0.13:9696/api/v1/command -> 201 {body, clientUserAgent, commandName, id, name, priority, queued, sendUpdatesToClient, status, trigger, updateScheduledTask} + GET http://172.18.0.13:9696/api/v1/command/54 -> 200 {body, clientUserAgent, commandName, duration, ended, id, message, name, priority, queued, sendUpdatesToClient, started, stateChangeTime, status, trigger, updateScheduledTask} + ApplicationIndexerSync -> status=completed result=None message=Completed + GET http://172.18.0.5:8989/api/v3/indexer -> 200 [list of 1] + sonarr now holds 1 indexer(s): [('nCore (Prowlarr)', 'Torznab')] + GET http://172.18.0.7:7878/api/v3/indexer -> 200 [list of 1] + radarr now holds 1 indexer(s): [('nCore (Prowlarr)', 'Torznab')] +== prowlarr part done +== plex http://172.18.0.12:32400 (token: the server's own PlexOnlineToken in Preferences.xml) + GET http://172.18.0.12:32400/library/sections -> 200 {MediaContainer} + library Movies: every drive's folder already in it — left as it is + library TV Shows: every drive's folder already in it — left as it is + GET http://172.18.0.12:32400/:/prefs -> 200 {MediaContainer} + pref FSEventLibraryUpdatesEnabled=True already + GET http://172.18.0.12:32400/:/prefs -> 200 {MediaContainer} + pref FSEventLibraryPartialScanEnabled=True already + GET http://172.18.0.12:32400/library/sections -> 200 {MediaContainer} + GET http://172.18.0.12:32400/library/sections/1/all -> 200 {MediaContainer} + library Movies: folders=['media_a', 'media_b', 'media_c'] items=['Felhom Probe Film', 'Hope', 'Matchbox the Movie', 'Who'] + GET http://172.18.0.12:32400/library/sections/2/all -> 200 {MediaContainer} + library TV Shows: folders=['media_a', 'media_b', 'media_c'] items=['American Horror Story', 'Avatar: Seven Havens', 'Felhom Probe Show', 'Slow Horses'] + POST http://172.18.0.14:8096/Users/AuthenticateByName -> 200 {AccessToken, ServerId, SessionInfo, User} +== jellyfin http://172.18.0.14:8096 (an admin session: the box made the first admin itself, so it holds the password) + GET http://172.18.0.14:8096/Auth/Keys -> 200 {Items, StartIndex, TotalRecordCount} + API key for the box: exists + GET http://172.18.0.14:8096/Library/VirtualFolders -> 200 [list of 2] + library Movies: every drive's folder already in it — left as it is + library TV Shows: every drive's folder already in it — left as it is + POST http://172.18.0.14:8096/Library/Refresh -> 204 '' + scan -> 204 + GET http://172.18.0.14:8096/Library/VirtualFolders -> 200 [list of 2] + library Movies: folders=['media_a', 'media_b', 'media_c'] + library TV Shows: folders=['media_a', 'media_b', 'media_c'] + GET http://172.18.0.14:8096/Items?Recursive=true&IncludeItemTypes=Series,Movie -> 200 {Items, StartIndex, TotalRecordCount} + items: ['Amerikai Horror Story', 'Avatar: Seven Havens', 'Felhom Probe Film (2026)', 'Felhom Probe Show (2026)', 'Hope - Remény', 'Matchbox', 'Roger nyúl a pácban', 'Utolsó befutók'] +== all parts done diff --git a/documentation/audits/media-drive-events-2026-10-14/K3-1-create.txt b/documentation/audits/media-drive-events-2026-10-14/K3-1-create.txt new file mode 100644 index 00000000..7fbc7fb2 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K3-1-create.txt @@ -0,0 +1,23 @@ +qbit add -> {'added_torrent_ids': ['522b2e41bd4a656a1b97b26491091a72828ca867'], 'failure_count': 0, 'pending_count': 0, 'success_count': 1} +trans add -> {"arguments": {"torrent-added": {"hashString": "241e4478e835637208d9c8f19b210ee24fb3dc78", "id": 9, "name": "k3-own-bytes-trans"}}, "result": "success"} + ('qbit', 'media_a', 'Avatar.Seven.Havens.S01E01.108', 'queuedUP', 1) + ('qbit', 'media_a', 'Avatar.Seven.Havens.S01E03.108', 'queuedUP', 1) + ('qbit', 'media_a', 'Hope.2026.720p.AMZN.WEB-DL.DDP', 'stalledUP', 1) + ('qbit', 'media_a', 'Slow.Horses.S06E02.PROPER.1080', 'stalledUP', 1) + ('qbit', 'media_a', 'Slow.Horses.S06E03.1080p.WEB.H', 'queuedUP', 1) + ('qbit', 'media_a', 'Slow.Horses.S06E05.Sayonara.10', 'queuedUP', 1) + ('qbit', 'media_b', 'American.Horror.Story.S13E01.1', 'queuedUP', 1) + ('qbit', 'media_b', 'American.Horror.Story.S13E03.1', 'queuedUP', 1) + ('qbit', 'media_b', 'American.Horror.Story.S13E05.1', 'queuedUP', 1) + ('qbit', 'media_b', 'American.Horror.Story.S13E07.1', 'queuedUP', 1) + ('qbit', 'media_b', 'Matchbox.The.Movie.2026.720p.A', 'queuedUP', 1) + ('qbit', 'media_c', 'k3-own-bytes-qbit', 'stalledUP', 1) + ('trans', 'media_a', 'Avatar.Seven.Havens.S01E02.108', 'seeding', 1.0) + ('trans', 'media_a', 'Slow.Horses.S06E01.1080p.WEB.H', 'seeding', 1.0) + ('trans', 'media_a', 'Slow.Horses.S06E04.1080p.WEB.H', 'seeding', 1.0) + ('trans', 'media_a', 'Who.2026.720p.DSNP.WEB-DL.DDP5', 'seeding', 1.0) + ('trans', 'media_b', 'American.Horror.Story.S13E02.1', 'seeding', 1.0) + ('trans', 'media_b', 'American.Horror.Story.S13E04.1', 'seeding', 1.0) + ('trans', 'media_b', 'American.Horror.Story.S13E06.1', 'seeding', 1.0) + ('trans', 'media_b', 'American.Horror.Story.S13E08.1', 'seeding', 1.0) + ('trans', 'media_c', 'k3-own-bytes-trans', 'seeding', 1.0) diff --git a/documentation/audits/media-drive-events-2026-10-14/K3-2-pause.txt b/documentation/audits/media-drive-events-2026-10-14/K3-2-pause.txt new file mode 100644 index 00000000..2fa0397a --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K3-2-pause.txt @@ -0,0 +1,23 @@ +qbit pause 1 torrent(s) on media_c -> +trans pause 1 torrent(s) on media_c -> success + ('qbit', 'media_a', 'Avatar.Seven.Havens.S01E01.108', 'queuedUP', 1) + ('qbit', 'media_a', 'Avatar.Seven.Havens.S01E03.108', 'queuedUP', 1) + ('qbit', 'media_a', 'Hope.2026.720p.AMZN.WEB-DL.DDP', 'stalledUP', 1) + ('qbit', 'media_a', 'Slow.Horses.S06E02.PROPER.1080', 'stalledUP', 1) + ('qbit', 'media_a', 'Slow.Horses.S06E03.1080p.WEB.H', 'stalledUP', 1) + ('qbit', 'media_a', 'Slow.Horses.S06E05.Sayonara.10', 'queuedUP', 1) + ('qbit', 'media_b', 'American.Horror.Story.S13E01.1', 'queuedUP', 1) + ('qbit', 'media_b', 'American.Horror.Story.S13E03.1', 'queuedUP', 1) + ('qbit', 'media_b', 'American.Horror.Story.S13E05.1', 'queuedUP', 1) + ('qbit', 'media_b', 'American.Horror.Story.S13E07.1', 'queuedUP', 1) + ('qbit', 'media_b', 'Matchbox.The.Movie.2026.720p.A', 'queuedUP', 1) + ('qbit', 'media_c', 'k3-own-bytes-qbit', 'stoppedUP', 1) + ('trans', 'media_a', 'Avatar.Seven.Havens.S01E02.108', 'seeding', 1.0) + ('trans', 'media_a', 'Slow.Horses.S06E01.1080p.WEB.H', 'seeding', 1.0) + ('trans', 'media_a', 'Slow.Horses.S06E04.1080p.WEB.H', 'seeding', 1.0) + ('trans', 'media_a', 'Who.2026.720p.DSNP.WEB-DL.DDP5', 'seeding', 1.0) + ('trans', 'media_b', 'American.Horror.Story.S13E02.1', 'seeding', 1.0) + ('trans', 'media_b', 'American.Horror.Story.S13E04.1', 'seeding', 1.0) + ('trans', 'media_b', 'American.Horror.Story.S13E06.1', 'seeding', 1.0) + ('trans', 'media_b', 'American.Horror.Story.S13E08.1', 'seeding', 1.0) + ('trans', 'media_c', 'k3-own-bytes-trans', 'stopped', 1.0) diff --git a/documentation/audits/media-drive-events-2026-10-14/K3-3-eject.txt b/documentation/audits/media-drive-events-2026-10-14/K3-3-eject.txt new file mode 100644 index 00000000..fd8e21f8 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K3-3-eject.txt @@ -0,0 +1,22 @@ +POST /api/storage/eject -> 200 in 2.0s {"data": {"vmid": 9201, "ejected": "/mnt/media_c", "dependent_guests": null}, "ok": true} +TARGET SOURCE +guest ls C: +sonarr sees C: Felhom Probe Show (2026) +qbittorrent sees C: Felhom Probe Show (2026) +plex sees C: Felhom Probe Show (2026) +filebrowser sees C: ls: /mnt/felhom-drives/media_c/userdata/media/tv: No such file or directory +adatlemez beolvasas media_a media_b +2026/10/14 21:10:25 [INFO] [settings] Removed storage path: /mnt/felhom-drives/media_c +2026/10/14 21:10:26 [INFO] [web] FileBrowser mounts synced (recreated) — 3 storage path(s), config updated + +Oct 14 23:10:24 felhom sudo[674618]: felhom-agent : PWD=/ ; USER=root ; COMMAND=/usr/bin/chown 100000:100000 /mnt/media_c/felhom-data +Oct 14 23:10:24 felhom sudo[674621]: felhom-agent : PWD=/ ; USER=root ; COMMAND=/usr/bin/mkdir -p /mnt/felhom-drives/media_c +Oct 14 23:10:24 felhom felhom-agent[1201]: time=2026-10-14T23:10:24.680+02:00 level=INFO msg="reconcile: enrolled drive bound under shared parent (live, no reboot)" vmid=9201 where=/mnt/media_c guest_path=/mnt/felhom-dri +Oct 14 23:10:25 felhom felhom-agent[1201]: time=2026-10-14T23:10:25.922+02:00 level=INFO msg="local-api: drive intent recorded" where=/mnt/media_c action=ejected durable_id=uuid:86f33f9c-1022-43d8-95eb-f9a6ec9eb923 +Oct 14 23:10:25 felhom sudo[674712]: felhom-agent : PWD=/ ; USER=root ; COMMAND=/usr/bin/umount /mnt/felhom-drives/media_c +Oct 14 23:10:25 felhom systemd[1]: mnt-felhom\x2ddrives-media_c.mount: Deactivated successfully. +Oct 14 23:10:25 felhom felhom-agent[1201]: time=2026-10-14T23:10:25.943+02:00 level=INFO msg="guest-detach: drive fully unmounted from shared parent (live, fail-closed)" where=/mnt/media_c stable=/mnt/felhom-drives/media +Oct 14 23:10:25 felhom felhom-agent[1201]: time=2026-10-14T23:10:25.943+02:00 level=INFO msg="local-api: drive ejected (bind detached, raw mount kept)" vmid=9201 where=/mnt/media_c dependent_guests=0 +Oct 14 23:10:30 felhom sudo[675114]: felhom-agent : PWD=/ ; USER=root ; COMMAND=/usr/sbin/smartctl -a -j /dev/sde +Oct 14 23:10:31 felhom sudo[675177]: felhom-agent : PWD=/ ; USER=root ; COMMAND=/usr/sbin/smartctl -a -j /dev/sde +1 diff --git a/documentation/audits/media-drive-events-2026-10-14/K3-4-restart-after-eject.txt b/documentation/audits/media-drive-events-2026-10-14/K3-4-restart-after-eject.txt new file mode 100644 index 00000000..03e07d5b --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K3-4-restart-after-eject.txt @@ -0,0 +1,26 @@ +BEFORE: guest C path: drwxr-xr-x 2 nobody nogroup 4096 Oct 14 21:03 . drwxr-xr-x 6 nobody nogroup 4096 Oct 14 21:03 .. /dev/mapper/pve-root + +plex stop -> 200 +plex start -> 500 +jellyfin stop -> 200 +jellyfin start -> 500 +plex: sees C: +jellyfin: sees C: +AFTER: guest C path tree (did docker create folders on the guest's own disk?): +/mnt/felhom-drives/media_c +on device: /dev/mapper/pve-root + +2026/10/14 21:11:28 [INFO] Event pushed: app_start_failed (warning) [hu-only] — Telepített alkalmazás nem fut: Plex +2026/10/14 21:11:28 [INFO] Event pushed: app_start_failed (warning) [hu-only] — Telepített alkalmazás nem fut: Jellyfin +2026/10/14 21:11:28 [INFO] [stacks] ParseComposeHDDMounts: found 1 HDD mounts for /opt/docker/stacks/jellyfin/docker-compose.yml +2026/10/14 21:11:28 [INFO] [stacks] ParseComposeHDDMounts: found 1 HDD mounts for /opt/docker/stacks/plex/docker-compose.yml +jellyfin Created +plex Created +2026/10/14 21:11:19 [ERROR] [api] start failed for plex: starting stack plex: exit code 1 +2026/10/14 21:11:25 [INFO] [api] start requested for stack: jellyfin +2026/10/14 21:11:26 [INFO] [stacks] Starting stack: jellyfin +2026/10/14 21:11:26 [ERROR] [api] start failed for jellyfin: starting stack jellyfin: exit code 1 +2026/10/14 21:11:28 [INFO] Event pushed: app_start_failed (warning) [hu-only] — Telepített alkalmazás nem fut: Plex +2026/10/14 21:11:28 [INFO] Event pushed: app_start_failed (warning) [hu-only] — Telepített alkalmazás nem fut: Jellyfin +Error response from daemon: error while creating mount source path '/mnt/felhom-drives/media_c/userdata/media': mkdir /mnt/felhom-drives/media_c/userdata: permission denied +failed to start containers: plex diff --git a/documentation/audits/media-drive-events-2026-10-14/K3-5-detach-reattach.txt b/documentation/audits/media-drive-events-2026-10-14/K3-5-detach-reattach.txt new file mode 100644 index 00000000..e3a6449b --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K3-5-detach-reattach.txt @@ -0,0 +1,27 @@ +2026-10-14T23:12:42+02:00 +unused0: nvme-scratch:341/vm-341-disk-5.raw +Oct 14 23:12:43 felhom systemd[1]: mnt-media_c.mount: Deactivated successfully. +Oct 14 23:12:43 felhom systemd[1]: Unmounted mnt-media_c.mount - Felhom storage mount 86f33f9c-1022-43d8-95eb-f9a6ec9eb923. +Oct 14 23:12:43 felhom felhom-agent[1201]: time=2026-10-14T23:12:43.987+02:00 level=INFO msg="storage: enrolled drive absent by UUID — not re-asserting (will retry when it enumerates)" name=86f33f9c +Oct 14 23:12:48 felhom felhom-agent[1201]: time=2026-10-14T23:12:48.266+02:00 level=INFO msg="storage: watchdog — known drive went absent; clearing any eject intent (replug will auto-mount)" target= +Oct 14 23:13:03 felhom felhom-agent[1201]: time=2026-10-14T23:13:03.987+02:00 level=INFO msg="storage: enrolled drive absent by UUID — not re-asserting (will retry when it enumerates)" name=86f33f9c +Oct 14 23:13:04 felhom felhom-agent[1201]: time=2026-10-14T23:13:04.608+02:00 level=WARN msg="reconcile: enrolled drive not present (durable-id absent) — skipping" vmid=9201 durable_id=uuid:86f33f9c +2026-10-14T23:13:11+02:00 +scsi4: nvme-scratch:341/vm-341-disk-5.raw,backup=0,size=50G +sdd drive-scsi4 /mnt/media_c +/dev/sdd on /mnt/media_c type ext4 (rw,relatime) +/dev/sdd on /mnt/felhom-drives/media_c type ext4 (rw,relatime) +Oct 14 23:13:44 felhom sudo[682301]: felhom-agent : PWD=/ ; USER=root ; COMMAND=/usr/bin/mkdir -p /mnt/felhom-drives/media_c +Oct 14 23:13:44 felhom felhom-agent[1201]: time=2026-10-14T23:13:44.678+02:00 level=INFO msg="reconcile: enrolled drive bound under shared parent (live, no reboot)" vmid=9201 where=/mnt/media_c guest_ +Oct 14 23:14:04 felhom sudo[682848]: felhom-agent : PWD=/ ; USER=root ; COMMAND=/usr/bin/mkdir -p /mnt/media_c/felhom-data +Oct 14 23:14:04 felhom sudo[682851]: felhom-agent : PWD=/ ; USER=root ; COMMAND=/usr/bin/chown 100000:100000 /mnt/media_c/felhom-data +Oct 14 23:14:04 felhom sudo[682854]: felhom-agent : PWD=/ ; USER=root ; COMMAND=/usr/bin/mkdir -p /mnt/felhom-drives/media_c +Oct 14 23:14:04 felhom felhom-agent[1201]: time=2026-10-14T23:14:04.666+02:00 level=INFO msg="reconcile: enrolled drive bound under shared parent (live, no reboot)" vmid=9201 where=/mnt/media_c guest_ +dashboard drives: [('4ebfd36d', 'attached', '/mnt/felhom-drives/media_a'), ('863c67fd', 'attached', '/mnt/felhom-drives/adatlemez'), ('86f33f9c', 'attached', '/mnt/felhom-drives/media_c'), ('e15652d5', 'attached', '/mnt/felhom-drives/media_b')] +candidates: {"vmid": 9201, "initialize": [{"device": "/dev/sdd", "size_bytes": 53687091200, "model": "QEMU HARDDISK", "fstype": "ext4", "data_bearing": true, "mountable": true, "mount_source": "/dev/sdd", "durable_id": "uuid:86f33f9c-1022-43d8-95eb-f9a6ec9eb923"}], "attach": [{"device": "/dev/sdd", "size_bytes" +register-mounted /mnt/felhom-drives/media_c -> 200 {"data": {"registered": true, "where": "/mnt/felhom-drives/media_c"}, "ok": true} +2026/10/14 21:14:35 [INFO] [settings] Added storage path: /mnt/felhom-drives/media_c +2026/10/14 21:14:35 [INFO] [web] storage path registered (already-mounted store): /mnt/felhom-drives/media_c +2026/10/14 21:14:35 [INFO] [web] FileBrowser mounts synced (recreated) — 4 storage path(s), config updated +Felhom Probe Show (2026) + diff --git a/documentation/audits/media-drive-events-2026-10-14/K3-6-resume.txt b/documentation/audits/media-drive-events-2026-10-14/K3-6-resume.txt new file mode 100644 index 00000000..9e225117 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K3-6-resume.txt @@ -0,0 +1,45 @@ +qbit resume 1 torrent(s) on media_c -> +trans resume 1 torrent(s) on media_c -> success + ('qbit', 'media_a', 'Avatar.Seven.Havens.S01E01.108', 'queuedUP', 1) + ('qbit', 'media_a', 'Avatar.Seven.Havens.S01E03.108', 'queuedUP', 1) + ('qbit', 'media_a', 'Hope.2026.720p.AMZN.WEB-DL.DDP', 'stalledUP', 1) + ('qbit', 'media_a', 'Slow.Horses.S06E02.PROPER.1080', 'queuedUP', 1) + ('qbit', 'media_a', 'Slow.Horses.S06E03.1080p.WEB.H', 'queuedUP', 1) + ('qbit', 'media_a', 'Slow.Horses.S06E05.Sayonara.10', 'queuedUP', 1) + ('qbit', 'media_b', 'American.Horror.Story.S13E01.1', 'queuedUP', 1) + ('qbit', 'media_b', 'American.Horror.Story.S13E03.1', 'queuedUP', 1) + ('qbit', 'media_b', 'American.Horror.Story.S13E05.1', 'queuedUP', 1) + ('qbit', 'media_b', 'American.Horror.Story.S13E07.1', 'stalledUP', 1) + ('qbit', 'media_b', 'Matchbox.The.Movie.2026.720p.A', 'queuedUP', 1) + ('qbit', 'media_c', 'k3-own-bytes-qbit', 'stalledUP', 1) + ('trans', 'media_a', 'Avatar.Seven.Havens.S01E02.108', 'seeding', 1.0) + ('trans', 'media_a', 'Slow.Horses.S06E01.1080p.WEB.H', 'seeding', 1.0) + ('trans', 'media_a', 'Slow.Horses.S06E04.1080p.WEB.H', 'seeding', 1.0) + ('trans', 'media_a', 'Who.2026.720p.DSNP.WEB-DL.DDP5', 'seeding', 1.0) + ('trans', 'media_b', 'American.Horror.Story.S13E02.1', 'seeding', 1.0) + ('trans', 'media_b', 'American.Horror.Story.S13E04.1', 'seeding', 1.0) + ('trans', 'media_b', 'American.Horror.Story.S13E06.1', 'seeding', 1.0) + ('trans', 'media_b', 'American.Horror.Story.S13E08.1', 'seeding', 1.0) + ('trans', 'media_c', 'k3-own-bytes-trans', 'seeding', 1.0) +--- 21:17:53 + ('qbit', 'media_a', 'Avatar.Seven.Havens.S01E01.108', 'queuedUP', 1) + ('qbit', 'media_a', 'Avatar.Seven.Havens.S01E03.108', 'queuedUP', 1) + ('qbit', 'media_a', 'Hope.2026.720p.AMZN.WEB-DL.DDP', 'stalledUP', 1) + ('qbit', 'media_a', 'Slow.Horses.S06E02.PROPER.1080', 'queuedUP', 1) + ('qbit', 'media_a', 'Slow.Horses.S06E03.1080p.WEB.H', 'stalledUP', 1) + ('qbit', 'media_a', 'Slow.Horses.S06E05.Sayonara.10', 'queuedUP', 1) + ('qbit', 'media_b', 'American.Horror.Story.S13E01.1', 'queuedUP', 1) + ('qbit', 'media_b', 'American.Horror.Story.S13E03.1', 'queuedUP', 1) + ('qbit', 'media_b', 'American.Horror.Story.S13E05.1', 'queuedUP', 1) + ('qbit', 'media_b', 'American.Horror.Story.S13E07.1', 'queuedUP', 1) + ('qbit', 'media_b', 'Matchbox.The.Movie.2026.720p.A', 'queuedUP', 1) + ('qbit', 'media_c', 'k3-own-bytes-qbit', 'stalledUP', 1) + ('trans', 'media_a', 'Avatar.Seven.Havens.S01E02.108', 'seeding', 1.0) + ('trans', 'media_a', 'Slow.Horses.S06E01.1080p.WEB.H', 'seeding', 1.0) + ('trans', 'media_a', 'Slow.Horses.S06E04.1080p.WEB.H', 'seeding', 1.0) + ('trans', 'media_a', 'Who.2026.720p.DSNP.WEB-DL.DDP5', 'seeding', 1.0) + ('trans', 'media_b', 'American.Horror.Story.S13E02.1', 'seeding', 1.0) + ('trans', 'media_b', 'American.Horror.Story.S13E04.1', 'seeding', 1.0) + ('trans', 'media_b', 'American.Horror.Story.S13E06.1', 'seeding', 1.0) + ('trans', 'media_b', 'American.Horror.Story.S13E08.1', 'seeding', 1.0) + ('trans', 'media_c', 'k3-own-bytes-trans', 'seeding', 1.0) diff --git a/documentation/audits/media-drive-events-2026-10-14/K3-PREDICTIONS.md b/documentation/audits/media-drive-events-2026-10-14/K3-PREDICTIONS.md new file mode 100644 index 00000000..95e9428b --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K3-PREDICTIONS.md @@ -0,0 +1,8 @@ +# K3 predictions — written before the safe removal of Media C (2026-10-14 ~23:15 CEST) +- Pausing by save folder stops ONLY C's two torrents; A's and B's keep their state (seeding/queued). +- „Biztonságos leválasztás" (POST /api/storage/eject): the agent unmounts; the controller DEREGISTERS the drive + (RemoveStoragePath) and re-syncs FileBrowser. With six running containers holding a bind of C, the unmount may be + refused as busy — or succeed lazily, leaving the containers a view of the old filesystem. +- After the VM disk is detached and re-attached, the agent mounts it again (replug), but the controller no longer has + it registered: it needs „Meglévő meghajtó csatolása" (attach) — the household's step today. +- After restart + resume, C's torrents seed again without a re-check; A's never stopped. diff --git a/documentation/audits/media-drive-events-2026-10-14/K5-1-restart-after-added.txt b/documentation/audits/media-drive-events-2026-10-14/K5-1-restart-after-added.txt new file mode 100644 index 00000000..a6e8a691 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K5-1-restart-after-added.txt @@ -0,0 +1,6 @@ +qbittorrent dashboard restart (compose up -d, compose CHANGED) -> 200 in 2.0s; started before=21:00:32 after=2026-10-14T21:04:23.405827829Z | audiobooks +transmission dashboard restart (compose up -d, compose CHANGED) -> 200 in 5.9s; started before=21:00:32 after=2026-10-14T21:04:38.76445242Z | audiobooks +sonarr dashboard restart (compose up -d, compose CHANGED) -> 200 in 3.9s; started before=21:00:33 after=2026-10-14T21:04:52.134164368Z | audiobooks +radarr dashboard restart (compose up -d, compose CHANGED) -> 200 in 4.0s; started before=21:00:34 after=2026-10-14T21:05:06.56466461Z | audiobooks +plex dashboard restart (compose up -d, compose CHANGED) -> 200 in 9.3s; started before=21:00:34 after=2026-10-14T21:05:25.260983982Z | audiobooks +jellyfin dashboard restart (compose up -d, compose CHANGED) -> 200 in 0.9s; started before=21:00:35 after=2026-10-14T21:05:36.397174757Z | audiobooks diff --git a/documentation/audits/media-drive-events-2026-10-14/K5-2-restart-after-return.txt b/documentation/audits/media-drive-events-2026-10-14/K5-2-restart-after-return.txt new file mode 100644 index 00000000..a7628189 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/K5-2-restart-after-return.txt @@ -0,0 +1,18 @@ +BEFORE: {'sonarr': "ls: cannot access '/mnt/felhom-drives/media_c/userdata/media/tv': I/O error", 'radarr': "ls: cannot access '/mnt/felhom-drives/media_c/userdata/media/tv': I/O error", 'qbittorrent': 'ls: /mnt/felhom-drives/media_c/userdata/media/tv: I/O error', 'transmission': "ls: cannot access '/mnt/felhom-drives/media_c/userdata/media/tv': I/O error"} +sonarr compose up -d --force-recreate: 6.9s; sees C: Felhom Probe Show (2026) | Container sonarr Starting + Container sonarr Started +qbittorrent compose up -d --force-recreate: 5.9s; sees C: Felhom Probe Show (2026) | Container qbittorrent Starting + Container qbittorrent Started +radarr dashboard stop+start (200,200): 4.1s; sees C: Felhom Probe Show (2026) +transmission dashboard stop+start (200,200): 5.3s; sees C: Felhom Probe Show (2026) +plex dashboard start (200): 0.6s; sees C: Felhom Probe Show (2026) +jellyfin dashboard start (200): 0.7s; sees C: Felhom Probe Show (2026) +filebrowser sees C: Felhom Probe Show (2026) +sonarr dashboard restart -> 200 +qbittorrent dashboard restart -> 200 +sonarr rule: Host(`sonarr.enkicsifelhom.hu`) sees C: Felhom Probe Show (2026) +qbittorrent rule: Host(`torrent.enkicsifelhom.hu`) sees C: Felhom Probe Show (2026) +qbit password env set: yes + +sonarr public: 302 +torrent public: 200 diff --git a/documentation/audits/media-drive-events-2026-10-14/NIGHT-LOG.md b/documentation/audits/media-drive-events-2026-10-14/NIGHT-LOG.md new file mode 100644 index 00000000..781452a2 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/NIGHT-LOG.md @@ -0,0 +1,9 @@ +# Night log — media drive events (Part 2), times CEST +22:52 K0 safety baseline: every file on every drive's userdata/media (K0-filesig-start.tsv) +22:54:48 K1 DETACH Media B (VM 341 scsi3, nvme-scratch:341/vm-341-disk-4.raw) — expect a hub mail +22:59:00 K1 RE-ATTACH Media B (same volume, scsi3) +23:02 K1 done: nothing lost in any app; files identical +23:03:10 K2 Media C added (VM 341 scsi4, 50 GB, backup=0) +23:10:23 K3 safe removal of Media C (POST /api/storage/eject) — expect a hub mail? +23:12:41 K3 DETACH Media C disk (scsi4) after the safe removal +23:13:11 K3 RE-ATTACH Media C disk (scsi4) diff --git a/documentation/audits/media-drive-events-2026-10-14/tools/filesig.py b/documentation/audits/media-drive-events-2026-10-14/tools/filesig.py new file mode 100644 index 00000000..6df02620 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/tools/filesig.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +"""filesig.py — the safety baseline: every regular file under each drive's userdata/media with size, inode, +link count and mtime, one line each, sorted. Run INSIDE the guest. Two runs compared = 'did any FILE change or vanish' +(the Part 2 STOP condition), as opposed to a library entry.""" +import glob, os +for root in sorted(glob.glob("/mnt/felhom-drives/*/userdata/media")): + for dp, dn, fn in os.walk(root): + for f in fn: + p = os.path.join(dp, f) + try: + s = os.lstat(p) + except OSError as e: + print(f"ERR {p} {e}"); continue + print(f"{p}\t{s.st_size}\t{s.st_ino}\t{s.st_nlink}\t{int(s.st_mtime)}") diff --git a/documentation/audits/media-drive-events-2026-10-14/tools/grun.sh b/documentation/audits/media-drive-events-2026-10-14/tools/grun.sh new file mode 100755 index 00000000..eaaca993 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/tools/grun.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# grun.sh [args...] — run a tool of this folder INSIDE the Tester 1 guest via stdin, with Jellyfin's admin +# password substituted for __JF_PW__ in the piped copy only (it lives in ~/.felhom-tester1/secrets.json). The guest copy is deleted. +cd "$(dirname "$0")" +python3 - "$@" <<'PY' | sed -E 's/X-Plex-Token=[A-Za-z0-9_-]+/X-Plex-Token=/g' +import json,os,sys,shlex +sys.path.insert(0,"/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts") +os.environ.setdefault("SC",os.path.expanduser("~/.felhom-tester1")); os.environ.setdefault("TARGET","tester-1") +import box_walk as w +pw=json.load(open(os.path.expanduser("~/.felhom-tester1/secrets.json")))["jellyfin"]["admin"] +src=open(sys.argv[1]).read().replace("__JF_PW__",pw) +args=" ".join(shlex.quote(a) for a in sys.argv[2:]) +r=w.sh(w.ssh_args()+[f'pct exec 9201 -- sh -c "cat > /root/.g.py; timeout 900 python3 /root/.g.py {args} 2>&1; rm -f /root/.g.py"'],timeout=960,inp=src) +print(r.stdout, end=""); print(r.stderr[-600:]) if r.returncode else None +PY diff --git a/documentation/audits/media-drive-events-2026-10-14/tools/k1.py b/documentation/audits/media-drive-events-2026-10-14/tools/k1.py new file mode 100644 index 00000000..2a3bdd9f --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/tools/k1.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 +"""k1.py — K1 (what the media apps do while a drive is gone), run INSIDE the Tester 1 guest. + +steps: + mark mark one Media B episode watched in Plex and Jellyfin (the watch-state probe) + state what every app shows now (per item: present / unavailable / gone; watch state of the probe) + plexscan force Plex to scan both libraries (and wait) + plexset <0|1> set Plex's „empty trash automatically after every scan" (autoEmptyTrash) + jfscan force Jellyfin to scan (and wait) + arrscan run Sonarr's RefreshSeries + RescanSeries and Radarr's RefreshMovie + RescanMovie (and wait) + arrset <0|1> set Sonarr/Radarr „unmonitor deleted episodes/movies" +Plex's token: its own Preferences.xml. Jellyfin's admin password: substituted into this copy by the runner. Nothing printed. +""" +import json, re, subprocess, sys, time, urllib.error, urllib.parse, urllib.request + +JFPW = "__JF_PW__" +PROBE_SHOW, PROBE_EP = "American Horror Story", (13, 1) # on Media B + + +def sh(c): + return subprocess.run(c, shell=True, capture_output=True, text=True).stdout.strip() + + +def ip(c): + return sh(f"docker inspect {c} -f '{{{{range .NetworkSettings.Networks}}}}{{{{.IPAddress}}}}{{{{end}}}}'") + + +def call(m, url, h=None, body=None, raw=False, t=60): + hh = dict(h or {}) + data = None + if body is not None: + data = json.dumps(body).encode(); hh["Content-Type"] = "application/json" + try: + with urllib.request.urlopen(urllib.request.Request(url, data=data, headers=hh, method=m), timeout=t) as r: + b = r.read(); return r.status, (b.decode() if raw else (json.loads(b) if b else None)) + except urllib.error.HTTPError as e: + return e.code, None + + +P = "/var/lib/docker/volumes/plex_plex_config/_data/Library/Application Support/Plex Media Server" +PT = re.search(r'PlexOnlineToken="([^"]+)', open(f"{P}/Preferences.xml").read()).group(1) +PB = f"http://{ip('plex')}:32400" +PH = {"Accept": "application/json", "X-Plex-Token": PT} + + +def plex(m, path, **q): + return call(m, PB + path + ("?" + urllib.parse.urlencode(q) if q else ""), PH) + + +def arr(app): + port = 8989 if app == "sonarr" else 7878 + k = re.search(r"([^<]+)", open(f"/var/lib/docker/volumes/{app}_{app}_config/_data/config.xml").read()).group(1) + return f"http://{ip(app)}:{port}/api/v3", {"X-Api-Key": k, "Host": app} + + +def jf(): + b = f"http://{ip('jellyfin')}:8096" + A = 'MediaBrowser Client="felhom-test", Device="k1", DeviceId="felhom-k1", Version="1"' + c, r = call("POST", b + "/Users/AuthenticateByName", {"Authorization": A}, {"Username": "admin", "Pw": JFPW}) + return b, {"X-Emby-Token": r["AccessToken"]}, r["User"]["Id"] + + +def plex_items(): + out = {} + for sec in (1, 2): + c, d = plex("GET", f"/library/sections/{sec}/all") + for m in (d or {}).get("MediaContainer", {}).get("Metadata", []): + out[m["title"]] = m["ratingKey"] + return out + + +def plex_probe(): + rk = plex_items().get(PROBE_SHOW) + if not rk: + return None, "show gone from Plex" + c, d = plex("GET", f"/library/metadata/{rk}/allLeaves") + for e in (d or {}).get("MediaContainer", {}).get("Metadata", []): + if (e.get("parentIndex"), e.get("index")) == PROBE_EP: + return e["ratingKey"], f"viewCount={e.get('viewCount', 0)}" + return None, "episode gone" + + +def jf_probe(b, H, uid): + # the plain recursive episode list (the per-show and search routes answered empty on this 10.11) + c, d = call("GET", b + f"/Users/{uid}/Items?Recursive=true&IncludeItemTypes=Episode", H) + for e in (d or {}).get("Items", []): + if "Horror" in (e.get("SeriesName") or "") and (e.get("ParentIndexNumber"), e.get("IndexNumber")) == PROBE_EP: + return e["Id"], f"Played={e.get('UserData', {}).get('Played')}" + return None, "episode gone" + + +def state(): + print(f"--- {time.strftime('%H:%M:%S')} state") + for sec in (1, 2): + c, d = plex("GET", f"/library/sections/{sec}/all", includeMissing=1) + for m in (d or {}).get("MediaContainer", {}).get("Metadata", []): + c2, d2 = plex("GET", f"/library/metadata/{m['ratingKey']}/allLeaves" if m["type"] == "show" else f"/library/metadata/{m['ratingKey']}") + leaves = (d2 or {}).get("MediaContainer", {}).get("Metadata", []) + parts = [p for l in leaves for md in l.get("Media", []) for p in md.get("Part", [])] + gone = sum(1 for p in parts if p.get("accessible") is False or p.get("exists") is False) + print(f" plex {m['type']:5} {m['title'][:24]:24} items={len(leaves)} parts={len(parts)} unavailable={gone}") + print(" plex probe:", plex_probe()[1]) + b, H, uid = jf() + c, d = call("GET", b + f"/Users/{uid}/Items?Recursive=true&IncludeItemTypes=Series,Movie,Episode", H) + items = (d or {}).get("Items", []) + print(" jellyfin:", {t: len([i for i in items if i["Type"] == t]) for t in ("Series", "Movie", "Episode")}, + sorted(i["Name"] for i in items if i["Type"] in ("Series", "Movie"))) + print(" jellyfin probe:", jf_probe(b, H, uid)[1]) + for app, kind in (("sonarr", "series"), ("radarr", "movie")): + base, H2 = arr(app) + c, s = call("GET", base + f"/{kind}", H2) + for x in s or []: + st = x.get("statistics") or {} + print(f" {app} {x['title'][:24]:24} monitored={x['monitored']} files={st.get('episodeFileCount', x.get('hasFile'))} path={x['path'].split('/')[3]}") + if app == "sonarr": + for x in s or []: + c, eps = call("GET", base + f"/episode?seriesId={x['id']}", H2) + eps = eps or [] + print(f" {x['title'][:24]:24} episodes monitored={sum(1 for e in eps if e['monitored'])}/{len(eps)} hasFile={sum(1 for e in eps if e['hasFile'])}") + + +def wait_cmd(base, H, cid): + for _ in range(60): + time.sleep(3) + c, d = call("GET", base + f"/command/{cid}", H) + if (d or {}).get("status") in ("completed", "failed", "aborted"): + return d["status"] + return "timeout" + + +step = sys.argv[1] +if step == "mark": + rk, s = plex_probe(); print("plex probe before:", s) + c, _ = plex("GET", "/:/scrobble", key=rk, identifier="com.plexapp.plugins.library"); print("plex scrobble ->", c) + b, H, uid = jf(); iid, s = jf_probe(b, H, uid); print("jellyfin probe before:", s) + c, _ = call("POST", b + f"/Users/{uid}/PlayedItems/{iid}", H); print("jellyfin played ->", c) + print("after: plex", plex_probe()[1], "| jellyfin", jf_probe(b, H, uid)[1]) +elif step == "state": + state() +elif step == "plexscan": + for sec in (1, 2): + print(f"plex refresh section {sec} ->", plex("GET", f"/library/sections/{sec}/refresh", force=1)[0]) + for _ in range(60): + time.sleep(5) + c, d = plex("GET", "/library/sections") + if not any(x.get("refreshing") for x in (d or {}).get("MediaContainer", {}).get("Directory", [])): + break + print("plex scan done", time.strftime("%H:%M:%S")) +elif step == "plexset": + print("plex autoEmptyTrash ->", plex("PUT", "/:/prefs", autoEmptyTrash=sys.argv[2])[0]) +elif step == "jfscan": + b, H, uid = jf() + print("jellyfin /Library/Refresh ->", call("POST", b + "/Library/Refresh", H)[0]) + for _ in range(60): + time.sleep(5) + c, tasks = call("GET", b + "/ScheduledTasks", H) + t = [x for x in tasks or [] if x.get("Key") == "RefreshLibrary"] + if t and t[0].get("State") == "Idle": + break + print("jellyfin scan done", time.strftime("%H:%M:%S")) +elif step == "arrscan": + for app, cmds in (("sonarr", ("RefreshSeries", "RescanSeries")), ("radarr", ("RefreshMovie", "RescanMovie"))): + base, H = arr(app) + for cmd in cmds: + c, d = call("POST", base + "/command", H, {"name": cmd}) + print(f"{app} {cmd} -> {c} {wait_cmd(base, H, (d or {}).get('id'))}") +elif step == "arrset": + v = sys.argv[2] == "1" + for app, key in (("sonarr", "autoUnmonitorPreviouslyDownloadedEpisodes"), ("radarr", "autoUnmonitorPreviouslyDownloadedMovies")): + base, H = arr(app) + c, mm = call("GET", base + "/config/mediamanagement", H) + mm[key] = v + print(f"{app} {key}={v} ->", call("PUT", base + f"/config/mediamanagement/{mm['id']}", H, mm)[0]) diff --git a/documentation/audits/media-drive-events-2026-10-14/tools/k3.py b/documentation/audits/media-drive-events-2026-10-14/tools/k3.py new file mode 100644 index 00000000..1c41eca4 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/tools/k3.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +"""k3.py create|state|pause|resume [drive] — K3 (a safe removal), run INSIDE the Tester 1 guest. + +create makes ONE torrent of our own random bytes (3 MB, private, no tracker) per client on Media C and adds it so it + seeds: qBittorrent (category tv-drive-c → media_c/userdata/media/torrents/tv), Transmission + (media_c/userdata/media/transmission/tv). +state every torrent: client, drive, state, progress — the „did anything else stop / re-check" record. +pause pause ONLY the torrents whose save folder is on (qBittorrent by save_path, Transmission by downloadDir). +resume start them again. +Passwords: the deploy secrets read from the containers' environment; never printed. +""" +import base64, hashlib, json, os, re, subprocess, sys, time, urllib.error, urllib.parse, urllib.request + +DRV = sys.argv[2] if len(sys.argv) > 2 else "media_c" +M = f"/mnt/felhom-drives/{DRV}/userdata/media" + + +def sh(c): + return subprocess.run(c, shell=True, capture_output=True, text=True).stdout.strip() + + +def ip(c): + return sh(f"docker inspect {c} -f '{{{{range .NetworkSettings.Networks}}}}{{{{.IPAddress}}}}{{{{end}}}}'") + + +def be(x): + if isinstance(x, int): return b"i%de" % x + if isinstance(x, bytes): return b"%d:%s" % (len(x), x) + if isinstance(x, str): return be(x.encode()) + if isinstance(x, list): return b"l" + b"".join(be(i) for i in x) + b"e" + return b"d" + b"".join(be(k) + be(x[k]) for k in sorted(x)) + b"e" + + +def mktorrent(d): + L = 262144 + files = sorted(os.listdir(d)); data = b"".join(open(os.path.join(d, f), "rb").read() for f in files) + info = {"name": os.path.basename(d), "piece length": L, "private": 1, + "pieces": b"".join(hashlib.sha1(data[i:i+L]).digest() for i in range(0, len(data), L)), + "files": [{"length": os.path.getsize(os.path.join(d, f)), "path": [f]} for f in files]} + return be({"info": info, "created by": "felhom K3 (own random bytes)"}) + + +QB = f"http://{ip('qbittorrent')}:8080" +qpw = sh("docker exec qbittorrent printenv QBT_ADMIN_PASSWORD") +req = urllib.request.Request(QB + "/api/v2/auth/login", data=urllib.parse.urlencode({"username": "admin", "password": qpw}).encode(), headers={"Referer": QB}) +with urllib.request.urlopen(req) as r: + m = re.search(r"((?:QBT_)?SID[A-Za-z0-9_]*)=([^;]+)", r.headers.get("Set-Cookie", "")) +QH = {"Referer": QB, "Cookie": f"{m.group(1)}={m.group(2)}"} + + +def qb(path, form=None, files=None): + if files: + bnd = "felhomk3" + body = b"" + for k, v in (form or {}).items(): + body += f"--{bnd}\r\nContent-Disposition: form-data; name=\"{k}\"\r\n\r\n{v}\r\n".encode() + for k, (fn, data) in files.items(): + body += f"--{bnd}\r\nContent-Disposition: form-data; name=\"{k}\"; filename=\"{fn}\"\r\nContent-Type: application/x-bittorrent\r\n\r\n".encode() + data + b"\r\n" + body += f"--{bnd}--\r\n".encode() + r = urllib.request.Request(QB + path, data=body, headers=dict(QH, **{"Content-Type": f"multipart/form-data; boundary={bnd}"})) + elif form is not None: + r = urllib.request.Request(QB + path, data=urllib.parse.urlencode(form).encode(), headers=QH) + else: + r = urllib.request.Request(QB + path, headers=QH) + with urllib.request.urlopen(r, timeout=60) as x: + t = x.read().decode() + try: return json.loads(t) + except ValueError: return t + + +TR = f"http://{ip('transmission')}:9091/transmission/rpc" +tpw = sh("docker exec transmission printenv PASS") +TA = {"Authorization": "Basic " + base64.b64encode(f"admin:{tpw}".encode()).decode()} + + +def tr(method, args=None): + h = dict(TA) + for _ in range(2): + r = urllib.request.Request(TR, data=json.dumps({"method": method, "arguments": args or {}}).encode(), headers=h) + try: + with urllib.request.urlopen(r, timeout=60) as x: return json.load(x) + except urllib.error.HTTPError as e: + if e.code == 409: h["X-Transmission-Session-Id"] = e.headers.get("X-Transmission-Session-Id"); continue + raise + + +def state(): + out = [] + for t in qb("/api/v2/torrents/info"): + out.append(("qbit", t["save_path"].split("/")[3], t["name"][:30], t["state"], round(t["progress"], 4))) + for t in tr("torrent-get", {"fields": ["name", "downloadDir", "status", "percentDone", "error", "errorString"]})["arguments"]["torrents"]: + st = {0: "stopped", 1: "check-wait", 2: "checking", 3: "dl-wait", 4: "downloading", 5: "seed-wait", 6: "seeding"}.get(t["status"], t["status"]) + out.append(("trans", t["downloadDir"].split("/")[3], t["name"][:30], st + (" ERR " + t["errorString"][:30] if t["error"] else ""), round(t["percentDone"], 4))) + return sorted(out) + + +step = sys.argv[1] +if step == "create": + for client, sub in (("qbit", "torrents/tv"), ("trans", "transmission/tv")): + d = f"{M}/{sub}/k3-own-bytes-{client}" + os.makedirs(d, exist_ok=True) + with open(f"{d}/data.bin", "wb") as f: f.write(os.urandom(3_000_000)) + sh(f"chown -R 1000:1000 '{d}'") + tor = mktorrent(d) + if client == "qbit": + print("qbit add ->", qb("/api/v2/torrents/add", {"category": "tv-drive-c"}, {"torrents": ("k3.torrent", tor)})) + else: + print("trans add ->", json.dumps(tr("torrent-add", {"metainfo": base64.b64encode(tor).decode(), "download-dir": f"{M}/transmission/tv"}))[:160]) + time.sleep(20) + for s in state(): print(" ", s) +elif step == "state": + print(f"--- {time.strftime('%H:%M:%S')}") + for s in state(): print(" ", s) +elif step in ("pause", "resume"): + hs = [t["hash"] for t in qb("/api/v2/torrents/info") if t["save_path"].startswith(M)] + print(f"qbit {step} {len(hs)} torrent(s) on {DRV} ->", qb(f"/api/v2/torrents/{'stop' if step == 'pause' else 'start'}", {"hashes": "|".join(hs)}) if hs else "none") + ids = [t["id"] for t in tr("torrent-get", {"fields": ["id", "downloadDir"]})["arguments"]["torrents"] if t["downloadDir"].startswith(M)] + print(f"trans {step} {len(ids)} torrent(s) on {DRV} ->", tr("torrent-stop" if step == "pause" else "torrent-start", {"ids": ids})["result"] if ids else "none") + time.sleep(5) + for s in state(): print(" ", s) diff --git a/documentation/audits/media-drive-events-2026-10-14/tools/run_in_guest.py b/documentation/audits/media-drive-events-2026-10-14/tools/run_in_guest.py new file mode 100644 index 00000000..81a9d851 --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/tools/run_in_guest.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 +"""run_in_guest.py [args] — run one of this folder's stdlib scripts INSIDE the Tester 1 guest +(SC=~/.felhom-tester1 TARGET=tester-1) and remove it afterwards. Output only; nothing is left on the box.""" +import os, sys +sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts") +import box_walk as w +src = open(os.path.join(os.path.dirname(os.path.abspath(__file__)), sys.argv[1])).read() +args = " ".join(sys.argv[2:]) +print(w.guest(f"cat > /root/.media-tool.py <<'PYEOF'\n{src}\nPYEOF\npython3 /root/.media-tool.py {args} 2>&1; rm -f /root/.media-tool.py", timeout=1800)) diff --git a/documentation/audits/media-drive-events-2026-10-14/tools/wire.py b/documentation/audits/media-drive-events-2026-10-14/tools/wire.py new file mode 100644 index 00000000..7bfdd64d --- /dev/null +++ b/documentation/audits/media-drive-events-2026-10-14/tools/wire.py @@ -0,0 +1,374 @@ +#!/usr/bin/env python3 +"""wire.py [-v] — (extended 2026-10-14 night, K2: drives from WIRE_DRIVES; download folders; Plex; Jellyfin) — Part C: connect Sonarr and Radarr to qBittorrent and Transmission, ONE ENTRY PER DRIVE, on the box. + +Runs INSIDE the guest (it talks to the containers on the docker network and reads their keys from their own config +volumes / environment). Stdlib only. Every key and password is read at runtime and NEVER written to a file or a log: +every printed line passes through redact(). `-v` prints every call and the SHAPE of every answer (status, top-level +keys / list length) — this is the reference the later link code copies. + +For each drive d in DRIVES, M = /mnt/felhom-drives//userdata/media (the same path in every container): + qBittorrent : categories tv-, movies- -> save path M/torrents/{tv,movies}, incomplete path M/torrents/.temp + Sonarr : root folder M/tv; tag ; „qBittorrent " (category tv-, tag ) and + „Transmission " (directory M/transmission/tv, tag ) + Radarr : the same with movies + Sonarr/Radarr: minimum free space when importing = MIN_FREE_MB; „use hardlinks instead of copy" quoted + Prowlarr : (when installed) one Application entry each for Sonarr and Radarr, sync level „Full Sync", reached by + container name; then an ApplicationIndexerSync command, and the indexers each app holds read back +Box-wide, recorded with the reason in the output: + qBittorrent : auto_tmm_enabled = true (a category's save path applies ONLY in automatic torrent management; + qBittorrent's default is manual, which saves every torrent to the default + path = drive A, whatever the category says) + Transmission: incomplete-dir-enabled = false (Transmission has ONE incomplete folder for all torrents; on drive A it + would make every drive-B download a cross-drive copy at completion) + +Idempotent: an existing tag / root folder / client / category of the same name is left as it is and reported. +""" +import base64, json, os, re, subprocess, sys, time, urllib.error, urllib.parse, urllib.request + +V = "-v" in sys.argv +_d = next((a[7:] for a in sys.argv if a.startswith("drives=")), os.environ.get("WIRE_DRIVES", "drive-a:media_a,drive-b:media_b")) +DRIVES = [tuple(x.split(":")) for x in _d.split(",")] # e.g. drives=drive-a:media_a,drive-b:media_b,drive-c:media_c +JFPW = "__JF_PW__" # Jellyfin's admin password: substituted into the box's copy by the runner (grun.sh), never stored here +MIN_FREE_MB = 10240 +SECRETS = [] + + +def redact(s): + s = str(s) + for x in SECRETS: + if x: + s = s.replace(x, "") + return s + + +def say(*a): + print(redact(" ".join(str(x) for x in a)), flush=True) + + +def sh(cmd): + return subprocess.run(cmd, shell=True, capture_output=True, text=True).stdout.strip() + + +def shape(body): + if isinstance(body, dict): + return "{" + ", ".join(sorted(body.keys()))[:300] + "}" + if isinstance(body, list): + return f"[list of {len(body)}]" + return repr(body)[:120] + + +def call(method, url, headers=None, data=None, form=False, raw=False, expect=(200, 201, 202, 204)): + h = dict(headers or {}) + if data is not None and not isinstance(data, (bytes, str)): + data = urllib.parse.urlencode(data) if form else json.dumps(data) + h.setdefault("Content-Type", "application/x-www-form-urlencoded" if form else "application/json") + if isinstance(data, str): + data = data.encode() + req = urllib.request.Request(url, data=data, headers=h, method=method) + try: + with urllib.request.urlopen(req, timeout=60) as r: + code, txt, hdrs = r.status, r.read().decode("utf-8", "replace"), {k.lower(): v for k, v in r.headers.items()} + except urllib.error.HTTPError as e: + code, txt, hdrs = e.code, e.read().decode("utf-8", "replace"), {k.lower(): v for k, v in e.headers.items()} + try: + body = json.loads(txt) if txt and not raw else txt + except ValueError: + body = txt + if V: + say(f" {method} {url} -> {code} {shape(body)}") + if code not in expect: + say(f" !! {method} {url} -> {code}: {redact(txt)[:600]}") + return code, body, hdrs + + +def ip(c): + return sh(f"docker inspect {c} -f '{{{{range .NetworkSettings.Networks}}}}{{{{.IPAddress}}}} {{{{end}}}}'").split()[0] + + +def apikey(app): + x = open(f"/var/lib/docker/volumes/{app}_{app}_config/_data/config.xml").read() # sonarr, radarr, prowlarr + k = re.search(r"([^<]+)", x).group(1) + SECRETS.append(k) + return k + + +def env(c, var): + v = sh(f"docker exec {c} printenv {var}") + SECRETS.append(v) + return v + + +# ---------------------------------------------------------------- the download folders (the skeleton may not have them) +for tag, d in DRIVES: + M = f"/mnt/felhom-drives/{d}/userdata/media" + made = [] + for sub in ("torrents", "torrents/tv", "torrents/movies", "transmission", "transmission/tv", "transmission/movies"): + if not os.path.isdir(f"{M}/{sub}"): + os.makedirs(f"{M}/{sub}"); os.chown(f"{M}/{sub}", 1000, 1000); os.chmod(f"{M}/{sub}", 0o2775); made.append(sub) + say(f"== folders on {d}: {'made ' + str(made) if made else 'all present'}") + +# ---------------------------------------------------------------- qBittorrent +QB = f"http://{ip('qbittorrent')}:8080" +qpw = env("qbittorrent", "QBT_ADMIN_PASSWORD") +say("== qBittorrent", QB, "(password: the deploy secret, read from the container's environment)") +c, _, hd = call("POST", QB + "/api/v2/auth/login", {"Referer": QB}, {"username": "admin", "password": qpw}, form=True, raw=True) +# qBittorrent 5.x names the session cookie QBT_SID_ (older versions: SID) — take whichever it set +m = re.search(r"((?:QBT_)?SID[A-Za-z0-9_]*)=([^;]+)", hd.get("set-cookie", "")) +if not m: + sys.exit(say(f" !! no session cookie; answer headers: {sorted(hd)}") or 1) +say(f" session cookie name: {m.group(1)}") +SECRETS.append(m.group(2)) +QH = {"Referer": QB, "Cookie": f"{m.group(1)}={m.group(2)}"} +c, prefs, _ = call("GET", QB + "/api/v2/app/preferences", QH) +say(f" before: auto_tmm_enabled={prefs.get('auto_tmm_enabled')} save_path={prefs.get('save_path')} " + f"temp_path_enabled={prefs.get('temp_path_enabled')} temp_path={prefs.get('temp_path')}") +call("POST", QB + "/api/v2/app/setPreferences", QH, {"json": json.dumps({"auto_tmm_enabled": True})}, form=True, raw=True) +c, prefs, _ = call("GET", QB + "/api/v2/app/preferences", QH) +say(f" after : auto_tmm_enabled={prefs.get('auto_tmm_enabled')} (a category's save path applies only in automatic mode)") +c, cats, _ = call("GET", QB + "/api/v2/torrents/categories", QH) +for tag, d in DRIVES: + M = f"/mnt/felhom-drives/{d}/userdata/media" + for kind in ("tv", "movies"): + name = f"{kind}-{tag}" + form = {"category": name, "savePath": f"{M}/torrents/{kind}", "downloadPathEnabled": "true", + "downloadPath": f"{M}/torrents/.temp"} + ep = "editCategory" if name in (cats or {}) else "createCategory" + call("POST", QB + f"/api/v2/torrents/{ep}", QH, form, form=True, raw=True) +c, cats, _ = call("GET", QB + "/api/v2/torrents/categories", QH) +for n, v in sorted((cats or {}).items()): + say(f" category {n}: savePath={v.get('savePath')} downloadPath={v.get('download_path', v.get('downloadPath'))}") + +# ---------------------------------------------------------------- Transmission +TR = f"http://{ip('transmission')}:9091/transmission/rpc" +tpw = env("transmission", "PASS") +TA = "Basic " + base64.b64encode(f"admin:{tpw}".encode()).decode() +SECRETS.append(TA.split()[1]) +say("== Transmission", TR, "(password: the deploy secret, read from the container's environment)") + + +def trpc(method, args=None, host=None): + h = {"Authorization": TA} + if host: + h["Host"] = host + c, b, hd = call("POST", TR, h, {"method": method, "arguments": args or {}}, expect=(200, 409)) + if c == 409: + h["X-Transmission-Session-Id"] = hd.get("x-transmission-session-id", "") + c, b, hd = call("POST", TR, h, {"method": method, "arguments": args or {}}, expect=(200, 421)) + return c, b + + +c, b = trpc("session-get", {"fields": ["download-dir", "incomplete-dir", "incomplete-dir-enabled", "rename-partial-files"]}) +say(f" before: {json.dumps((b or {}).get('arguments'))}") +trpc("session-set", {"incomplete-dir-enabled": False}) +c, b = trpc("session-get", {"fields": ["download-dir", "incomplete-dir-enabled", "rename-partial-files"]}) +say(f" after : {json.dumps((b or {}).get('arguments'))} (one incomplete folder for all torrents would be on drive A)") +# The probe the link code needs: can another container reach Transmission BY NAME? (Host-header allow-list) +c, b = trpc("session-get", {"fields": ["version"]}, host="transmission:9091") +say(f" probe Host: transmission:9091 -> HTTP {c} {redact(str(b))[:160]}") +TR_BY_NAME = c == 200 + + +# ---------------------------------------------------------------- Sonarr / Radarr +def arr(app, port, kind, root_kind): + base = f"http://{ip(app)}:{port}/api/v3" + # Host = the container name: Sonarr/Radarr refuse a bare IP once the household sets „Allowed hosts" + # (measured 2026-10-14 18:30: 400 „Invalid Hostname" for Host=, 200 for Host=sonarr) + H = {"X-Api-Key": apikey(app), "Host": app} + say(f"== {app} {base} (key: in /config/config.xml, read from the config volume)") + c, tags, _ = call("GET", base + "/tag", H) + have = {t["label"]: t["id"] for t in tags or []} + for tag, _d in DRIVES: + if tag not in have: + c, t, _ = call("POST", base + "/tag", H, {"label": tag}) + have[tag] = t["id"] + say(f" tags: {have}") + c, roots, _ = call("GET", base + "/rootfolder", H) + rp = {r["path"].rstrip("/") for r in roots or []} + for tag, d in DRIVES: + p = f"/mnt/felhom-drives/{d}/userdata/media/{root_kind}" + if p not in rp: + call("POST", base + "/rootfolder", H, {"path": p}) + c, roots, _ = call("GET", base + "/rootfolder", H) + for r in roots or []: + say(f" root folder: {r['path']} accessible={r.get('accessible')} freeSpace={r.get('freeSpace')}") + c, schema, _ = call("GET", base + "/downloadclient/schema", H) + sch = {s["implementation"]: s for s in schema} + c, clients, _ = call("GET", base + "/downloadclient", H) + names = {x["name"] for x in clients or []} + for tag, d in DRIVES: + M = f"/mnt/felhom-drives/{d}/userdata/media" + for impl, nm, vals in ( + ("QBittorrent", f"qBittorrent {tag}", {"host": "qbittorrent", "port": 8080, "username": "admin", + "password": qpw, f"{kind}Category": f"{'tv' if kind == 'tv' else 'movies'}-{tag}"}), + ("Transmission", f"Transmission {tag}", {"host": "transmission", "port": 9091, "username": "admin", + "password": tpw, "urlBase": "/transmission/", + # the schema's default category must be CLEARED: Sonarr/Radarr + # refuse „Cannot use Category and Directory" (measured, run 1) + f"{kind}Category": "", + f"{kind}Directory": f"{M}/transmission/{root_kind}"})): + if nm in names: + say(f" client {nm}: exists — left as it is") + continue + body = json.loads(json.dumps(sch[impl])) + body.update({"name": nm, "enable": True, "tags": [have[tag]], "priority": 1}) + for f in body["fields"]: + if f["name"] in vals: + f["value"] = vals[f["name"]] + c, r, _ = call("POST", base + "/downloadclient", H, body, expect=(200, 201, 400)) + if c == 400: + say(f" client {nm}: REFUSED by {app}'s own connection test: {redact(json.dumps(r))[:400]}") + c, clients, _ = call("GET", base + "/downloadclient", H) + for x in clients or []: + f = {k["name"]: k.get("value") for k in x["fields"]} + say(f" client {x['name']}: impl={x['implementation']} enable={x['enable']} tags={x['tags']} host={f.get('host')}" + f" category={f.get(kind + 'Category')} directory={f.get(kind + 'Directory')}") + c, mm, _ = call("GET", base + "/config/mediamanagement", H) + say(f" before: minimumFreeSpaceWhenImporting={mm.get('minimumFreeSpaceWhenImporting')} MB, " + f"copyUsingHardlinks={mm.get('copyUsingHardlinks')}") + mm["minimumFreeSpaceWhenImporting"] = MIN_FREE_MB + call("PUT", base + f"/config/mediamanagement/{mm['id']}", H, mm) + c, mm, _ = call("GET", base + "/config/mediamanagement", H) + say(f" after : minimumFreeSpaceWhenImporting={mm.get('minimumFreeSpaceWhenImporting')} MB, " + f"copyUsingHardlinks={mm.get('copyUsingHardlinks')} (default kept)") + + +arr("sonarr", 8989, "tv", "tv") +arr("radarr", 7878, "movie", "movies") +say("== done; Transmission reachable by its container name:", TR_BY_NAME) + +# ---------------------------------------------------------------- Prowlarr -> Sonarr / Radarr (Settings -> Apps) +def prowlarr(): + if not sh("docker ps -q -f name=^prowlarr$"): + say("== prowlarr: not installed — skipped") + return + base = f"http://{ip('prowlarr')}:9696/api/v1" + H = {"X-Api-Key": apikey("prowlarr"), "Host": "prowlarr"} + say(f"== prowlarr {base} (key: in /config/config.xml, read from the config volume)") + c, schema, _ = call("GET", base + "/applications/schema", H) + sch = {x["implementation"]: x for x in schema} + c, apps, _ = call("GET", base + "/applications", H) + have = {a["name"] for a in apps or []} + for impl, port in (("Sonarr", 8989), ("Radarr", 7878)): + if impl in have: + say(f" application {impl}: exists — left as it is") + continue + body = json.loads(json.dumps(sch[impl])) + vals = {"prowlarrUrl": "http://prowlarr:9696", "baseUrl": f"http://{impl.lower()}:{port}", + "apiKey": apikey(impl.lower())} + for f in body["fields"]: + if f["name"] in vals: + f["value"] = vals[f["name"]] + body.update({"name": impl, "syncLevel": "fullSync", "tags": []}) + c, r, _ = call("POST", base + "/applications", H, body, expect=(200, 201, 400)) + if c == 400: + say(f" application {impl}: REFUSED by Prowlarr's own test: {redact(json.dumps(r))[:400]}") + c, apps, _ = call("GET", base + "/applications", H) + for a in apps or []: + f = {k["name"]: k.get("value") for k in a["fields"]} + say(f" application {a['name']}: syncLevel={a.get('syncLevel')} prowlarrUrl={f.get('prowlarrUrl')} baseUrl={f.get('baseUrl')}" + f" syncCategories={f.get('syncCategories')}") + c, ix, _ = call("GET", base + "/indexer", H) + say(f" indexers in Prowlarr: {[i['name'] for i in ix or []]}") + c, cmd, _ = call("POST", base + "/command", H, {"name": "ApplicationIndexerSync"}) + cid = (cmd or {}).get("id") + import time + for _ in range(20): + time.sleep(3) + c, st, _ = call("GET", base + f"/command/{cid}", H) + if (st or {}).get("status") in ("completed", "failed", "aborted"): + break + say(f" ApplicationIndexerSync -> status={(st or {}).get('status')} result={(st or {}).get('result')} message={(st or {}).get('message')}") + for app, port in (("sonarr", 8989), ("radarr", 7878)): + c, ai, _ = call("GET", f"http://{ip(app)}:{port}/api/v3/indexer", {"X-Api-Key": apikey(app), "Host": app}) + say(f" {app} now holds {len(ai or [])} indexer(s): {[(i['name'], i.get('implementation')) for i in ai or []]}") + + +prowlarr() +say("== prowlarr part done") + + +# ---------------------------------------------------------------- Plex: add every drive's folder to the EXISTING libraries +def plex(): + if not sh("docker ps -q -f name=^plex$"): + say("== plex: not installed — skipped"); return + P = "/var/lib/docker/volumes/plex_plex_config/_data/Library/Application Support/Plex Media Server/Preferences.xml" + tok = re.search(r'PlexOnlineToken="([^"]+)', open(P).read()).group(1) # the server's own token (K2-5: 401 without) + SECRETS.append(tok) + B = f"http://{ip('plex')}:32400" + H = {"Accept": "application/json", "X-Plex-Token": tok} + say(f"== plex {B} (token: the server's own PlexOnlineToken in Preferences.xml)") + c, d, _ = call("GET", B + "/library/sections", H) + want = {"show": "tv", "movie": "movies"} + for sec in (d or {}).get("MediaContainer", {}).get("Directory", []): + sub = want.get(sec["type"]) + if not sub: + continue + have = [l["path"] for l in sec.get("Location", [])] + need = [f"/mnt/felhom-drives/{dd}/userdata/media/{sub}" for _t, dd in DRIVES] + missing = [x for x in need if x not in have] + if not missing: + say(f" library {sec['title']}: every drive's folder already in it — left as it is"); continue + q = [("name", sec["title"]), ("type", sec["type"]), ("agent", sec["agent"]), ("scanner", sec["scanner"]), + ("language", sec["language"])] + [("location", x) for x in have + missing] + c, _, _ = call("PUT", B + f"/library/sections/{sec['key']}?" + urllib.parse.urlencode(q), H, expect=(200, 201, 204)) + say(f" library {sec['title']}: added {missing} -> {c}") + c, _, _ = call("GET", B + f"/library/sections/{sec['key']}/refresh", H) + say(f" library {sec['title']}: scan started -> {c}") + for k in ("FSEventLibraryUpdatesEnabled", "FSEventLibraryPartialScanEnabled"): + c, pr, _ = call("GET", B + "/:/prefs", H) + cur = next((x["value"] for x in pr["MediaContainer"]["Setting"] if x["id"] == k), None) + if cur is True: + say(f" pref {k}=True already"); continue + c, _, _ = call("PUT", B + f"/:/prefs?{k}=1", H, expect=(200, 204)) + say(f" pref {k} (R-1037: scan when a change is detected) -> {c}") + time.sleep(20) + c, d, _ = call("GET", B + "/library/sections", H) + for sec in (d or {}).get("MediaContainer", {}).get("Directory", []): + c2, a, _ = call("GET", B + f"/library/sections/{sec['key']}/all", H) + say(f" library {sec['title']}: folders={[l['path'].split('/')[3] for l in sec.get('Location', [])]} items={[m['title'] for m in (a or {}).get('MediaContainer', {}).get('Metadata', [])]}") + + +# ---------------------------------------------------------------- Jellyfin: add every drive's folder to the EXISTING libraries +def jellyfin(): + if not sh("docker ps -q -f name=^jellyfin$"): + say("== jellyfin: not installed — skipped"); return + B = f"http://{ip('jellyfin')}:8096" + A = 'MediaBrowser Client="felhom", Device="wire", DeviceId="felhom-wire", Version="1"' + SECRETS.append(JFPW) + c, r, _ = call("POST", B + "/Users/AuthenticateByName", {"Authorization": A}, {"Username": "admin", "Pw": JFPW}) + tok = r["AccessToken"]; SECRETS.append(tok) + H = {"X-Emby-Token": tok} + say(f"== jellyfin {B} (an admin session: the box made the first admin itself, so it holds the password)") + c, keys, _ = call("GET", B + "/Auth/Keys", H) + if not any(k.get("AppName") == "felhom" for k in (keys or {}).get("Items", [])): + c, _, _ = call("POST", B + "/Auth/Keys?app=felhom", H, expect=(200, 204)) + say(f" API key for the box (POST /Auth/Keys?app=felhom) -> {c} (needs an admin session; without one, one household step)") + else: + say(" API key for the box: exists") + c, vf, _ = call("GET", B + "/Library/VirtualFolders", H) + want = {"tvshows": "tv", "movies": "movies"} + for v in vf or []: + sub = want.get(v.get("CollectionType")) + if not sub: + continue + need = [f"/mnt/felhom-drives/{dd}/userdata/media/{sub}" for _t, dd in DRIVES] + missing = [x for x in need if x not in v.get("Locations", [])] + if not missing: + say(f" library {v['Name']}: every drive's folder already in it — left as it is"); continue + for x in missing: + c, _, _ = call("POST", B + "/Library/VirtualFolders/Paths?refreshLibrary=false", H, + {"Name": v["Name"], "PathInfo": {"Path": x}}, expect=(200, 204)) + say(f" library {v['Name']}: added {x} -> {c}") + c, _, _ = call("POST", B + "/Library/Refresh", H, expect=(200, 204)) + say(f" scan -> {c}") + time.sleep(25) + c, vf, _ = call("GET", B + "/Library/VirtualFolders", H) + for v in vf or []: + say(f" library {v['Name']}: folders={[l.split('/')[3] for l in v.get('Locations', [])]}") + c, it, _ = call("GET", B + "/Items?Recursive=true&IncludeItemTypes=Series,Movie", H) + say(f" items: {sorted(i['Name'] for i in (it or {}).get('Items', []))}") + + +plex() +jellyfin() +say("== all parts done")