Skip to content

Commit 7c1abd9

Browse files
committed
Make returned video duration in getBranding.ts consistent
Instead of picking the first segment returned by the db (i.e. possibly random), sort segments by submission time and use the oldest visible segment with a non-zero video duration.
1 parent 709485e commit 7c1abd9

File tree

2 files changed

+36
-9
lines changed

2 files changed

+36
-9
lines changed

src/routes/getBranding.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ export async function getVideoBranding(res: Response, videoID: VideoID, service:
4545
const getSegments = () => db.prepare(
4646
"all",
4747
`SELECT "startTime", "endTime", "category", "videoDuration" FROM "sponsorTimes"
48-
WHERE "votes" > -2 AND "shadowHidden" = 0 AND "hidden" = 0 AND "actionType" = 'skip' AND "videoID" = ? AND "service" = ?`,
48+
WHERE "votes" > -2 AND "shadowHidden" = 0 AND "hidden" = 0 AND "actionType" = 'skip' AND "videoID" = ? AND "service" = ?
49+
ORDER BY "timeSubmitted" ASC`,
4950
[videoID, service],
5051
{ useReplica: true }
5152
) as Promise<BrandingSegmentDBResult[]>;
@@ -110,7 +111,8 @@ export async function getVideoBrandingByHash(videoHashPrefix: VideoIDHash, servi
110111
const getSegments = () => db.prepare(
111112
"all",
112113
`SELECT "videoID", "startTime", "endTime", "category", "videoDuration" FROM "sponsorTimes"
113-
WHERE "votes" > -2 AND "shadowHidden" = 0 AND "hidden" = 0 AND "actionType" = 'skip' AND "hashedVideoID" LIKE ? AND "service" = ?`,
114+
WHERE "votes" > -2 AND "shadowHidden" = 0 AND "hidden" = 0 AND "actionType" = 'skip' AND "hashedVideoID" LIKE ? AND "service" = ?
115+
ORDER BY "timeSubmitted" ASC`,
114116
[`${videoHashPrefix}%`, service],
115117
{ useReplica: true }
116118
) as Promise<BrandingSegmentHashDBResult[]>;
@@ -200,11 +202,13 @@ async function filterAndSortBranding(videoID: VideoID, returnUserID: boolean, fe
200202
}))
201203
.filter((a) => fetchAll || a.votes >= 0 || a.locked) as ThumbnailResult[];
202204

205+
const videoDuration = dbSegments.filter(s => s.videoDuration !== 0)[0]?.videoDuration ?? null;
206+
203207
return {
204208
titles,
205209
thumbnails,
206-
randomTime: findRandomTime(videoID, dbSegments),
207-
videoDuration: dbSegments[0]?.videoDuration ?? null
210+
randomTime: findRandomTime(videoID, dbSegments, videoDuration),
211+
videoDuration: videoDuration,
208212
};
209213
}
210214

@@ -233,7 +237,7 @@ async function shouldKeepSubmission(submissions: BrandingDBSubmission[], type: B
233237
return (_, index) => shouldKeep[index];
234238
}
235239

236-
export function findRandomTime(videoID: VideoID, segments: BrandingSegmentDBResult[]): number {
240+
export function findRandomTime(videoID: VideoID, segments: BrandingSegmentDBResult[], videoDuration: number): number {
237241
let randomTime = SeedRandom.alea(videoID)();
238242

239243
// Don't allow random times past 90% of the video if no endcard
@@ -243,7 +247,7 @@ export function findRandomTime(videoID: VideoID, segments: BrandingSegmentDBResu
243247

244248
if (segments.length === 0) return randomTime;
245249

246-
const videoDuration = segments[0].videoDuration || Math.max(...segments.map((s) => s.endTime));
250+
videoDuration ||= Math.max(...segments.map((s) => s.endTime)); // use highest end time as a fallback here
247251

248252
// There are segments, treat this as a relative time in the chopped up video
249253
const sorted = segments.sort((a, b) => a.startTime - b.startTime);

test/cases/getBranding.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ describe("getBranding", () => {
1313
const videoIDEmpty = "videoID4";
1414
const videoIDRandomTime = "videoID5";
1515
const videoIDUnverified = "videoID6";
16+
const videoIDvidDuration = "videoID7";
1617

1718
const videoID1Hash = getHash(videoID1, 1).slice(0, 4);
1819
const videoID2LockedHash = getHash(videoID2Locked, 1).slice(0, 4);
1920
const videoID2ShadowHideHash = getHash(videoID2ShadowHide, 1).slice(0, 4);
2021
const videoIDEmptyHash = "aaaa";
2122
const videoIDRandomTimeHash = getHash(videoIDRandomTime, 1).slice(0, 4);
2223
const videoIDUnverifiedHash = getHash(videoIDUnverified, 1).slice(0, 4);
24+
const videoIDvidDurationHash = getHash(videoIDUnverified, 1).slice(0, 4);
2325

2426
const endpoint = "/api/branding";
2527
const getBranding = (params: Record<string, any>) => client({
@@ -40,6 +42,7 @@ describe("getBranding", () => {
4042
const thumbnailQuery = `INSERT INTO "thumbnails" ("videoID", "original", "userID", "service", "hashedVideoID", "timeSubmitted", "UUID") VALUES (?, ?, ?, ?, ?, ?, ?)`;
4143
const thumbnailTimestampsQuery = `INSERT INTO "thumbnailTimestamps" ("UUID", "timestamp") VALUES (?, ?)`;
4244
const thumbnailVotesQuery = `INSERT INTO "thumbnailVotes" ("UUID", "votes", "locked", "shadowHidden", "downvotes", "removed") VALUES (?, ?, ?, ?, ?, ?)`;
45+
const segmentQuery = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "category", "actionType", "service", "videoDuration", "hidden", "shadowHidden", "description", "hashedVideoID") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
4346

4447
await Promise.all([
4548
db.prepare("run", titleQuery, [videoID1, "title1", 0, "userID1", Service.YouTube, videoID1Hash, 1, "UUID1"]),
@@ -107,9 +110,8 @@ describe("getBranding", () => {
107110
db.prepare("run", thumbnailVotesQuery, ["UUID32T", 1, 0, 1, 0, 0])
108111
]);
109112

110-
const query = 'INSERT INTO "sponsorTimes" ("videoID", "startTime", "endTime", "votes", "locked", "UUID", "userID", "timeSubmitted", "views", "category", "actionType", "service", "videoDuration", "hidden", "shadowHidden", "description", "hashedVideoID") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
111-
await db.prepare("run", query, [videoIDRandomTime, 1, 11, 1, 0, "uuidbranding1", "testman", 0, 50, "sponsor", "skip", "YouTube", 100, 0, 0, "", videoIDRandomTimeHash]);
112-
await db.prepare("run", query, [videoIDRandomTime, 20, 33, 2, 0, "uuidbranding2", "testman", 0, 50, "intro", "skip", "YouTube", 100, 0, 0, "", videoIDRandomTimeHash]);
113+
await db.prepare("run", segmentQuery, [videoIDRandomTime, 1, 11, 1, 0, "uuidbranding1", "testman", 0, 50, "sponsor", "skip", "YouTube", 100, 0, 0, "", videoIDRandomTimeHash]);
114+
await db.prepare("run", segmentQuery, [videoIDRandomTime, 20, 33, 2, 0, "uuidbranding2", "testman", 0, 50, "intro", "skip", "YouTube", 100, 0, 0, "", videoIDRandomTimeHash]);
113115

114116
await Promise.all([
115117
db.prepare("run", titleQuery, [videoIDUnverified, "title1", 0, "userID1", Service.YouTube, videoIDUnverifiedHash, 1, "UUID-uv-1"]),
@@ -130,6 +132,17 @@ describe("getBranding", () => {
130132
db.prepare("run", thumbnailVotesQuery, ["UUID-uv-2T", 2, 0, 0, 0, 0]),
131133
db.prepare("run", thumbnailVotesQuery, ["UUID-uv-3T", 1, 0, 0, 0, 0])
132134
]);
135+
136+
// Video duration test segments
137+
await Promise.all([
138+
db.prepare("run", segmentQuery, [videoIDvidDuration, 0, 1, 0, 0, "uuidvd1", "testman", 10, 0, "sponsor", "skip", "YouTube", 0, 0, 0, "", videoIDvidDurationHash]), // visible, no vid duration
139+
db.prepare("run", segmentQuery, [videoIDvidDuration, 0, 2, -2, 0, "uuidvd2", "testman", 11, 0, "sponsor", "skip", "YouTube", 10, 0, 0, "", videoIDvidDurationHash]), // downvoted
140+
db.prepare("run", segmentQuery, [videoIDvidDuration, 0, 3, 0, 0, "uuidvd3", "testman", 12, 0, "sponsor", "skip", "YouTube", 10.1, 1, 0, "", videoIDvidDurationHash]), // hidden
141+
db.prepare("run", segmentQuery, [videoIDvidDuration, 0, 4, 0, 0, "uuidvd4", "testman", 13, 0, "sponsor", "skip", "YouTube", 20.1, 0, 1, "", videoIDvidDurationHash]), // shadowhidden
142+
db.prepare("run", segmentQuery, [videoIDvidDuration, 0, 5, 0, 0, "uuidvd5", "testman", 14, 0, "sponsor", "skip", "YouTube", 21.3, 0, 0, "", videoIDvidDurationHash]), // oldest visible w/ duration, should be picked
143+
db.prepare("run", segmentQuery, [videoIDvidDuration, 0, 6, 0, 0, "uuidvd6", "testman", 15, 0, "sponsor", "skip", "YouTube", 21.37, 0, 0, "", videoIDvidDurationHash]), // not the oldest visible
144+
db.prepare("run", segmentQuery, [videoIDvidDuration, 0, 7, -2, 0, "uuidvd7", "testman", 16, 0, "sponsor", "skip", "YouTube", 21.38, 0, 0, "", videoIDvidDurationHash]), // downvoted, not the oldest
145+
]);
133146
});
134147

135148
it("should get top titles and thumbnails", async () => {
@@ -312,6 +325,16 @@ describe("getBranding", () => {
312325
});
313326
});
314327

328+
it("should get the correct video duration", async () => {
329+
const correctDuration = 21.3;
330+
331+
const result1 = await getBranding({ videoID: videoIDvidDuration, fetchAll: true });
332+
const result2 = await getBrandingByHash(videoIDvidDurationHash, { fetchAll: true });
333+
334+
assert.strictEqual(result1.data.videoDuration, correctDuration);
335+
assert.strictEqual(result2.data[videoIDvidDuration].videoDuration, correctDuration);
336+
});
337+
315338
async function checkVideo(videoID: string, videoIDHash: string, fetchAll: boolean, expected: {
316339
titles: TitleResult[],
317340
thumbnails: ThumbnailResult[]

0 commit comments

Comments
 (0)