All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m3s
225 lines
7.6 KiB
Diff
225 lines
7.6 KiB
Diff
From 9d58a8327eb2dfc7eb3db06f41a6778570d28fa3 Mon Sep 17 00:00:00 2001
|
||
From: Fijxu <fijxu@nadeko.net>
|
||
Date: Mon, 14 Apr 2025 01:24:57 -0400
|
||
Subject: [PATCH 12/17] metrics: track unidentified innertube errors
|
||
|
||
---
|
||
src/lib/helpers/metrics.ts | 142 +++++++++++++++++++++++++++++--------
|
||
1 file changed, 111 insertions(+), 31 deletions(-)
|
||
|
||
diff --git a/src/lib/helpers/metrics.ts b/src/lib/helpers/metrics.ts
|
||
index 5dee540..2464fe7 100644
|
||
--- a/src/lib/helpers/metrics.ts
|
||
+++ b/src/lib/helpers/metrics.ts
|
||
@@ -5,11 +5,16 @@ export class Metrics {
|
||
private METRICS_PREFIX = "invidious_companion_";
|
||
public register = new Registry();
|
||
|
||
- public createCounter(name: string, help?: string): Counter {
|
||
+ public createCounter(
|
||
+ name: string,
|
||
+ help?: string,
|
||
+ labels?: string[],
|
||
+ ): Counter {
|
||
return new Counter({
|
||
name: `${this.METRICS_PREFIX}${name}`,
|
||
help: help || "No help has been provided for this metric",
|
||
registers: [this.register],
|
||
+ labelNames: Array.isArray(labels) ? labels : [],
|
||
});
|
||
}
|
||
|
||
@@ -26,6 +31,7 @@ export class Metrics {
|
||
private innertubeErrorStatusUnknown = this.createCounter(
|
||
"innertube_error_status_unknown_total",
|
||
"Number of times that an unknown status has been returned by Innertube API",
|
||
+ ["error"],
|
||
);
|
||
|
||
private innertubeErrorReasonSignIn = this.createCounter(
|
||
@@ -41,11 +47,13 @@ export class Metrics {
|
||
private innertubeErrorReasonUnknown = this.createCounter(
|
||
"innertube_error_reason_unknown_total",
|
||
"Number of times that an unknown reason has been returned by the Innertube API",
|
||
+ ["error"],
|
||
);
|
||
|
||
private innertubeErrorSubreasonUnknown = this.createCounter(
|
||
"innertube_error_subreason_unknown_total",
|
||
"Number of times that an unknown subreason has been returned by the Innertube API",
|
||
+ ["error"],
|
||
);
|
||
|
||
public innertubeSuccessfulRequest = this.createCounter(
|
||
@@ -66,26 +74,70 @@ export class Metrics {
|
||
private checkStatus(videoData: IRawResponse) {
|
||
const status = videoData.playabilityStatus?.status;
|
||
|
||
- return {
|
||
- unplayable: status ===
|
||
- "UNPLAYABLE",
|
||
- contentCheckRequired: status ===
|
||
- "CONTENT_CHECK_REQUIRED",
|
||
- loginRequired: status === "LOGIN_REQUIRED",
|
||
+ interface Error {
|
||
+ unplayable: boolean;
|
||
+ contentCheckRequired: boolean;
|
||
+ loginRequired: boolean;
|
||
+ unknown: string | undefined;
|
||
+ }
|
||
+
|
||
+ const error: Error = {
|
||
+ unplayable: false,
|
||
+ contentCheckRequired: false,
|
||
+ loginRequired: false,
|
||
+ unknown: undefined,
|
||
};
|
||
+
|
||
+ switch (status) {
|
||
+ case "UNPLAYABLE":
|
||
+ error.unplayable = true;
|
||
+ return error;
|
||
+ // Sensitive content videos
|
||
+ // Example video id: `VuSU7PcEKpU`
|
||
+ case "CONTENT_CHECK_REQUIRED":
|
||
+ error.contentCheckRequired = true;
|
||
+ return error;
|
||
+ case "LOGIN_REQUIRED":
|
||
+ error.loginRequired = true;
|
||
+ return error;
|
||
+ default:
|
||
+ error.unknown = status;
|
||
+ return error;
|
||
+ }
|
||
}
|
||
|
||
private checkReason(videoData: IRawResponse) {
|
||
- const reason = videoData.playabilityStatus?.reason;
|
||
-
|
||
- return {
|
||
- signInToConfirmAge: reason?.includes(
|
||
- "Sign in to confirm your age",
|
||
- ),
|
||
- SignInToConfirmBot: reason?.includes(
|
||
- "Sign in to confirm you’re not a bot",
|
||
- ),
|
||
+ // On specific status like `CONTENT_CHECK_REQUIRED`, the reason is
|
||
+ // contained inside `errorScreen`, just like how we check subReason.
|
||
+ const reason = videoData.playabilityStatus?.reason ||
|
||
+ videoData.playabilityStatus?.errorScreen
|
||
+ ?.playerErrorMessageRenderer
|
||
+ ?.reason?.simpleText;
|
||
+
|
||
+ interface Error {
|
||
+ signInToConfirmAge: boolean;
|
||
+ SignInToConfirmBot: boolean;
|
||
+ unknown: string | undefined;
|
||
+ }
|
||
+
|
||
+ const error: Error = {
|
||
+ signInToConfirmAge: false,
|
||
+ SignInToConfirmBot: false,
|
||
+ unknown: undefined,
|
||
};
|
||
+
|
||
+ switch (true) {
|
||
+ case reason?.includes("Sign in to confirm you’re not a bot"):
|
||
+ error.SignInToConfirmBot = true;
|
||
+ return error;
|
||
+ // Age restricted videos
|
||
+ case reason?.includes("Sign in to confirm your age"):
|
||
+ error.signInToConfirmAge = true;
|
||
+ return error;
|
||
+ default:
|
||
+ error.unknown = reason;
|
||
+ return error;
|
||
+ }
|
||
}
|
||
|
||
private checkSubreason(videoData: IRawResponse) {
|
||
@@ -93,39 +145,67 @@ export class Metrics {
|
||
?.playerErrorMessageRenderer
|
||
?.subreason?.runs?.[0]?.text;
|
||
|
||
- return {
|
||
- thisHelpsProtectCommunity: subReason?.includes(
|
||
- "This helps protect our community",
|
||
- ),
|
||
+ interface Error {
|
||
+ thisHelpsProtectCommunity: boolean;
|
||
+ unknown: string | undefined;
|
||
+ }
|
||
+
|
||
+ const error: Error = {
|
||
+ thisHelpsProtectCommunity: false,
|
||
+ unknown: undefined,
|
||
};
|
||
+
|
||
+ switch (true) {
|
||
+ case subReason?.includes("This helps protect our community"):
|
||
+ error.thisHelpsProtectCommunity = true;
|
||
+ return error;
|
||
+ default:
|
||
+ error.unknown = subReason;
|
||
+ return error;
|
||
+ }
|
||
}
|
||
|
||
public checkInnertubeResponse(videoData: IRawResponse) {
|
||
this.innertubeFailedRequest.inc();
|
||
- const status = this.checkStatus(videoData);
|
||
|
||
+ const status = this.checkStatus(videoData);
|
||
if (status.contentCheckRequired || status.unplayable) return;
|
||
|
||
+ if (status?.unknown) {
|
||
+ this.innertubeErrorStatusUnknown.labels({
|
||
+ error: status.unknown,
|
||
+ }).inc();
|
||
+ }
|
||
+
|
||
+ const reason = this.checkReason(videoData);
|
||
+ if (reason.signInToConfirmAge) return;
|
||
+
|
||
+ if (reason.unknown) {
|
||
+ this.innertubeErrorReasonUnknown.labels({
|
||
+ error: reason.unknown,
|
||
+ }).inc();
|
||
+ }
|
||
+
|
||
+ // On specific `playabilityStatus.status` like `CONTENT_CHECK_REQUIRED`,
|
||
+ // `subReason` doesn't come with a `playabilityStatus.reason`
|
||
+ // key. So we need to check this separately from `reason`
|
||
+ const subReason = this.checkSubreason(videoData);
|
||
+ if (subReason.unknown) {
|
||
+ this.innertubeErrorSubreasonUnknown.labels({
|
||
+ error: subReason.unknown,
|
||
+ }).inc();
|
||
+ }
|
||
+
|
||
if (status.loginRequired) {
|
||
this.innertubeErrorStatusLoginRequired.inc();
|
||
- const reason = this.checkReason(videoData);
|
||
-
|
||
- if (reason.signInToConfirmAge) return;
|
||
|
||
if (reason.SignInToConfirmBot) {
|
||
this.innertubeErrorReasonSignIn.inc();
|
||
- const subReason = this.checkSubreason(videoData);
|
||
|
||
if (subReason.thisHelpsProtectCommunity) {
|
||
this.innertubeErrorSubreasonProtectCommunity.inc();
|
||
- } else {
|
||
- this.innertubeErrorSubreasonUnknown.inc();
|
||
}
|
||
- } else {
|
||
- this.innertubeErrorReasonUnknown.inc();
|
||
}
|
||
- } else {
|
||
- this.innertubeErrorStatusUnknown.inc();
|
||
}
|
||
}
|
||
}
|
||
--
|
||
2.49.0
|
||
|