Skip to content

Commit

Permalink
Fallback to autogenerated for random thumbnails instead of local render
Browse files Browse the repository at this point in the history
Should prevent playback issues on unpopular videos

Also fix falling back on certain errors
  • Loading branch information
ajayyy committed Jan 8, 2024
1 parent ba1b4fd commit 2ea05ac
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 12 deletions.
15 changes: 10 additions & 5 deletions src/dataFetching.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { VideoID, getVideoID } from "../maze-utils/src/video";
import { ThumbnailResult, ThumbnailSubmission, fetchVideoMetadata } from "./thumbnails/thumbnailData";
import { ThumbnailSubmission, ThumbnailWithRandomTimeResult, fetchVideoMetadata } from "./thumbnails/thumbnailData";
import { TitleResult, TitleSubmission } from "./titles/titleData";
import { FetchResponse, sendRealRequestToCustomServer } from "../maze-utils/src/background-request-proxy";
import { BrandingLocation, BrandingResult, updateBrandingForVideo } from "./videoBranding/videoBranding";
Expand Down Expand Up @@ -36,14 +36,15 @@ const activeRequests: Record<VideoID, Promise<Record<VideoID, BrandingResult> |
const activeThumbnailCacheRequests: Record<VideoID, ActiveThumbnailCacheRequestInfo> = {};

export async function getVideoThumbnailIncludingUnsubmitted(videoID: VideoID, brandingLocation?: BrandingLocation,
returnRandomTime = true): Promise<ThumbnailResult | null> {
returnRandomTime = true): Promise<ThumbnailWithRandomTimeResult | null> {
const unsubmitted = Config.local!.unsubmitted[videoID]?.thumbnails?.find(t => t.selected);
if (unsubmitted) {
return {
...unsubmitted,
votes: 0,
locked: false,
UUID: generateUserID() as BrandingUUID
UUID: generateUserID() as BrandingUUID,
isRandomTime: false
};
}

Expand All @@ -59,7 +60,8 @@ export async function getVideoThumbnailIncludingUnsubmitted(videoID: VideoID, br
votes: 0,
locked: false,
timestamp: timestamp,
original: false
original: false,
isRandomTime: true
};
} else {
return null;
Expand All @@ -68,7 +70,10 @@ export async function getVideoThumbnailIncludingUnsubmitted(videoID: VideoID, br
return null;
}
} else {
return result;
return {
...result,
isRandomTime: false
};
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/thumbnails/thumbnailData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ export type OriginalThumbnailSubmission = {
export type OriginalThumbnailResult = PartialThumbnailResult & OriginalThumbnailSubmission;

export type ThumbnailResult = CustomThumbnailResult | OriginalThumbnailResult;
export type ThumbnailWithRandomTimeResult = ThumbnailResult & {
isRandomTime: boolean;
};
export type ThumbnailSubmission = CustomThumbnailSubmission | OriginalThumbnailSubmission;

export interface Format {
Expand Down
36 changes: 29 additions & 7 deletions src/thumbnails/thumbnailRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,29 @@ function addStopRenderingCallback(videoID: VideoID, callback: (error?: string) =
thumbnailRendererControls[videoID].push(callback);
}

export class ThumbnailNotInCacheError extends Error {
constructor(videoID: VideoID) {
super(`Thumbnail not found in cache for ${videoID}`);
}
}

export async function renderThumbnail(videoID: VideoID, width: number,
height: number, saveVideo: boolean, timestamp: number): Promise<RenderedThumbnailVideo | null> {
height: number, saveVideo: boolean, timestamp: number, onlyFromThumbnailCache = false): Promise<RenderedThumbnailVideo | null> {

const startTime = performance.now();

if (onlyFromThumbnailCache) {
await waitForThumbnailCache(videoID);
}

let bestVideoData = await findBestVideo(videoID, width, height, timestamp);
if (bestVideoData.renderedThumbnail) {
return bestVideoData.renderedThumbnail;
}

if (onlyFromThumbnailCache) {
throw new ThumbnailNotInCacheError(videoID);
}

await Promise.race([
waitForSpotInRenderQueue(videoID),
Expand Down Expand Up @@ -344,6 +358,7 @@ export async function createThumbnailImageElement(existingElement: HTMLImageElem
image.style.display = "none";

let timestamp = forcedTimestamp as number;
let isRandomTime = false;
if (timestamp === null) {
try {
const thumbnailPromise = getVideoThumbnailIncludingUnsubmitted(videoID, brandingLocation);
Expand All @@ -362,6 +377,7 @@ export async function createThumbnailImageElement(existingElement: HTMLImageElem
const thumbnail = await thumbnailPromise;
if (thumbnail && !thumbnail.original) {
timestamp = thumbnail.timestamp;
isRandomTime = thumbnail.isRandomTime;
} else if (await getThumbnailFallbackOption(videoID) === ThumbnailFallbackOption.AutoGenerated) {
return image;
} else {
Expand Down Expand Up @@ -424,14 +440,20 @@ export async function createThumbnailImageElement(existingElement: HTMLImageElem
ready(image, url, timestamp);
}

const activeRender = activeRenders[videoID] ?? renderThumbnail(videoID, width, height, saveVideo, timestamp);
const activeRender = activeRenders[videoID] ?? renderThumbnail(videoID, width, height, saveVideo, timestamp, isRandomTime);
activeRenders[videoID] = activeRender;

activeRender.then(result).catch(() => {
// Try again with lower resolution
renderThumbnail(videoID, 0, 0, saveVideo, timestamp).then(result).catch(() => {
logError(`Failed to render thumbnail for ${videoID}`);
});
activeRender.then(result).catch((e) => {
if (e instanceof ThumbnailNotInCacheError) {
failure();
} else {
// Try again with lower resolution
renderThumbnail(videoID, 0, 0, saveVideo, timestamp, isRandomTime).then(result).catch((e) => {
log(`Failed to render thumbnail for ${videoID} due to ${e}`);

failure();
});
}
});

return image;
Expand Down

0 comments on commit 2ea05ac

Please sign in to comment.