Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add continuation for search results #2838

Merged
merged 5 commits into from
Jun 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions client/composables/api/search.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ApiDto } from '@viewtube/shared';
import { type LocationQuery } from 'vue-router';
import type { VTSearchDto } from '../../../server/src/mapper/dto/search/vt-search.dto';

export type FilterType = { filterValue: any; filterType?: any; filterName: any };

Expand All @@ -21,7 +21,7 @@ export const useGetSearchResult = () => {
`search`,
async () => {
const { apiUrl } = useApiUrl();
const searchResponse = await vtFetch<VTSearchDto>(`${apiUrl.value}search`, {
const searchResponse = await vtFetch<ApiDto<'VTSearchDto'>>(`${apiUrl.value}search`, {
query: { q: searchQuery.value, filters: searchFilters.value }
});

Expand All @@ -33,7 +33,7 @@ export const useGetSearchResult = () => {
);
};

const getSearchFilters = (query: LocationQuery) => {
export const getSearchFilters = (query: LocationQuery) => {
const searchParams = new URLSearchParams(query as Record<string, string>);
const filters: Record<string, string> = {};

Expand Down
3 changes: 1 addition & 2 deletions client/composables/proxyUrls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ export const useProxyUrls = () => {

const origin = window?.location?.origin ?? '';

const streamProxy = `${clientApiUrl.value}proxy/stream?url=`;
const streamProxy = `${clientApiUrl.value}proxy/stream?originUrl=${origin}&url=`;
return {
imgProxy: `${clientApiUrl.value}proxy/image?url=`,
streamProxy,
m3u8Proxy: `${clientApiUrl.value}proxy/m3u8?proxyUrl=${origin}${streamProxy}&url=`,
videoPlaybackProxy: `${clientApiUrl.value}videoplayback`,
textProxy: `${clientApiUrl.value}proxy/text?url=`
};
Expand Down
1 change: 0 additions & 1 deletion client/composables/videoplayer/videoSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export const useVideoSource = (video: Ref<ApiDto<'VTVideoInfoDto'>>) => {
sourceType = VideoSourceType.HLS;
source = video.value.hlsManifestUrl;
} else if (video.value.dashManifest) {
const googlevideoRegex = /https:\/\/.*?.googlevideo\.com/gi;
const manifest = video.value.dashManifest.replace(googlevideoRegex, videoPlaybackProxy);
sourceType = VideoSourceType.DASH;
source = 'data:application/dash+xml;charset=utf-8;base64,' + btoa(manifest);
Expand Down
103 changes: 56 additions & 47 deletions client/pages/results.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
import RelatedSearches from '@/components/search/RelatedSearches.vue';
import Spinner from '@/components/Spinner.vue';
import Filters from '@/components/search/Filters.vue';
import SeparatorSmall from '@/components/list/SeparatorSmall.vue';
import BadgeButton from '@/components/buttons/BadgeButton.vue';
import { useMessagesStore } from '@/store/messages';
import { useSettingsStore } from '@/store/settings';
import type { ApiDto } from '@viewtube/shared';

const VideoEntry = resolveComponent('ListVideoEntry');
const PlaylistEntry = resolveComponent('ListPlaylistEntry');
Expand All @@ -14,6 +17,8 @@ const Shelf = resolveComponent('SearchShelf');
const route = useRoute();
const messagesStore = useMessagesStore();
const settingsStore = useSettingsStore();
const { vtFetch } = useVtFetch();
const { apiUrl } = useApiUrl();

const searchQuery = computed(() => {
const searchParams = new URLSearchParams(route.query as Record<string, string>);
Expand All @@ -33,16 +38,16 @@ onMounted(() => {
}
});

// const additionalResultItems = ref([]);
// const searchContinuationData = ref<any>(searchData.value?.searchResults.continuation);
const additionalResultItems = ref([]);
const searchContinuationData = ref<any>(searchData.value?.continuation);

// watch(
// () => searchData.value,
// newData => {
// // additionalResultItems.value = [];
// searchContinuationData.value = newData?.searchResults.continuation;
// }
// );
watch(
() => searchData.value,
newData => {
additionalResultItems.value = [];
searchContinuationData.value = newData?.continuation;
}
);

watch(error, newValue => {
if (newValue) {
Expand Down Expand Up @@ -76,42 +81,46 @@ const getListEntryType = (type: string) => {
}
};

// const loadMoreVideos = async () => {
// moreVideosLoading.value = true;
// page.value += 1;
const moreVideosLoading = ref(false);
const page = ref(1);

const loadMoreVideos = async () => {
moreVideosLoading.value = true;
page.value += 1;

// if (searchData.value?.searchResults && searchContinuationData.value) {
// try {
// const searchContinuation = await vtFetch(
// `${apiUrl.value}search/continuation`,
// {
// method: 'POST',
// body: {
// continuationData: searchContinuationData.value
// }
// }
// );
if (searchData.value && searchContinuationData.value) {
try {
const searchContinuation = await vtFetch<ApiDto<'VTSearchDto'>>(`${apiUrl.value}search`, {
query: {
q: searchQuery.value,
filters: getSearchFilters(route.query),
continuationString: searchContinuationData.value
}
});

// if (searchContinuation) {
// additionalResultItems.value = [...additionalResultItems.value, ...searchContinuation.items];
// searchContinuationData.value = searchContinuation.continuation;
// }
// } catch (error) {
// messagesStore.createMessage({
// type: 'error',
// title: 'Unable to load more results',
// message: 'Try again or use a different search term for more results'
// });
// }
// } else {
// messagesStore.createMessage({
// type: 'error',
// title: 'Unable to load more results',
// message: 'Use a different search term for more results'
// });
// }
// moreVideosLoading.value = false;
// };
if (searchContinuation) {
additionalResultItems.value = [
...additionalResultItems.value,
...searchContinuation.results
];
searchContinuationData.value = searchContinuation.continuation;
}
} catch (error) {
messagesStore.createMessage({
type: 'error',
title: 'Unable to load more results',
message: 'Try again or use a different search term for more results'
});
}
} else {
messagesStore.createMessage({
type: 'error',
title: 'Unable to load more results',
message: 'Use a different search term for more results'
});
}
moreVideosLoading.value = false;
};
</script>

<template>
Expand Down Expand Up @@ -144,7 +153,7 @@ const getListEntryType = (type: string) => {
:lazy="true"
/>
</div>
<!-- <SeparatorSmall v-if="!pending && additionalResultItems.length > 0"
<SeparatorSmall v-if="!pending && additionalResultItems.length > 0"
>More results</SeparatorSmall
>
<div v-if="!pending && additionalResultItems.length > 0" class="search-videos-container">
Expand All @@ -161,12 +170,12 @@ const getListEntryType = (type: string) => {
:lazy="true"
/>
</div>
<div class="show-more-btn-container">
<div v-if="searchContinuationData" class="show-more-btn-container">
<BadgeButton :click="loadMoreVideos" :loading="moreVideosLoading">
<VTIcon name="mdi:reload" />
<p>Show more</p>
</BadgeButton>
</div> -->
</div>
</div>
</div>
</template>
Expand Down Expand Up @@ -273,7 +282,7 @@ const getListEntryType = (type: string) => {
.show-more-btn-container {
display: flex;
width: 100%;
padding: 0 0 20px 0;
padding: 20px 0 20px 0;

> a {
margin: 0 auto;
Expand Down
1 change: 1 addition & 0 deletions client/utils/googlevideoRegex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const googlevideoRegex = /https:\/\/.*?.googlevideo\.com/gi;
6 changes: 5 additions & 1 deletion client/utils/videoplayer/adapters/hlsAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,11 @@ export const hlsAdapter = async ({
});

playerInstance?.on(Hls.Events.ERROR, (event, data) => {
if (!['fragParsingError', 'bufferStalledError'].includes(data.details)) {
if (
!['fragParsingError', 'bufferStalledError', 'levelLoadError', 'fragLoadError'].includes(
data.details
)
) {
console.log('error', event, data);
videoState.playerError = {
message: data.details,
Expand Down
6 changes: 3 additions & 3 deletions client/utils/videoplayer/adapters/rxPlayerAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,14 @@ export const rxPlayerAdapter = async ({
}: RxPlayerAdapterOptions) => {
// RxPlayer.addFeatures([DASH_WASM]);

const RxPlayer = await import('rx-player').then(module => module.default);
const { DASH_WASM } = await import('rx-player/features');
const RxPlayer = await import('rx-player/minimal').then(module => module.default);
const { DASH, DASH_WASM } = await import('rx-player/features');
const { EMBEDDED_DASH_WASM, EMBEDDED_WORKER } = await import(
'rx-player/experimental/features/embeds'
);

try {
RxPlayer.addFeatures([DASH]);
await DASH_WASM.initialize({ wasmUrl: EMBEDDED_DASH_WASM }).catch(() => {});
} catch {
// Ignore
Expand Down Expand Up @@ -257,7 +258,6 @@ export const rxPlayerAdapter = async ({
};

const mapAudioTracks = (audioTracks: IAvailableAudioTrack[]): AudioTrack[] => {
console.log(audioTracks);
return audioTracks
.filter(audioTrack => {
if (videoState.languageList.length <= 1) return true;
Expand Down
4 changes: 3 additions & 1 deletion server/src/common/innertube/innertube.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import path from 'path';
import { Innertube, InnertubeConfig, UniversalCache } from 'youtubei.js';
import { Innertube, InnertubeConfig, Log, UniversalCache } from 'youtubei.js';
import { innertubeFetch } from './innertubeFetch';

Log.setLevel(Log.Level.ERROR);

let cacheDirectory = './cache';
if (process.env.VIEWTUBE_DATA_DIRECTORY) {
cacheDirectory = path.join(process.env.VIEWTUBE_DATA_DIRECTORY, 'cache');
Expand Down
30 changes: 1 addition & 29 deletions server/src/core/channels/channels.controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CacheInterceptor, CacheTTL } from '@nestjs/cache-manager';
import { Controller, Get, Header, Param, Query, Res, UseInterceptors } from '@nestjs/common';
import { ApiResponse, ApiTags } from '@nestjs/swagger';
import { ApiTags } from '@nestjs/swagger';
import { FastifyReply } from 'fastify';
import { ChannelsService } from './channels.service';
import { ChannelInfoErrorDto } from './dto/channel-info-error.dto';
Expand Down Expand Up @@ -38,8 +38,6 @@ export class ChannelsController {

@Header('Cache-Control', 'public, max-age=3600')
@Get(':id')
@ApiResponse({ status: 404 })
@ApiResponse({ status: 500 })
@UseInterceptors(CacheInterceptor)
@CacheTTL(3600000)
getChannelInfo(@Param('id') channelId: string): Promise<ChannelInfoDto> {
Expand All @@ -48,8 +46,6 @@ export class ChannelsController {

@Header('Cache-Control', 'public, max-age=3600')
@Get(':id/home')
@ApiResponse({ status: 404 })
@ApiResponse({ status: 500 })
@UseInterceptors(CacheInterceptor)
@CacheTTL(3600000)
getChannelHome(@Param('id') channelId: string): Promise<ChannelHomeDto> {
Expand All @@ -58,8 +54,6 @@ export class ChannelsController {

@Header('Cache-Control', 'public, max-age=3600')
@Get(':id/videos')
@ApiResponse({ status: 404 })
@ApiResponse({ status: 500 })
@UseInterceptors(CacheInterceptor)
@CacheTTL(3600000)
getChannelVideos(
Expand All @@ -71,8 +65,6 @@ export class ChannelsController {

@Header('Cache-Control', 'public, max-age=3600')
@Get('videos/continuation')
@ApiResponse({ status: 404 })
@ApiResponse({ status: 500 })
@UseInterceptors(CacheInterceptor)
@CacheTTL(3600000)
getChannelVideosContinuation(
Expand All @@ -85,8 +77,6 @@ export class ChannelsController {

@Header('Cache-Control', 'public, max-age=3600')
@Get(':id/shorts')
@ApiResponse({ status: 404 })
@ApiResponse({ status: 500 })
@UseInterceptors(CacheInterceptor)
@CacheTTL(3600000)
getChannelShorts(
Expand All @@ -98,8 +88,6 @@ export class ChannelsController {

@Header('Cache-Control', 'public, max-age=3600')
@Get(':id/livestreams')
@ApiResponse({ status: 404 })
@ApiResponse({ status: 500 })
@UseInterceptors(CacheInterceptor)
@CacheTTL(3600000)
getChannelLivestreams(
Expand All @@ -111,8 +99,6 @@ export class ChannelsController {

@Header('Cache-Control', 'public, max-age=3600')
@Get(':id/playlists')
@ApiResponse({ status: 404 })
@ApiResponse({ status: 500 })
@UseInterceptors(CacheInterceptor)
@CacheTTL(3600000)
getChannelPlaylists(@Param('id') channelId: string): Promise<ChannelPlaylistsDto> {
Expand All @@ -121,8 +107,6 @@ export class ChannelsController {

@Header('Cache-Control', 'public, max-age=3600')
@Get('playlists/continuation')
@ApiResponse({ status: 404 })
@ApiResponse({ status: 500 })
@UseInterceptors(CacheInterceptor)
@CacheTTL(3600000)
getChannelPlaylistsContinuation(
Expand All @@ -133,8 +117,6 @@ export class ChannelsController {

@Header('Cache-Control', 'public, max-age=3600')
@Get(':id/search')
@ApiResponse({ status: 404 })
@ApiResponse({ status: 500 })
@UseInterceptors(CacheInterceptor)
@CacheTTL(3600000)
searchChannel(
Expand All @@ -146,8 +128,6 @@ export class ChannelsController {

@Header('Cache-Control', 'public, max-age=3600')
@Get('search/continuation')
@ApiResponse({ status: 404 })
@ApiResponse({ status: 500 })
@UseInterceptors(CacheInterceptor)
@CacheTTL(3600000)
searchChannelContinuation(
Expand All @@ -158,8 +138,6 @@ export class ChannelsController {

@Header('Cache-Control', 'public, max-age=3600')
@Get('relatedchannels/continuation')
@ApiResponse({ status: 404 })
@ApiResponse({ status: 500 })
@UseInterceptors(CacheInterceptor)
@CacheTTL(3600000)
getRelatedChannelsContinuation(
Expand All @@ -170,8 +148,6 @@ export class ChannelsController {

@Header('Cache-Control', 'public, max-age=3600')
@Get(':id/communityposts')
@ApiResponse({ status: 404 })
@ApiResponse({ status: 500 })
@UseInterceptors(CacheInterceptor)
@CacheTTL(3600000)
getChannelCommunityPosts(@Param('id') channelId: string): Promise<ChannelCommunityPostsDto> {
Expand All @@ -180,8 +156,6 @@ export class ChannelsController {

@Header('Cache-Control', 'public, max-age=3600')
@Get('communityposts/continuation')
@ApiResponse({ status: 404 })
@ApiResponse({ status: 500 })
@UseInterceptors(CacheInterceptor)
@CacheTTL(3600000)
getChannelCommunityPostsContinuation(
Expand All @@ -193,8 +167,6 @@ export class ChannelsController {

@Header('Cache-Control', 'public, max-age=3600')
@Get(':id/stats')
@ApiResponse({ status: 404 })
@ApiResponse({ status: 500 })
@UseInterceptors(CacheInterceptor)
@CacheTTL(3600000)
getChannelStats(@Param('id') channelId: string): Promise<ChannelStatsDto> {
Expand Down
Loading
Loading