Media drive events, night 2026-10-14: 15 §10 (event x app, measured), K4-K8 evidence, rows R-1044..R-1049, narrowed R-1022/1023/1024/1026/1028/1037/1038, night report
gates / gates (push) Successful in 6m34s
gates / gates (push) Successful in 6m34s
- 15-media-library.md §10: what the box does on each drive event, per app, with the measured call; the guards that ARE needed and the ones that are NOT (K1: nothing lost while a drive is gone). - Evidence K4 (dead-drive check: mountinfo `shutdown`, stat unreliable), K5 (restart), K6 (unclaimed Plex), K7 (old Sonarr/Radarr repair on the bench), K8 (hub received the drive-return events and mailed none), K9 (files identical). - nightly-rotation.md runs scripts/https_only_check.py first every night; scripts/CHANGELOG.md entry. - STATUS, CONTEXT (decisions 244-246), REPORT-night-2026-10-14.md. Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
=== all live (B has a returned-drive history, C was re-registered) 21:20:37
|
||||
adatlemez A: mounts stacked=2 top-super=rw shutdown=False | B: stat ok in 4 ms | => live
|
||||
media_a A: mounts stacked=2 top-super=rw shutdown=False | B: stat ok in 3 ms | => live
|
||||
media_b A: mounts stacked=2 top-super=rw shutdown=False | B: stat ok in 3 ms | => live
|
||||
media_c A: mounts stacked=2 top-super=rw shutdown=False | B: stat ok in 2 ms | => live
|
||||
=== Media C DEAD (pulled 30 s ago) 21:21:22
|
||||
adatlemez A: mounts stacked=2 top-super=rw shutdown=False | B: stat ok in 2 ms | => live
|
||||
media_a A: mounts stacked=2 top-super=rw shutdown=False | B: stat ok in 2 ms | => live
|
||||
media_b A: mounts stacked=2 top-super=rw shutdown=False | B: stat ok in 2 ms | => live
|
||||
media_c A: mounts stacked=2 top-super=rw,shutdown shutdown=True | B: stat ok in 2 ms | => DEAD
|
||||
=== Media C RETURNED (re-attached 60 s ago, no app restart) 21:22:25
|
||||
adatlemez A: mounts stacked=2 top-super=rw shutdown=False | B: stat ok in 3 ms | => live
|
||||
media_a A: mounts stacked=2 top-super=rw shutdown=False | B: stat ok in 3 ms | => live
|
||||
media_b A: mounts stacked=2 top-super=rw shutdown=False | B: stat ok in 3 ms | => live
|
||||
media_c A: mounts stacked=2 top-super=rw shutdown=False | B: stat ok in 3 ms | => live
|
||||
958 405 8:64 /felhom-data /mnt/felhom-drives/media_c rw,relatime shared:506 master:591 - ext4 /dev/sde rw
|
||||
959 804 8:64 /felhom-data /mnt/felhom-drives/media_c rw,relatime shared:506 master:591 - ext4 /dev/sde rw
|
||||
sonarr sees C: ls: cannot access '/mnt/felhom-drives/media_c/userdata/media/tv': I/O error
|
||||
filebrowser sees C: ls: /mnt/felhom-drives/media_c/userdata/media/tv: No such file or directory
|
||||
sonarr (pid 463719): ext4 /dev/sdd rw,shutdown
|
||||
radarr (pid 460265): ext4 /dev/sdd rw,shutdown
|
||||
qbittorrent (pid 464094): ext4 /dev/sdd rw,shutdown
|
||||
transmission (pid 461036): ext4 /dev/sdd rw,shutdown
|
||||
plex (pid 461609): ext4 /dev/sdd rw,shutdown
|
||||
jellyfin (pid 462353): ext4 /dev/sdd rw,shutdown
|
||||
filebrowser (pid 458188): ext4 /dev/sdd rw,shutdown
|
||||
@@ -0,0 +1,18 @@
|
||||
qbittorrent dashboard stop+start (200,200) 2.9s
|
||||
transmission dashboard stop+start (200,200) 6.1s
|
||||
sonarr dashboard stop+start (200,200) 4.1s
|
||||
radarr dashboard stop+start (200,200) 4.3s
|
||||
plex dashboard stop+start (200,200) 9.8s
|
||||
jellyfin dashboard stop+start (200,200) 1.9s
|
||||
filebrowser compose up -d --force-recreate (no variables in its compose — the controller's own composeUp would run the same): 5.2s Container filebrowser Started
|
||||
sonarr: /dev/sde rw
|
||||
radarr: /dev/sde rw
|
||||
qbittorrent: /dev/sde rw
|
||||
transmission: /dev/sde rw
|
||||
plex: /dev/sde rw
|
||||
jellyfin: /dev/sde rw
|
||||
filebrowser: /dev/sde rw
|
||||
Felhom Probe Show (2026)
|
||||
sonarr rule: Host(`sonarr.enkicsifelhom.hu`)
|
||||
qbittorrent rule: Host(`torrent.enkicsifelhom.hu`)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
jellyfin restarted (docker stop+start) at +10 s, took 10.4s
|
||||
the stream: curl exit=56 200 bytes=22970488 exit_after (exit 18/56 = the transfer was cut)
|
||||
jellyfin answers again 22s after the restart began
|
||||
@@ -0,0 +1,15 @@
|
||||
started
|
||||
identity: version="1.0" claimed="0" version="1.41.4.9463-630c9f557"
|
||||
no token, from another container: GET /library/sections -> 200
|
||||
no token, from another container: GET /:/prefs -> 200
|
||||
no token, from another container: GET /myplex/account -> 200
|
||||
POST /myplex/claim with an INVALID code -> 500<html><head><title>Internal Server Error</title></head><body><h1>500 Internal Server Error</h1></body></html>
|
||||
public route: none (no traefik labels):
|
||||
Oct 14, 2026 23:25:52.548 [129866045209400] DEBUG - [Req#f5/HCl#5b] HTTP requesting GET https://plex.tv/api/v2/release_channels?X-Plex-Token=
|
||||
Oct 14, 2026 23:25:52.690 [129866068745016] DEBUG - [HttpClient/HCl#5b] HTTP/1.1 (0.1s) 200 response from GET https://plex.tv/api/v2/release_channels?X-Plex-Token=xxxxxxxxx
|
||||
Oct 14, 2026 23:25:52.690 [129866045209400] DEBUG - [Req#f5/HCl#5c] HTTP requesting GET https://plex.tv/api/v2/features?X-Plex-Token=
|
||||
Oct 14, 2026 23:25:52.852 [129866068745016] DEBUG - [HttpClient/HCl#5c] HTTP/1.1 (0.2s) 200 response from GET https://plex.tv/api/v2/features?X-Plex-Token=xxxxxxxxx
|
||||
Oct 14, 2026 23:25:52.853 [129866045209400] DEBUG - [Req#f5/HCl#5d] HTTP requesting GET https://plex.tv/api/v2/server/users/features
|
||||
Oct 14, 2026 23:25:52.988 [129866068745016] DEBUG - [HttpClient/HCl#5d] HTTP/1.1 (0.1s) 401 response from GET https://plex.tv/api/v2/server/users/features (reused)
|
||||
Oct 14, 2026 23:25:52.988 [129866043099960] ERROR - [Req#f5] MyPlex: Error 401 requesting JSON from: https://plex.tv/api/v2/server/users/features
|
||||
Oct 14, 2026 23:25:52.988 [129866080086840] DEBUG - Completed: [172.18.0.2:43806] 500 POST /myplex/claim?token=<code> (6 live) #f5 991ms 325 bytes (pipelined: 1)
|
||||
@@ -0,0 +1,7 @@
|
||||
# K6 predictions (before the run, ~23:28 CEST)
|
||||
- An unclaimed Plex starts fine with no PLEX_CLAIM; /identity says claimed=0.
|
||||
- While unclaimed, Plex lets a request from a private address in WITHOUT a token (it treats the LAN as the owner) —
|
||||
which is exactly why it must not be reachable through the tunnel unclaimed (traefik's address is private).
|
||||
- Plex's own API takes a claim code AFTER the start: POST /myplex/claim?token=<code> on the server — so the box can
|
||||
ask for the code only when the server is up (no 4-minute race with the image pull). Proof needs the household's
|
||||
plex.tv account → the morning step; without it, the route's answer to an invalid code shows it exists.
|
||||
@@ -0,0 +1,49 @@
|
||||
0
|
||||
======== sonarr
|
||||
Container sonarr Started
|
||||
1. installed from the OLD template (bind /media/tv)
|
||||
rescan -> completed
|
||||
[OLD template] root folders: [('/media/tv', True)]
|
||||
[OLD template] Problem Child: path=/media/tv/Problem Child files=1 sizeOnDisk=145093
|
||||
[OLD template] health: [('IndexerRssCheck', 'No indexers available with RSS sync enabled, Sonarr will not grab new releases automatical'), ('DownloadClientCheck', 'No download client is available'), ('IndexerSearchCheck', 'No indexers available with Automatic Search enabled, Sonarr will not provide any automatic')]
|
||||
2. updated to TODAY's template (bc54c01): one bind, the same path inside
|
||||
Container sonarr Started
|
||||
rescan -> completed
|
||||
[NEW template, before the repair] root folders: [('/media/tv', False)]
|
||||
[NEW template, before the repair] Problem Child: path=/media/tv/Problem Child files=1 sizeOnDisk=145093
|
||||
[NEW template, before the repair] health: [('DownloadClientCheck', 'No download client is available'), ('IndexerSearchCheck', 'No indexers available with Automatic Search enabled, Sonarr will not provide any automatic'), ('IndexerRssCheck', 'No indexers available with RSS sync enabled, Sonarr will not grab new releases automatical'), ('RootFolderCheck', 'Missing root folder: /media/tv')]
|
||||
3. the repair, through the app's own API, NO file moved
|
||||
add root folder /srv/k7/userdata/media/tv -> 201
|
||||
editor: rootFolderPath -> /srv/k7/userdata/media/tv, moveFiles=false -> 202
|
||||
rescan -> completed
|
||||
remove the old root folder /media/tv -> 200
|
||||
[NEW template, after the repair] root folders: [('/srv/k7/userdata/media/tv', True)]
|
||||
[NEW template, after the repair] Problem Child: path=/srv/k7/userdata/media/tv/Problem Child files=1 sizeOnDisk=145093
|
||||
[NEW template, after the repair] health: [('DownloadClientCheck', 'No download client is available'), ('IndexerSearchCheck', 'No indexers available with Automatic Search enabled, Sonarr will not provide any automatic'), ('IndexerRssCheck', 'No indexers available with RSS sync enabled, Sonarr will not grab new releases automatical'), ('RootFolderCheck', 'Missing root folder: /media/tv')]
|
||||
the file: same inode before and after = True (moved by nobody), size=145093
|
||||
======== radarr
|
||||
Container radarr Started
|
||||
1. installed from the OLD template (bind /media/movies)
|
||||
rescan -> completed
|
||||
[OLD template] root folders: [('/media/movies', True)]
|
||||
[OLD template] Night of the Living Dead: path=/media/movies/Night of the Living Dead (1968) files=True sizeOnDisk=290186
|
||||
[OLD template] health: [('IndexerRssCheck', 'No indexers available with RSS sync enabled, Radarr will not grab new releases automatical'), ('DownloadClientCheck', 'No download client is available'), ('IndexerSearchCheck', 'No indexers available with Automatic Search enabled, Radarr will not provide any automatic')]
|
||||
2. updated to TODAY's template (bc54c01): one bind, the same path inside
|
||||
Container radarr Started
|
||||
rescan -> completed
|
||||
[NEW template, before the repair] root folders: [('/media/movies', False)]
|
||||
[NEW template, before the repair] Night of the Living Dead: path=/media/movies/Night of the Living Dead (1968) files=True sizeOnDisk=290186
|
||||
[NEW template, before the repair] health: [('IndexerRssCheck', 'No indexers available with RSS sync enabled, Radarr will not grab new releases automatical'), ('DownloadClientCheck', 'No download client is available'), ('RootFolderCheck', 'Missing root folder: /media/movies'), ('IndexerSearchCheck', 'No indexers available with Automatic Search enabled, Radarr will not provide any automatic'), ('MovieCollectionRootFolderCheck', 'Missing root folder for movie collection: /media/movies (Living Dead Collection)')]
|
||||
3. the repair, through the app's own API, NO file moved
|
||||
add root folder /srv/k7/userdata/media/movies -> 201
|
||||
editor: rootFolderPath -> /srv/k7/userdata/media/movies, moveFiles=false -> 202
|
||||
rescan -> completed
|
||||
remove the old root folder /media/movies -> 200
|
||||
[NEW template, after the repair] root folders: [('/srv/k7/userdata/media/movies', True)]
|
||||
[NEW template, after the repair] Night of the Living Dead: path=/srv/k7/userdata/media/movies/Night of the Living Dead (1968) files=True sizeOnDisk=290186
|
||||
[NEW template, after the repair] health: [('IndexerRssCheck', 'No indexers available with RSS sync enabled, Radarr will not grab new releases automatical'), ('DownloadClientCheck', 'No download client is available'), ('RootFolderCheck', 'Missing root folder: /media/movies'), ('IndexerSearchCheck', 'No indexers available with Automatic Search enabled, Radarr will not provide any automatic'), ('MovieCollectionRootFolderCheck', 'Missing root folder for movie collection: /media/movies (Living Dead Collection)')]
|
||||
the file: same inode before and after = True (moved by nobody), size=145093
|
||||
======== teardown
|
||||
Volume k7sonarr_sonarr_config Removed
|
||||
Volume k7radarr_radarr_config Removed
|
||||
left: 0 containers; 0 volumes
|
||||
@@ -0,0 +1,29 @@
|
||||
# K8 — what the hub received and what it mailed for the night's drive events (tester-1), read 2026-10-15 ~00:00 CEST
|
||||
# Two channels: (1) the hub's own DB, copied from the RUNNING pod with hub.db-wal + -shm (R-286), times UTC;
|
||||
# (2) the operator's mailbox (Gmail connector, the @felhom.eu catch-all), times CEST — the control from another channel.
|
||||
|
||||
== hub events (UTC)
|
||||
20:55:01 storage_disconnected error Meghajtó váratlanul leválasztva: Media B (detach 22:54:48 CEST)
|
||||
20:56:29 health_degraded warning Rendszer állapot romlott (volt: ok)
|
||||
20:59:32 storage_reconnected info Meghajtó újra csatlakoztatva: Media B (re-attach 22:59:00 CEST)
|
||||
21:01:28 health_recovered info Rendszer állapot helyreállt: ok (volt: warn)
|
||||
21:11:28 app_start_failed warning Telepített alkalmazás nem fut: Jellyfin (K3-4, restart after the eject)
|
||||
21:11:28 app_start_failed warning Telepített alkalmazás nem fut: Plex (K3-4)
|
||||
21:21:01 storage_disconnected error Meghajtó váratlanul leválasztva: Media C (detach 23:20:47 CEST)
|
||||
21:21:28 health_degraded warning Rendszer állapot romlott (volt: ok)
|
||||
21:21:32 storage_reconnected info Meghajtó újra csatlakoztatva: Media C (re-attach 23:21:22 CEST)
|
||||
21:26:28 health_recovered info Rendszer állapot helyreállt: ok (volt: warn)
|
||||
(no event for the safe removal 23:10:23 nor for the disk detach 23:12:41 after it — the drive was deregistered)
|
||||
|
||||
== hub notification_log (UTC)
|
||||
20:55:01 storage_disconnected sent operator
|
||||
20:56:29 health_degraded sent operator
|
||||
21:11:29 app_start_failed sent operator (x2)
|
||||
21:21:01 storage_disconnected sent operator
|
||||
21:21:28 health_degraded suppressed operator
|
||||
-> NO row for either storage_reconnected or either health_recovered: received, never mailed.
|
||||
|
||||
== mailbox (CEST), monitoring@felhom.eu, after 2026-10-14 22:50
|
||||
22:55 storage_disconnected Media B · 22:56 health_degraded · 23:11 app_start_failed Jellyfin · 23:11 app_start_failed Plex
|
||||
23:21 storage_disconnected Media C
|
||||
-> the mailbox agrees with notification_log: five mails, no „came back" and no „recovered".
|
||||
@@ -0,0 +1,70 @@
|
||||
/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
|
||||
/mnt/felhom-drives/media_c/userdata/media/tv/Felhom Probe Show (2026)/Season 01/Felhom Probe Show (2026) - S01E01.mkv 145093 655382 1 1792011968
|
||||
/mnt/felhom-drives/media_c/userdata/media/transmission/tv/k3-own-bytes-trans/data.bin 3000000 655393 1 1792012185
|
||||
/mnt/felhom-drives/media_c/userdata/media/movies/Felhom Probe Film (2026)/Felhom Probe Film (2026).mkv 145093 655383 1 1792011968
|
||||
/mnt/felhom-drives/media_c/userdata/media/torrents/tv/k3-own-bytes-qbit/data.bin 3000000 655391 1 1792012185
|
||||
|
||||
|
@@ -7,3 +7,7 @@
|
||||
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)
|
||||
23:20:47 K4 DETACH Media C (scsi4) — a pull without safe removal
|
||||
23:21:22 K4 RE-ATTACH Media C (scsi4)
|
||||
23:30 K7 done on the bench; bench shut down as found
|
||||
23:30 END check: Media A+B files identical to the baseline
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env python3
|
||||
"""k4.py [label] — K4: tell a LIVE drive from a DEAD one (mountpoint yes, reads I/O error), run INSIDE the guest.
|
||||
|
||||
Two checks per /mnt/felhom-drives/<name>:
|
||||
A. NO disk I/O (the controller's rule: health checks issue no block I/O — gates.md): /proc/self/mountinfo only.
|
||||
How many mounts are stacked on the path, and the TOP one's super options (a dead ext4 says `shutdown`;
|
||||
the stacked dead one under a returned drive is listed too).
|
||||
B. A timed read in a SEPARATE process: `stat` of <path>/userdata with a 3 s timeout — the caller never blocks; the
|
||||
result and the time it took.
|
||||
"""
|
||||
import os, subprocess, sys, time
|
||||
|
||||
label = sys.argv[1] if len(sys.argv) > 1 else ""
|
||||
mi = open("/proc/self/mountinfo").read().splitlines()
|
||||
print(f"=== {label} {time.strftime('%H:%M:%S')}")
|
||||
for d in sorted(os.listdir("/mnt/felhom-drives")):
|
||||
p = f"/mnt/felhom-drives/{d}"
|
||||
rows = [l for l in mi if l.split()[4] == p]
|
||||
top = rows[-1] if rows else ""
|
||||
sup = top.split(" - ", 1)[1].split()[2] if " - " in top else "-"
|
||||
t0 = time.time()
|
||||
try:
|
||||
r = subprocess.run(["timeout", "3", "stat", "-c", "%i", f"{p}/userdata"], capture_output=True, text=True)
|
||||
res = "ok" if r.returncode == 0 else ("TIMEOUT" if r.returncode == 124 else "ERR " + r.stderr.strip().split(":")[-1].strip())
|
||||
except Exception as e:
|
||||
res = f"EXC {e}"
|
||||
dt = (time.time() - t0) * 1000
|
||||
shut = "shutdown" in sup.split(",")
|
||||
verdict = "DEAD" if (shut or res.startswith("ERR")) else ("absent" if not rows else "live")
|
||||
print(f" {d:10} A: mounts stacked={len(rows)} top-super={sup[:40]:40} shutdown={shut} | B: stat {res} in {dt:.0f} ms | => {verdict}")
|
||||
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env python3
|
||||
"""k7.py — K7 on the BENCH (LXC 9401): Sonarr and Radarr installed from the templates BEFORE app-catalog bc54c01
|
||||
(library at /media/tv, /media/movies inside the container), then updated to bc54c01 (one same-path `media` bind), then
|
||||
repaired through the apps' OWN API without moving a file. Needs /tmp/k7/{old,new}-{sonarr,radarr}.yml and
|
||||
/tmp/k7/probe.mkv (a 10 s self-made clip). Everything it makes is removed at the end (containers, volumes, folders).
|
||||
"""
|
||||
import json, os, re, shutil, subprocess, time, urllib.error, urllib.request
|
||||
|
||||
W = "/srv/k7"; U = f"{W}/userdata"
|
||||
|
||||
|
||||
def sh(c, t=600):
|
||||
r = subprocess.run(c, shell=True, capture_output=True, text=True, timeout=t)
|
||||
return (r.stdout + r.stderr).strip()
|
||||
|
||||
|
||||
def ip(c):
|
||||
return sh(f"docker inspect {c} -f '{{{{range .NetworkSettings.Networks}}}}{{{{.IPAddress}}}}{{{{end}}}}'")
|
||||
|
||||
|
||||
def call(m, url, H, body=None):
|
||||
h = dict(H); data = None
|
||||
if body is not None:
|
||||
data = json.dumps(body).encode(); h["Content-Type"] = "application/json"
|
||||
try:
|
||||
with urllib.request.urlopen(urllib.request.Request(url, data=data, headers=h, method=m), timeout=120) as r:
|
||||
t = r.read(); return r.status, (json.loads(t) if t else None)
|
||||
except urllib.error.HTTPError as e:
|
||||
return e.code, e.read().decode()[:300]
|
||||
|
||||
|
||||
def up(app, yml):
|
||||
os.makedirs(f"{W}/{app}", exist_ok=True); shutil.copy(yml, f"{W}/{app}/docker-compose.yml")
|
||||
open(f"{W}/{app}/.env", "w").write(f"DOMAIN=bench.invalid\nSUBDOMAIN={app}\nUSERDATA_PATH={U}\n")
|
||||
print(sh(f"cd {W}/{app} && docker compose -p k7{app} up -d 2>&1 | tail -1"))
|
||||
port = 8989 if app == "sonarr" else 7878
|
||||
for _ in range(60):
|
||||
time.sleep(3)
|
||||
if sh(f"docker inspect {app} -f '{{{{.State.Health.Status}}}}'") == "healthy":
|
||||
break
|
||||
key = re.search(r"<ApiKey>([^<]+)", sh(f"docker exec {app} cat /config/config.xml")).group(1)
|
||||
return f"http://{ip(app)}:{port}/api/v3", {"X-Api-Key": key}
|
||||
|
||||
|
||||
def cmd(base, H, name, **kw):
|
||||
c, d = call("POST", base + "/command", H, dict(name=name, **kw))
|
||||
for _ in range(60):
|
||||
time.sleep(2)
|
||||
c2, s = call("GET", base + f"/command/{d['id']}", H)
|
||||
if s.get("status") in ("completed", "failed"):
|
||||
return s["status"]
|
||||
|
||||
|
||||
def report(app, base, H, label):
|
||||
kind = "series" if app == "sonarr" else "movie"
|
||||
c, items = call("GET", base + f"/{kind}", H)
|
||||
c, roots = call("GET", base + "/rootfolder", H)
|
||||
c, health = call("GET", base + "/health", H)
|
||||
print(f" [{label}] root folders: {[(r['path'], r['accessible']) for r in roots]}")
|
||||
for x in items:
|
||||
st = x.get("statistics") or {}
|
||||
print(f" [{label}] {x['title']}: path={x['path']} files={st.get('episodeFileCount', x.get('hasFile'))} sizeOnDisk={st.get('sizeOnDisk')}")
|
||||
print(f" [{label}] health: {[(h['source'], h['message'][:90]) for h in health]}")
|
||||
|
||||
|
||||
for app, kind, lib, lookup, fname in (
|
||||
("sonarr", "series", "tv", "/series/lookup?term=tvdb:73705", "{t} - S01E01 - Episode.mkv"),
|
||||
("radarr", "movie", "movies", "/movie/lookup/tmdb?tmdbId=10331", "{t}.mkv")):
|
||||
print(f"======== {app}")
|
||||
os.makedirs(f"{U}/media/{lib}", exist_ok=True); os.makedirs(f"{U}/downloads", exist_ok=True)
|
||||
sh(f"chown -R 1000:1000 {W}; chmod -R 2775 {U}")
|
||||
base, H = up(app, f"/tmp/k7/old-{app}.yml")
|
||||
print(" 1. installed from the OLD template (bind /media/" + lib + ")")
|
||||
call("POST", base + "/rootfolder", H, {"path": f"/media/{lib}"})
|
||||
c, lk = call("GET", base + lookup, H)
|
||||
lk = lk[0] if isinstance(lk, list) else lk
|
||||
qp = call("GET", base + "/qualityprofile", H)[1][0]["id"]
|
||||
body = dict(lk, qualityProfileId=qp, rootFolderPath=f"/media/{lib}", monitored=True,
|
||||
addOptions={"searchForMissingEpisodes": False} if app == "sonarr" else {"searchForMovie": False})
|
||||
if app == "sonarr":
|
||||
body["languageProfileId"] = 1; body["seasonFolder"] = True
|
||||
c, item = call("POST", base + f"/{kind}", H, body)
|
||||
title_path = item["path"] # /media/tv/<Title> (container path)
|
||||
host_dir = f"{U}/media/{lib}/" + title_path.split(f"/media/{lib}/", 1)[1] + ("/Season 01" if app == "sonarr" else "")
|
||||
os.makedirs(host_dir, exist_ok=True)
|
||||
f = os.path.join(host_dir, fname.format(t=item["title"] + (f" ({item['year']})" if app == "radarr" else "")))
|
||||
shutil.copy("/tmp/k7/probe.mkv", f); sh(f"chown -R 1000:1000 '{U}/media/{lib}'")
|
||||
ino0 = os.stat(f).st_ino
|
||||
print(" rescan ->", cmd(base, H, "RescanSeries" if app == "sonarr" else "RescanMovie"))
|
||||
report(app, base, H, "OLD template")
|
||||
print(" 2. updated to TODAY's template (bc54c01): one bind, the same path inside")
|
||||
base, H = up(app, f"/tmp/k7/new-{app}.yml")
|
||||
print(" rescan ->", cmd(base, H, "RescanSeries" if app == "sonarr" else "RescanMovie"))
|
||||
report(app, base, H, "NEW template, before the repair")
|
||||
print(" 3. the repair, through the app's own API, NO file moved")
|
||||
new_root = f"{U}/media/{lib}"
|
||||
c, r = call("POST", base + "/rootfolder", H, {"path": new_root}); print(f" add root folder {new_root} -> {c}")
|
||||
c, items = call("GET", base + f"/{kind}", H)
|
||||
ids = [x["id"] for x in items]
|
||||
if app == "sonarr":
|
||||
c, r = call("PUT", base + "/series/editor", H, {"seriesIds": ids, "rootFolderPath": new_root, "moveFiles": False})
|
||||
else:
|
||||
c, r = call("PUT", base + "/movie/editor", H, {"movieIds": ids, "rootFolderPath": new_root, "moveFiles": False})
|
||||
print(f" editor: rootFolderPath -> {new_root}, moveFiles=false -> {c}")
|
||||
print(" rescan ->", cmd(base, H, "RescanSeries" if app == "sonarr" else "RescanMovie"))
|
||||
for r0 in call("GET", base + "/rootfolder", H)[1]:
|
||||
if r0["path"].rstrip("/") == f"/media/{lib}":
|
||||
print(f" remove the old root folder /media/{lib} -> {call('DELETE', base + f'/rootfolder/{r0['id']}', H)[0]}")
|
||||
report(app, base, H, "NEW template, after the repair")
|
||||
print(f" the file: same inode before and after = {os.stat(f).st_ino == ino0} (moved by nobody), size={os.path.getsize(f)}")
|
||||
|
||||
print("======== teardown")
|
||||
for app in ("sonarr", "radarr"):
|
||||
print(sh(f"cd {W}/{app} && docker compose -p k7{app} down -v 2>&1 | tail -1"))
|
||||
shutil.rmtree(W, ignore_errors=True)
|
||||
print("left:", sh("docker ps -aq -f name=sonarr -f name=radarr | wc -l"), "containers;", sh("docker volume ls -q | grep -c k7"), "volumes")
|
||||
Reference in New Issue
Block a user