diff --git a/clients/callback_client.go b/clients/callback_client.go index 103849519..527f2e450 100644 --- a/clients/callback_client.go +++ b/clients/callback_client.go @@ -46,9 +46,13 @@ func (c CallbackClient) DoWithRetries(r *http.Request) error { return nil } -func (c CallbackClient) SendTranscodeStatus(url string, status TranscodeStatus, completionRatio float32) error { +// Sends a Transcode Status message to the Client (initially just Studio) +// The status strings will be useful for debugging where in the workflow we got to, but everything +// in Studio will be driven off the overall "Completion Ratio". +// This method will accept the completion ratio of the current stage and will translate that into the overall ratio +func (c CallbackClient) SendTranscodeStatus(url string, status TranscodeStatus, currentStageCompletionRatio float64) error { tsm := TranscodeStatusMessage{ - CompletionRatio: completionRatio, + CompletionRatio: overallCompletionRatio(status, currentStageCompletionRatio), Status: status.String(), Timestamp: config.Clock.GetTimestampUTC(), } @@ -86,21 +90,54 @@ func (c CallbackClient) SendTranscodeStatusError(callbackURL, errorMsg string) e return c.DoWithRetries(r) } +// Calculate the overall completion ratio based on the completion ratio of the current stage. +// The weighting will need to be tweaked as we understand better the relative time spent in the +// segmenting vs. transcoding stages. +func overallCompletionRatio(status TranscodeStatus, currentStageCompletionRatio float64) float64 { + // Sanity check the inputs are within the 0-1 bounds + if currentStageCompletionRatio < 0 { + currentStageCompletionRatio = 0 + } + if currentStageCompletionRatio > 1 { + currentStageCompletionRatio = 1 + } + + // These are at the end of stages, so should always be 100% complete + if status == TranscodeStatusPreparingCompleted || status == TranscodeStatusCompleted { + currentStageCompletionRatio = 1 + } + + switch status { + case TranscodeStatusPreparing, TranscodeStatusPreparingCompleted: + return scaleProgress(currentStageCompletionRatio, 0, 0.4) + case TranscodeStatusTranscoding, TranscodeStatusCompleted: + return scaleProgress(currentStageCompletionRatio, 0.4, 1) + } + + // Either unhandled or an error + return -1 +} + +func scaleProgress(progress, start, end float64) float64 { + return start + progress*(end-start) +} + // An enum of potential statuses a Transcode job can have type TranscodeStatus int const ( TranscodeStatusPreparing TranscodeStatus = iota + TranscodeStatusPreparingCompleted TranscodeStatusTranscoding TranscodeStatusCompleted TranscodeStatusError ) type TranscodeStatusMessage struct { - CompletionRatio float32 `json:"completion_ratio,omitempty"` + CompletionRatio float64 `json:"completion_ratio"` // No omitempty or we lose this for 0% completion case Error string `json:"error,omitempty"` - Retriable bool `json:"retriable,omitempty"` + Retriable *bool `json:"retriable,omitempty"` // Has to be a pointer or we can't differentiate omission from 'false' Status string `json:"status,omitempty"` Timestamp int64 `json:"timestamp"` } @@ -109,10 +146,12 @@ func (ts TranscodeStatus) String() string { switch ts { case TranscodeStatusPreparing: return "preparing" + case TranscodeStatusPreparingCompleted: + return "preparing-completed" case TranscodeStatusTranscoding: return "transcoding" case TranscodeStatusCompleted: - return "completed" + return "success" case TranscodeStatusError: return "error" } diff --git a/clients/callback_client_test.go b/clients/callback_client_test.go index c4a809240..21c6207ab 100644 --- a/clients/callback_client_test.go +++ b/clients/callback_client_test.go @@ -1,6 +1,7 @@ package clients import ( + "fmt" "io/ioutil" "net/http" "net/http/httptest" @@ -22,7 +23,7 @@ func TestItRetriesOnFailedCallbacks(t *testing.T) { // Check that we got the callback we're expecting body, err := ioutil.ReadAll(r.Body) require.NoError(t, err) - require.JSONEq(t, `{"completion_ratio":1, "status":"completed", "timestamp": 123456789}`, string(body)) + require.JSONEq(t, `{"completion_ratio":1, "status":"success", "timestamp": 123456789}`, string(body)) // Return HTTP error codes the first two times tries += 1 @@ -54,7 +55,7 @@ func TestItEventuallyStopsRetrying(t *testing.T) { // Check that we got the callback we're expecting body, err := ioutil.ReadAll(r.Body) require.NoError(t, err) - require.JSONEq(t, `{"completion_ratio":1, "status":"completed", "timestamp": 123456789}`, string(body)) + require.JSONEq(t, `{"completion_ratio":1, "status":"success", "timestamp": 123456789}`, string(body)) tries += 1 @@ -81,7 +82,7 @@ func TestTranscodeStatusErrorNotifcation(t *testing.T) { // Check that we got the callback we're expecting body, err := ioutil.ReadAll(r.Body) require.NoError(t, err) - require.JSONEq(t, `{"error": "something went wrong", "status":"error", "timestamp": 123456789}`, string(body)) + require.JSONEq(t, `{"completion_ratio": 0, "error": "something went wrong", "status":"error", "timestamp": 123456789}`, string(body)) w.WriteHeader(http.StatusOK) })) @@ -91,3 +92,21 @@ func TestTranscodeStatusErrorNotifcation(t *testing.T) { client := NewCallbackClient() require.NoError(t, client.SendTranscodeStatusError(svr.URL, "something went wrong")) } + +func TestItCalculatesTheOverallCompletionRatioCorrectly(t *testing.T) { + testCases := []struct { + status TranscodeStatus + completionRatio float64 + expectedOverallCompletionRatio float64 + }{ + {TranscodeStatusPreparing, 0.5, 0.2}, // Half complete in the Preparing stage (i.e half way between 0 and 0.4) + {TranscodeStatusPreparingCompleted, 1234, 0.4}, // Preparing Completed should always == 0.4 for now, regardless of what's reported as the stage ratio + {TranscodeStatusTranscoding, 0.5, 0.7}, // Half complete in the Transcoding stage (i.e half way between 0.4 and 1) + {TranscodeStatusCompleted, 5678, 1}, // Completed should always == 1, regardless of what's reported as the stage ratio + } + for _, tc := range testCases { + t.Run(fmt.Sprintf("%f in %s", tc.completionRatio, tc.status), func(t *testing.T) { + require.Equal(t, tc.expectedOverallCompletionRatio, overallCompletionRatio(tc.status, tc.completionRatio)) + }) + } +}