-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: Handle private videos in playlists to prevent UNKNOWN_ERROR in Y…
…outubeAdapter#getFullPlaylist and refactoring (#20) * test: Add tests for utils * chore: Release 1.1.3 🚀 * test: Add tests for result of lib * chore: Update some scripts for test * refactor: Extract entity conversion functions from YouTubeAdapter class - Moved entity conversion functions to a separate module for better code organization and maintainability. - Updated YouTubeAdapter class to use the new conversion module. * test: Add test for EntityConverter * refactor: Simplify entity conversion functions to handle single items * fix: Handle private videos in playlists to prevent UNKNOWN_ERROR in YoutubeAdapter#getFullPlaylist - Closes: #3 - Updated YoutubeAdapter#getFullPlaylist to properly handle private videos in playlists. - Added checks to skip private videos and continue processing the rest of the playlist. - Improved error handling to avoid throwing UNKNOWN_ERROR when private videos are encountered. * chore: Release 1.1.4 🚀
- Loading branch information
Showing
7 changed files
with
451 additions
and
115 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { describe, expect, test } from "vitest"; | ||
|
||
import { Failure, Result, Success } from "."; | ||
|
||
const data = [ | ||
"", | ||
"data", | ||
{}, | ||
{ key: "value" }, | ||
0, | ||
1, | ||
[], | ||
[""], | ||
[9], | ||
[{}], | ||
[{ key: "value" }], | ||
undefined, | ||
null, | ||
]; | ||
const changedData = [ | ||
"", | ||
"changed data", | ||
{}, | ||
{ key: "changed value" }, | ||
0, | ||
1, | ||
[], | ||
[""], | ||
[9], | ||
[{}], | ||
[{ key: "changed value" }], | ||
undefined, | ||
null, | ||
]; | ||
|
||
describe("Result lib", () => { | ||
describe("Success", () => { | ||
test("should return true when isSuccess is called", () => { | ||
const success = new Success("success"); | ||
expect(success.isSuccess()).toBe(true); | ||
}); | ||
test("should return false when isFailure is called", () => { | ||
const success = new Success("success"); | ||
expect(success.isFailure()).toBe(false); | ||
}); | ||
test("should return the data from the constructor when referenced the data property", () => { | ||
for (const d of data) { | ||
const success = new Success(d); | ||
expect(success.data).toBe(d); | ||
} | ||
}); | ||
test("should return the data from the setData method", () => { | ||
for (let i = 0; i < data.length; i++) { | ||
const success = new Success(data[i]); | ||
expect(success.setData(changedData[i])).toBe(changedData[i]); | ||
} | ||
}); | ||
}); | ||
|
||
describe("Failure", () => { | ||
test("should return false when isSuccess is called", () => { | ||
const failure = new Failure("failure"); | ||
expect(failure.isSuccess()).toBe(false); | ||
}); | ||
test("should return true when isFailure is called", () => { | ||
const failure = new Failure("failure"); | ||
expect(failure.isFailure()).toBe(true); | ||
}); | ||
test("should return the data from the constructor when referenced the data property", () => { | ||
for (const d of data) { | ||
const failure = new Failure(d); | ||
expect(failure.data).toBe(d); | ||
} | ||
}); | ||
test("should return the data from the setData method", () => { | ||
for (let i = 0; i < data.length; i++) { | ||
const failure = new Failure(data[i]); | ||
expect(failure.setData(changedData[i])).toBe(changedData[i]); | ||
} | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,235 @@ | ||
import { describe, expect, test } from "vitest"; | ||
|
||
import { Playlist, PlaylistItem } from "@/lib/base-adapter"; | ||
import type { youtube_v3 } from "googleapis"; | ||
import { | ||
convertToPlaylist, | ||
convertToPlaylistItem, | ||
getThumbnailUrlFromAPIData, | ||
} from "./EntityConverter"; | ||
|
||
// The test does not verify the logic for selecting the thumbnail URL from the API response. | ||
// The logic is tested in the `getThumbnailUrlFromAPIData` function test. | ||
describe("convertToPlaylist", () => { | ||
test("should convert the given API response to a Playlist instance", () => { | ||
const data: [youtube_v3.Schema$Playlist, Playlist][] = [ | ||
[ | ||
{ | ||
id: "foo-id", | ||
snippet: { | ||
title: "foo-title", | ||
thumbnails: { default: { url: "foo-default-url" } }, | ||
}, | ||
}, | ||
new Playlist({ | ||
id: "foo-id", | ||
title: "foo-title", | ||
thumbnailUrl: "foo-default-url", | ||
}), | ||
], | ||
[ | ||
{ | ||
id: "foo-id", | ||
snippet: { | ||
title: "foo-title", | ||
thumbnails: { | ||
default: { url: "foo-default-url" }, | ||
high: { | ||
url: "foo-high-url", | ||
}, | ||
}, | ||
}, | ||
}, | ||
new Playlist({ | ||
id: "foo-id", | ||
title: "foo-title", | ||
thumbnailUrl: "foo-high-url", | ||
}), | ||
], | ||
[ | ||
{ | ||
id: "foo-id", | ||
snippet: { | ||
title: "foo-title", | ||
thumbnails: { | ||
maxres: { url: "foo-maxres-url" }, | ||
}, | ||
}, | ||
}, | ||
new Playlist({ | ||
id: "foo-id", | ||
title: "foo-title", | ||
thumbnailUrl: "foo-maxres-url", | ||
}), | ||
], | ||
]; | ||
|
||
for (const [apiData, expected] of data) { | ||
expect(convertToPlaylist(apiData)).toStrictEqual(expected); | ||
} | ||
}); | ||
}); | ||
|
||
describe("convertToPlaylistItem", () => { | ||
test("should convert the given API response to a PlaylistItem instance", () => { | ||
const data: [youtube_v3.Schema$PlaylistItem, PlaylistItem | null][] = [ | ||
[ | ||
{ | ||
id: "foo-id", | ||
snippet: { | ||
title: "foo-title", | ||
position: 1, | ||
videoOwnerChannelTitle: "foo-channel-title", | ||
resourceId: { videoId: "foo-video-id" }, | ||
thumbnails: { default: { url: "foo-default-url" } }, | ||
}, | ||
}, | ||
new PlaylistItem({ | ||
id: "foo-id", | ||
title: "foo-title", | ||
thumbnailUrl: "foo-default-url", | ||
position: 1, | ||
videoId: "foo-video-id", | ||
author: "foo-channel-title", | ||
}), | ||
], | ||
[ | ||
{ | ||
id: "foo-id", | ||
snippet: { | ||
title: "foo-title", | ||
position: 1, | ||
videoOwnerChannelTitle: "foo-channel-title", | ||
resourceId: { videoId: "foo-video-id" }, | ||
thumbnails: { | ||
default: { url: "foo-default-url" }, | ||
high: { url: "foo-high-url" }, | ||
}, | ||
}, | ||
}, | ||
new PlaylistItem({ | ||
id: "foo-id", | ||
title: "foo-title", | ||
thumbnailUrl: "foo-high-url", | ||
position: 1, | ||
videoId: "foo-video-id", | ||
author: "foo-channel-title", | ||
}), | ||
], | ||
[ | ||
{ | ||
id: "foo-id", | ||
snippet: { | ||
title: "foo-title", | ||
position: 1, | ||
videoOwnerChannelTitle: "foo-channel-title", | ||
resourceId: { videoId: "foo-video-id" }, | ||
thumbnails: { | ||
maxres: { url: "foo-maxres-url" }, | ||
}, | ||
}, | ||
}, | ||
new PlaylistItem({ | ||
id: "foo-id", | ||
title: "foo-title", | ||
thumbnailUrl: "foo-maxres-url", | ||
position: 1, | ||
videoId: "foo-video-id", | ||
author: "foo-channel-title", | ||
}), | ||
], | ||
[ | ||
{ | ||
id: "foo-id", | ||
snippet: { | ||
title: "foo-title", | ||
position: 1, | ||
videoOwnerChannelTitle: "foo-channel-title - Topic", | ||
resourceId: { videoId: "foo-video-id" }, | ||
thumbnails: { | ||
default: { url: "foo-default-url" }, | ||
}, | ||
}, | ||
}, | ||
new PlaylistItem({ | ||
id: "foo-id", | ||
title: "foo-title", | ||
thumbnailUrl: "foo-default-url", | ||
position: 1, | ||
videoId: "foo-video-id", | ||
author: "foo-channel-title", | ||
}), | ||
], | ||
[ | ||
{ | ||
kind: "youtube#playlistItem", | ||
etag: "XCet38iaoNCg_iYBc1N8sAKQb50", | ||
id: "UExWTERkNGRESGE3TXVmT2JfMks5YUpHa1FRSjctb3p5Ry4zRjM0MkVCRTg0MkYyQTM0", | ||
snippet: { | ||
publishedAt: "2021-10-30T13:20:19Z", | ||
channelId: "UCQn3V_Cuwq-HVtzpqMIYjHQ", | ||
title: "Private video", | ||
description: "This video is private.", | ||
thumbnails: {}, | ||
channelTitle: "鈴木", | ||
playlistId: "PLVLDd4dDHa7MufOb_2K9aJGkQQJ7-ozyG", | ||
position: 19, | ||
resourceId: { | ||
kind: "youtube#video", | ||
videoId: "WSVTrNkWOoU", | ||
}, | ||
}, | ||
contentDetails: { videoId: "WSVTrNkWOoU" }, | ||
status: { privacyStatus: "private" }, | ||
}, | ||
null, | ||
], | ||
]; | ||
|
||
for (const [apiData, expected] of data) { | ||
expect(convertToPlaylistItem(apiData)).toStrictEqual(expected); | ||
} | ||
}); | ||
}); | ||
|
||
describe("getThumbnailUrlFromAPIData", () => { | ||
test("should return the URL of the highest resolution thumbnail", () => { | ||
const data: [youtube_v3.Schema$ThumbnailDetails, string | undefined][] = | ||
[ | ||
[{}, undefined], | ||
[{ default: { url: "foo-default-url" } }, "foo-default-url"], | ||
[ | ||
{ | ||
default: { url: "foo-default-url" }, | ||
medium: { url: "foo-medium-url" }, | ||
}, | ||
"foo-medium-url", | ||
], | ||
[ | ||
{ | ||
default: { url: "foo-default-url" }, | ||
high: { url: "foo-high-url" }, | ||
}, | ||
"foo-high-url", | ||
], | ||
[ | ||
{ | ||
default: { url: "foo-default-url" }, | ||
standard: { url: "foo-standard-url" }, | ||
}, | ||
"foo-standard-url", | ||
], | ||
[ | ||
{ | ||
default: { url: "foo-default-url" }, | ||
maxres: { url: "foo-maxres-url" }, | ||
}, | ||
"foo-maxres-url", | ||
], | ||
]; | ||
|
||
for (const [apiData, expected] of data) { | ||
expect(getThumbnailUrlFromAPIData(apiData)).toBe(expected); | ||
} | ||
}); | ||
}); |
Oops, something went wrong.