Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
Kahlis committed Jul 12, 2024
2 parents d89cb7a + c91bbab commit 37a31e0
Show file tree
Hide file tree
Showing 12 changed files with 258 additions and 124 deletions.
9 changes: 6 additions & 3 deletions client/src/components/AddPreview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,7 @@ watch(inputAddPreview, () => {
onInputAddPreviewChange();
});
const highlightedAddPreviewItem = computed(() => {
return _.find(videos.value, { highlight: true });
});
const highlightedAddPreviewItem = ref<Video | undefined>(undefined);
const isAddPreviewInputUrl = computed(() => {
try {
return !!new URL(inputAddPreview.value).host;
Expand All @@ -184,6 +182,7 @@ async function requestAddPreview() {
hasAddPreviewFailed.value = false;
if (res.data.success) {
videos.value = res.data.result;
highlightedAddPreviewItem.value = res.data.highlighted;
console.log(`Got add preview with ${videos.value.length}`);
} else {
throw new Error(res.data.error.message);
Expand Down Expand Up @@ -237,6 +236,7 @@ async function requestAddPreviewExplicit() {
isLoadingAddPreview.value = true;
hasAddPreviewFailed.value = false;
videos.value = [];
highlightedAddPreviewItem.value = undefined;
await requestAddPreview();
}
async function addAllToQueue() {
Expand All @@ -262,16 +262,19 @@ function onInputAddPreviewChange() {
hasAddPreviewFailed.value = false;
if (!inputAddPreview.value || _.trim(inputAddPreview.value).length === 0) {
videos.value = [];
highlightedAddPreviewItem.value = undefined;
return;
}
if (!isAddPreviewInputUrl.value) {
videos.value = [];
highlightedAddPreviewItem.value = undefined;
// Don't send API requests for non URL inputs without the user's explicit input to do so.
// This is to help conserve youtube API quota.
return;
}
isLoadingAddPreview.value = true;
videos.value = [];
highlightedAddPreviewItem.value = undefined;
requestAddPreviewDebounced();
}
function onInputAddPreviewKeyDown(e) {
Expand Down
160 changes: 117 additions & 43 deletions client/src/locales/ru.ts

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions client/tests/e2e/component/AddPreview.cy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineComponent, h } from "vue";
import { useStore } from "../../../src/store";
import AddPreview from "../../../src/components/AddPreview.vue";
import Norifier from "../../../src/components/Notifier.vue";
import Notifier from "../../../src/components/Notifier.vue";

let page = defineComponent({
setup() {
Expand All @@ -10,7 +10,7 @@ let page = defineComponent({
return {};
},
render() {
return h("div", {}, [h(AddPreview), h(Norifier)]);
return h("div", {}, [h(AddPreview), h(Notifier)]);
},
});

Expand Down Expand Up @@ -83,7 +83,7 @@ describe("<AddPreview />", () => {
return {};
},
render() {
return h("div", {}, [h(AddPreview), h(Norifier)]);
return h("div", {}, [h(AddPreview), h(Notifier)]);
},
});
cy.mount(page);
Expand Down
1 change: 1 addition & 0 deletions common/models/rest-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export type OttApiRequestRemoveFromQueue = z.infer<typeof OttApiRequestRemoveFro

export type OttApiResponseAddPreview = {
result: Video[];
highlighted?: Video;
};

export type OttApiRequestVote = z.infer<typeof OttApiRequestVoteSchema>;
Expand Down
2 changes: 1 addition & 1 deletion crates/harness/src/monolith.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ impl MonolithBuilder {
self.add_mock_http_raw(path, parts, body.into())
}

pub fn behavior(mut self, behavior: impl Behavior + Send + 'static) -> Self {
pub fn behavior(mut self, behavior: impl Behavior + 'static) -> Self {
self.behavior = Some(Box::new(behavior));
self
}
Expand Down
6 changes: 3 additions & 3 deletions server/api/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ const addPreview: RequestHandler<
req.query.input.trim(),
conf.get("add_preview.search.provider")
);
const videos = result.videos;

res.setHeader(
"Cache-Control",
Expand All @@ -42,9 +41,10 @@ const addPreview: RequestHandler<

res.json({
success: true,
result: videos,
result: result.videos,
highlighted: result.highlighted,
});
log.info(`Sent add preview response with ${videos.length} items`);
log.info(`Sent add preview response with ${result.videos.length} items`);
} catch (err) {
if (
err.name === "UnsupportedServiceException" ||
Expand Down
68 changes: 47 additions & 21 deletions server/infoextractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { conf } from "./ott-config";
import PeertubeAdapter from "./services/peertube";
import PlutoAdapter from "./services/pluto";
import DashVideoAdapter from "./services/dash";
import { Ls } from "dayjs";

const log = getLogger("infoextract");

Expand Down Expand Up @@ -355,30 +356,44 @@ export default {

const fetchResults = await adapter.resolveURL(query);
const resolvedResults: VideoId[] = [];
for (let video of fetchResults) {
if ("url" in video) {
try {
const adapter = this.getServiceAdapterForURL(video.url);
if (!adapter) {
if (Array.isArray(fetchResults)) {
for (let video of fetchResults) {
if ("url" in video) {
try {
const adapter = this.getServiceAdapterForURL(video.url);
if (!adapter) {
continue;
}
if (adapter.isCollectionURL(video.url)) {
continue;
}
resolvedResults.push({
service: adapter.serviceId,
id: adapter.getVideoId(video.url),
});
} catch (e) {
log.warn(`Failed to resolve video URL ${video.url}: ${e.message}`);
continue;
}
if (adapter.isCollectionURL(video.url)) {
continue;
}
resolvedResults.push({
service: adapter.serviceId,
id: adapter.getVideoId(video.url),
});
} catch (e) {
log.warn(`Failed to resolve video URL ${video.url}: ${e.message}`);
continue;
} else {
resolvedResults.push(video);
}
} else {
resolvedResults.push(video);
}
const completeResults = await this.getManyVideoInfo(resolvedResults);
return new AddPreview(completeResults, cacheDuration);
} else {
const videos = fetchResults.videos;
const completeResults: BulkVideoResult = {
videos: await this.getManyVideoInfo(videos),
highlighted: fetchResults.highlighted
? await this.getVideoInfo(
fetchResults.highlighted.service,
fetchResults.highlighted.id
)
: undefined,
};
return new AddPreview(completeResults, cacheDuration);
}
const completeResults = await this.getManyVideoInfo(resolvedResults);
results.push(...completeResults);
} else {
if (query.length < ADD_PREVIEW_SEARCH_MIN_LENGTH) {
throw new InvalidAddPreviewInputException(ADD_PREVIEW_SEARCH_MIN_LENGTH);
Expand Down Expand Up @@ -424,13 +439,24 @@ export class AddPreview {
videos: Video[];
/** The number of seconds to allow downstream caches to cache the response. Affects caching HTTP headers. */
cacheDuration: number;
highlighted?: Video;

constructor(videos: Video[], cacheDuration: number) {
this.videos = videos;
constructor(videos: Video[] | BulkVideoResult, cacheDuration: number) {
if (Array.isArray(videos)) {
this.videos = videos;
} else {
this.videos = videos.videos;
this.highlighted = videos.highlighted;
}
this.cacheDuration = cacheDuration;
}
}

export interface BulkVideoResult {
videos: Video[];
highlighted?: Video;
}

const counterAddPreviewsRequested = new Counter({
name: "ott_infoextractor_add_previews_requested",
help: "The number of add previews that have been requested",
Expand Down
2 changes: 1 addition & 1 deletion server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"uuid": "^9.0.0",
"validator": "^13.7.0",
"winston": "^3.10.0",
"ws": "^7.4.6",
"ws": "^7.5.10",
"zod": "^3.22.4",
"zod-validation-error": "^3.0.2"
},
Expand Down
3 changes: 2 additions & 1 deletion server/serviceadapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { Video, VideoId, VideoMetadata, VideoService } from "ott-common/models/video";
import { IncompleteServiceAdapterException } from "./exceptions";
import { getLogger } from "./logger";
import { BulkVideoResult } from "./infoextractor";

const log = getLogger("serviceadapter");
export interface VideoRequest {
Expand Down Expand Up @@ -92,7 +93,7 @@ export class ServiceAdapter {
async resolveURL(
url: string,
properties?: (keyof VideoMetadata)[]
): Promise<(Video | { url: string })[]> {
): Promise<(Video | { url: string })[] | BulkVideoResult> {
throw new IncompleteServiceAdapterException(
`Service ${this.serviceId} does not implement method resolveURL`
);
Expand Down
43 changes: 26 additions & 17 deletions server/services/youtube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import storage from "../storage";
import { OttException } from "ott-common/exceptions";
import { conf } from "../ott-config";
import { parseIso8601Duration } from "./parsing/iso8601";
import { BulkVideoResult } from "server/infoextractor";

const log = getLogger("youtube");

Expand Down Expand Up @@ -203,7 +204,10 @@ export default class YouTubeAdapter extends ServiceAdapter {
}
}

async resolveURL(link: string, onlyProperties?: (keyof VideoMetadata)[]): Promise<Video[]> {
async resolveURL(
link: string,
onlyProperties?: (keyof VideoMetadata)[]
): Promise<BulkVideoResult> {
log.debug(`resolveURL: ${link}, ${onlyProperties ? onlyProperties.toString() : ""}`);
const url = new URL(link);

Expand All @@ -215,10 +219,12 @@ export default class YouTubeAdapter extends ServiceAdapter {
url.pathname.startsWith("/user/") ||
url.pathname.startsWith("/@")
) {
return this.fetchChannelVideos(this.getChannelId(url));
const videos = await this.fetchChannelVideos(this.getChannelId(url));
return { videos };
} else if (url.pathname === "/playlist") {
if (qPlaylist) {
return this.fetchPlaylistVideos(qPlaylist);
const videos = await this.fetchPlaylistVideos(qPlaylist);
return { videos };
} else {
throw new BadApiArgumentException("input", "Link is missing playlist ID");
}
Expand All @@ -228,10 +234,14 @@ export default class YouTubeAdapter extends ServiceAdapter {
return await this.fetchVideoWithPlaylist(this.getVideoId(link), qPlaylist);
} catch {
log.debug("Falling back to fetching video without playlist");
return [await this.fetchVideoInfo(this.getVideoId(link), onlyProperties)];
return {
videos: [await this.fetchVideoInfo(this.getVideoId(link), onlyProperties)],
};
}
} else {
return [await this.fetchVideoInfo(this.getVideoId(link), onlyProperties)];
return {
videos: [await this.fetchVideoInfo(this.getVideoId(link), onlyProperties)],
};
}
}
}
Expand Down Expand Up @@ -410,23 +420,22 @@ export default class YouTubeAdapter extends ServiceAdapter {
}
}

async fetchVideoWithPlaylist(videoId: string, playlistId: string): Promise<Video[]> {
async fetchVideoWithPlaylist(videoId: string, playlistId: string): Promise<BulkVideoResult> {
const playlist = await this.fetchPlaylistVideos(playlistId);
let highlighted = false;
playlist.forEach(video => {
if (video.id === videoId) {
highlighted = true;
video.highlight = true;
}
});
let highlighted = playlist.find(video => video.id === videoId);

if (!highlighted) {
const video = await this.fetchVideoInfo(videoId);
video.highlight = true;
playlist.unshift(video);
try {
highlighted = await this.fetchVideoInfo(videoId);
} catch (err) {
log.warn(`Failed to fetch highlighted video from playlist, skipping: ${err}`);
}
}

return playlist;
return {
videos: playlist,
highlighted,
};
}

async videoApiRequest(
Expand Down
Loading

0 comments on commit 37a31e0

Please sign in to comment.