Clarify and live-refresh media indexing state
This commit is contained in:
@@ -661,13 +661,25 @@ def _build_presentation(
|
|||||||
pipeline_torrents = []
|
pipeline_torrents = []
|
||||||
|
|
||||||
if jellyfin_partial:
|
if jellyfin_partial:
|
||||||
available_state, available_summary = "partial", f"{available} of {total} episodes available"
|
available_label = "Partially available"
|
||||||
|
available_state = "partial"
|
||||||
|
available_state_label = "Partly ready"
|
||||||
|
available_summary = f"{available} of {total} episodes are ready to watch in Grizzlyflix."
|
||||||
elif jellyfin_found:
|
elif jellyfin_found:
|
||||||
available_state, available_summary = "complete", "Available to watch"
|
available_label = "Available to watch"
|
||||||
|
available_state = "complete"
|
||||||
|
available_state_label = "Ready"
|
||||||
|
available_summary = "This title is ready to watch in Grizzlyflix."
|
||||||
elif arr_state == "available":
|
elif arr_state == "available":
|
||||||
available_state, available_summary = "active", "Waiting for the media server to index this title"
|
available_label = "Adding to Grizzlyflix"
|
||||||
|
available_state = "active"
|
||||||
|
available_state_label = "Indexing"
|
||||||
|
available_summary = "The download is complete. Grizzlyflix is indexing this title now."
|
||||||
else:
|
else:
|
||||||
available_state, available_summary = "waiting", "Not available on the media server yet"
|
available_label = "Media server"
|
||||||
|
available_state = "waiting"
|
||||||
|
available_state_label = "Waiting"
|
||||||
|
available_summary = "This title has not reached Grizzlyflix yet."
|
||||||
|
|
||||||
display_download = dict(download)
|
display_download = dict(download)
|
||||||
if fully_available:
|
if fully_available:
|
||||||
@@ -723,8 +735,9 @@ def _build_presentation(
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "available",
|
"id": "available",
|
||||||
"label": "Available",
|
"label": available_label,
|
||||||
"state": available_state,
|
"state": available_state,
|
||||||
|
"stateLabel": available_state_label,
|
||||||
"summary": available_summary,
|
"summary": available_summary,
|
||||||
"link": jellyfin_link,
|
"link": jellyfin_link,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -475,8 +475,14 @@ class RequestPresentationTests(unittest.TestCase):
|
|||||||
"The requested content has been collected and is available to watch. No further action is needed.",
|
"The requested content has been collected and is available to watch. No further action is needed.",
|
||||||
)
|
)
|
||||||
search_stage = next(stage for stage in presentation["pipeline"] if stage["id"] == "search")
|
search_stage = next(stage for stage in presentation["pipeline"] if stage["id"] == "search")
|
||||||
|
available_stage = next(stage for stage in presentation["pipeline"] if stage["id"] == "available")
|
||||||
self.assertEqual(search_stage["state"], "complete")
|
self.assertEqual(search_stage["state"], "complete")
|
||||||
self.assertEqual(search_stage["actionIds"], [])
|
self.assertEqual(search_stage["actionIds"], [])
|
||||||
|
self.assertEqual(available_stage["state"], "complete")
|
||||||
|
self.assertEqual(available_stage["stateLabel"], "Ready")
|
||||||
|
self.assertEqual(available_stage["label"], "Available to watch")
|
||||||
|
self.assertEqual(available_stage["summary"], "This title is ready to watch in Grizzlyflix.")
|
||||||
|
self.assertEqual(available_stage["link"], "https://media.test/title/3909")
|
||||||
|
|
||||||
def test_partially_available_content_keeps_missing_download_attention(self) -> None:
|
def test_partially_available_content_keeps_missing_download_attention(self) -> None:
|
||||||
snapshot = Snapshot(
|
snapshot = Snapshot(
|
||||||
@@ -546,6 +552,12 @@ class RequestPresentationTests(unittest.TestCase):
|
|||||||
self.assertEqual(search_stage["state"], "complete")
|
self.assertEqual(search_stage["state"], "complete")
|
||||||
self.assertEqual(download_stage["state"], "complete")
|
self.assertEqual(download_stage["state"], "complete")
|
||||||
self.assertEqual(available_stage["state"], "active")
|
self.assertEqual(available_stage["state"], "active")
|
||||||
|
self.assertEqual(available_stage["stateLabel"], "Indexing")
|
||||||
|
self.assertEqual(available_stage["label"], "Adding to Grizzlyflix")
|
||||||
|
self.assertEqual(
|
||||||
|
available_stage["summary"],
|
||||||
|
"The download is complete. Grizzlyflix is indexing this title now.",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class RequestCreationFlowTests(unittest.IsolatedAsyncioTestCase):
|
class RequestCreationFlowTests(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ type PipelineStage = {
|
|||||||
id: string
|
id: string
|
||||||
label: string
|
label: string
|
||||||
state: 'complete' | 'active' | 'partial' | 'attention' | 'waiting' | string
|
state: 'complete' | 'active' | 'partial' | 'attention' | 'waiting' | string
|
||||||
|
stateLabel?: string
|
||||||
summary: string
|
summary: string
|
||||||
available?: number
|
available?: number
|
||||||
missing?: number
|
missing?: number
|
||||||
@@ -227,6 +228,7 @@ const fallbackStatusLabel = (state: string) => {
|
|||||||
const fallbackPipeline = (snapshot: Snapshot): PipelineStage[] => {
|
const fallbackPipeline = (snapshot: Snapshot): PipelineStage[] => {
|
||||||
const approved = snapshot.state !== 'REQUESTED'
|
const approved = snapshot.state !== 'REQUESTED'
|
||||||
const complete = ['COMPLETED', 'AVAILABLE'].includes(snapshot.state)
|
const complete = ['COMPLETED', 'AVAILABLE'].includes(snapshot.state)
|
||||||
|
const indexing = snapshot.state === 'IMPORTING'
|
||||||
return [
|
return [
|
||||||
{ id: 'requested', label: 'Requested', state: 'complete', summary: 'Request received' },
|
{ id: 'requested', label: 'Requested', state: 'complete', summary: 'Request received' },
|
||||||
{
|
{
|
||||||
@@ -245,9 +247,14 @@ const fallbackPipeline = (snapshot: Snapshot): PipelineStage[] => {
|
|||||||
{ id: 'download', label: 'Download', state: 'waiting', summary: 'No download attempt yet' },
|
{ id: 'download', label: 'Download', state: 'waiting', summary: 'No download attempt yet' },
|
||||||
{
|
{
|
||||||
id: 'available',
|
id: 'available',
|
||||||
label: 'Available',
|
label: complete ? 'Available to watch' : indexing ? 'Adding to Grizzlyflix' : 'Media server',
|
||||||
state: complete ? 'complete' : 'waiting',
|
state: complete ? 'complete' : indexing ? 'active' : 'waiting',
|
||||||
summary: complete ? 'Available to watch' : 'Not available on the media server yet',
|
stateLabel: complete ? 'Ready' : indexing ? 'Indexing' : 'Waiting',
|
||||||
|
summary: complete
|
||||||
|
? 'This title is ready to watch in Grizzlyflix.'
|
||||||
|
: indexing
|
||||||
|
? 'The download is complete. Grizzlyflix is indexing this title now.'
|
||||||
|
: 'This title has not reached Grizzlyflix yet.',
|
||||||
link: snapshot.raw?.jellyfin?.link,
|
link: snapshot.raw?.jellyfin?.link,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -276,6 +283,11 @@ export default function RequestTimelinePage() {
|
|||||||
const [historyActions, setHistoryActions] = useState<ActionHistory[]>([])
|
const [historyActions, setHistoryActions] = useState<ActionHistory[]>([])
|
||||||
const [operationProgress, setOperationProgress] = useState<OperationProgress | null>(null)
|
const [operationProgress, setOperationProgress] = useState<OperationProgress | null>(null)
|
||||||
const [isAdmin, setIsAdmin] = useState(false)
|
const [isAdmin, setIsAdmin] = useState(false)
|
||||||
|
const awaitingMediaIndex = Boolean(
|
||||||
|
snapshot?.presentation?.pipeline?.some(
|
||||||
|
(stage) => stage.id === 'available' && stage.state === 'active'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!requestId) return
|
if (!requestId) return
|
||||||
@@ -355,12 +367,12 @@ export default function RequestTimelinePage() {
|
|||||||
if (!stopped) console.error(error)
|
if (!stopped) console.error(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const timer = window.setInterval(() => void refresh(), 15_000)
|
const timer = window.setInterval(() => void refresh(), awaitingMediaIndex ? 5_000 : 15_000)
|
||||||
return () => {
|
return () => {
|
||||||
stopped = true
|
stopped = true
|
||||||
window.clearInterval(timer)
|
window.clearInterval(timer)
|
||||||
}
|
}
|
||||||
}, [requestId, router])
|
}, [awaitingMediaIndex, requestId, router])
|
||||||
|
|
||||||
const liveDownloadKey = useMemo(() => {
|
const liveDownloadKey = useMemo(() => {
|
||||||
const downloadStage = snapshot?.presentation?.pipeline?.find((stage) => stage.id === 'download')
|
const downloadStage = snapshot?.presentation?.pipeline?.find((stage) => stage.id === 'download')
|
||||||
@@ -741,7 +753,7 @@ export default function RequestTimelinePage() {
|
|||||||
<>
|
<>
|
||||||
<div className="request-stage-topline">
|
<div className="request-stage-topline">
|
||||||
<span className="request-stage-number">{String(index + 1).padStart(2, '0')}</span>
|
<span className="request-stage-number">{String(index + 1).padStart(2, '0')}</span>
|
||||||
<span className={`request-stage-state state-${stage.state}`}>{stage.state}</span>
|
<span className={`request-stage-state state-${stage.state}`}>{stage.stateLabel ?? stage.state}</span>
|
||||||
</div>
|
</div>
|
||||||
<h3>{stage.label}</h3>
|
<h3>{stage.label}</h3>
|
||||||
<p>{stage.summary}</p>
|
<p>{stage.summary}</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user