itube-specs 0.0.962 → 0.0.964
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/card/model.vue +4 -3
- package/composables/fetch/use-fetch-vector-related-videos.ts +31 -0
- package/package.json +1 -1
- package/pages/videos/[slug].vue +5 -1
- package/runtime/constants/async-data.ts +1 -0
- package/server/api/videos/get-vector-related-videos-by-id/[id].get.ts +33 -0
- package/server/api/videos/get-vector-related-videos-by-url/[url].get.ts +31 -0
- package/services/api/videos.service.ts +47 -0
|
@@ -145,8 +145,9 @@ function getValue(item: { title?: string; value?: string }) {
|
|
|
145
145
|
object-fit: cover;
|
|
146
146
|
object-position: center top;
|
|
147
147
|
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
/* Наезд картинки вдвое медленнее остальных переходов карточки — так в макете. */
|
|
149
|
+
@media (hover: hover) {
|
|
150
|
+
transition: .3s var(--ease-in-out) transform;
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
153
|
|
|
@@ -179,7 +180,7 @@ function getValue(item: { title?: string; value?: string }) {
|
|
|
179
180
|
overflow: hidden;
|
|
180
181
|
text-transform: capitalize;
|
|
181
182
|
|
|
182
|
-
@
|
|
183
|
+
@media (hover: hover) {
|
|
183
184
|
transition: var(--transition) color;
|
|
184
185
|
}
|
|
185
186
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Language } from '../../runtime';
|
|
2
|
+
import { AsyncData } from '../../runtime';
|
|
3
|
+
import type { IVideoCard, PaginatedResponse } from '../../types';
|
|
4
|
+
|
|
5
|
+
export const useFetchVectorRelatedVideos = async (
|
|
6
|
+
apiMethod: (...args: any[]) => Promise<PaginatedResponse<IVideoCard>>
|
|
7
|
+
) => {
|
|
8
|
+
const lang = useLang() as Language;
|
|
9
|
+
const mobile = !!useState<boolean>('isMobile').value;
|
|
10
|
+
const limit = mobile ? 6 : 14;
|
|
11
|
+
const slug = useSlug();
|
|
12
|
+
const key = AsyncData.VectorRelatedVideos;
|
|
13
|
+
const stateKey = computed(() => `data-${key}-${slug.value}-${lang}`);
|
|
14
|
+
|
|
15
|
+
const { data, status, error } = await useAsyncData<PaginatedResponse<IVideoCard>>(
|
|
16
|
+
stateKey.value,
|
|
17
|
+
() => {
|
|
18
|
+
if (!slug.value) return Promise.resolve(null as unknown as PaginatedResponse<IVideoCard>);
|
|
19
|
+
|
|
20
|
+
return useApiFetcher<PaginatedResponse<IVideoCard>>(
|
|
21
|
+
stateKey.value,
|
|
22
|
+
apiMethod,
|
|
23
|
+
limit,
|
|
24
|
+
lang,
|
|
25
|
+
slug.value
|
|
26
|
+
)();
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return { data, status, error };
|
|
31
|
+
};
|
package/package.json
CHANGED
package/pages/videos/[slug].vue
CHANGED
|
@@ -34,6 +34,10 @@ const featureFlags = useRuntimeConfig().public.featureFlags;
|
|
|
34
34
|
|
|
35
35
|
const pageName = useGetPureRouteName();
|
|
36
36
|
|
|
37
|
+
const fetchRelated = featureFlags.VectorRelatedVideosEnabled
|
|
38
|
+
? useFetchVectorRelatedVideos(featureFlags.FetchByUrlEnabled ? VideosApiService.getVectorRelatedVideosByUrl : VideosApiService.getVectorRelatedVideosById)
|
|
39
|
+
: useFetchRelatedVideos(featureFlags.FetchByUrlEnabled ? VideosApiService.getRelatedVideosByUrl : VideosApiService.getRelatedVideosById);
|
|
40
|
+
|
|
37
41
|
const [
|
|
38
42
|
{ data: videoData, status: statusVideo },
|
|
39
43
|
{ data: relatedVideos, status },
|
|
@@ -41,7 +45,7 @@ const [
|
|
|
41
45
|
{ data: dataModels },
|
|
42
46
|
] = await Promise.all([
|
|
43
47
|
useFetchVideo(featureFlags.FetchByUrlEnabled ? VideosApiService.getVideoByUrl : VideosApiService.getVideoById),
|
|
44
|
-
|
|
48
|
+
fetchRelated,
|
|
45
49
|
useFetchRelatedSearch(SearchApiService.getPhrases),
|
|
46
50
|
useFetchTopModels(ModelsApiService.getTop, 24, AsyncData.TopModelsMore, String(pageName)),
|
|
47
51
|
]);
|
|
@@ -12,6 +12,7 @@ export const AsyncData = {
|
|
|
12
12
|
ModelsByFirstLetter: 'models-by-first-letter',
|
|
13
13
|
ModelsByFirstLetterChips: 'models-by-first-letter-chips',
|
|
14
14
|
RelatedVideos: 'related-videos',
|
|
15
|
+
VectorRelatedVideos: 'vector-related-videos',
|
|
15
16
|
RefererPhrases: 'referer-phrases-video',
|
|
16
17
|
AsideVideos: 'aside-videos',
|
|
17
18
|
TopModels: 'top-models',
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { IVideoCard, IRawVideoCard } from '../../../../types';
|
|
2
|
+
import { trackExternalCall } from '../../../utils/metrics';
|
|
3
|
+
import { cleanVideoCard } from '../../../../runtime';
|
|
4
|
+
|
|
5
|
+
export default defineEventHandler(async (event) => {
|
|
6
|
+
const apiUrl = process.env.NUXT_PUBLIC_API_DOMAIN;
|
|
7
|
+
|
|
8
|
+
const query = getQuery(event);
|
|
9
|
+
const queryString = new URLSearchParams(query as Record<string, string>).toString();
|
|
10
|
+
const id = getRouterParam(event, 'id');
|
|
11
|
+
const url = `${apiUrl}/v1/videos-vectors/${id}/related${queryString ? `?${queryString}` : ''}`;
|
|
12
|
+
|
|
13
|
+
const clientHeaders = event.node.req.headers as HeadersInit;
|
|
14
|
+
|
|
15
|
+
// similarity service is optional: its outage must not take the whole video page down
|
|
16
|
+
const rawData = await trackExternalCall('video-vector-related', () =>
|
|
17
|
+
apiFetch(url, {
|
|
18
|
+
method: 'GET',
|
|
19
|
+
headers: clientHeaders,
|
|
20
|
+
})
|
|
21
|
+
).catch(() => null);
|
|
22
|
+
|
|
23
|
+
const items: IRawVideoCard[] = rawData?.data?.items || [];
|
|
24
|
+
|
|
25
|
+
const cleanedItems: IVideoCard[] = items.map(cleanVideoCard);
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
currentPage: rawData?.data?.current_page,
|
|
29
|
+
allPages: rawData?.data?.all_pages,
|
|
30
|
+
total: rawData?.data?.total,
|
|
31
|
+
items: cleanedItems,
|
|
32
|
+
};
|
|
33
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { IVideoCard, IRawVideoCard } from '../../../../types';
|
|
2
|
+
import { trackExternalCall } from '../../../utils/metrics';
|
|
3
|
+
import { cleanVideoCard, getUrlWithProxiedParams } from '../../../../runtime';
|
|
4
|
+
|
|
5
|
+
export default defineEventHandler(async (event) => {
|
|
6
|
+
const apiUrl = process.env.NUXT_PUBLIC_API_DOMAIN;
|
|
7
|
+
|
|
8
|
+
const slug = getRouterParam(event, 'url');
|
|
9
|
+
const url = getUrlWithProxiedParams(`${apiUrl}/v1/videos-vectors/url/${slug}/related`, event);
|
|
10
|
+
|
|
11
|
+
const clientHeaders = event.node.req.headers as HeadersInit;
|
|
12
|
+
|
|
13
|
+
// similarity service is optional: its outage must not take the whole video page down
|
|
14
|
+
const rawData = await trackExternalCall('video-vector-related', () =>
|
|
15
|
+
apiFetch(url, {
|
|
16
|
+
method: 'GET',
|
|
17
|
+
headers: clientHeaders,
|
|
18
|
+
})
|
|
19
|
+
).catch(() => null);
|
|
20
|
+
|
|
21
|
+
const items: IRawVideoCard[] = rawData?.data?.items || [];
|
|
22
|
+
|
|
23
|
+
const cleanedItems: IVideoCard[] = items.map(cleanVideoCard);
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
currentPage: rawData?.data?.current_page,
|
|
27
|
+
allPages: rawData?.data?.all_pages,
|
|
28
|
+
total: rawData?.data?.total,
|
|
29
|
+
items: cleanedItems,
|
|
30
|
+
};
|
|
31
|
+
});
|
|
@@ -189,6 +189,53 @@ export class VideosApiService {
|
|
|
189
189
|
);
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
static async getVectorRelatedVideosById(
|
|
193
|
+
limit: number,
|
|
194
|
+
lang: Language,
|
|
195
|
+
id: string,
|
|
196
|
+
niche = NicheHelper.getSiteDefaultNiche(),
|
|
197
|
+
): Promise<PaginatedResponse<IVideoCard>> {
|
|
198
|
+
return await ApiHelper.fetch(
|
|
199
|
+
`/videos/get-vector-related-videos-by-id/${id}`,
|
|
200
|
+
{
|
|
201
|
+
method: 'GET',
|
|
202
|
+
headers: {
|
|
203
|
+
'X-Niche': niche,
|
|
204
|
+
'X-Language': toHeaderLanguage(lang),
|
|
205
|
+
'X-Cache-Time': '259200',
|
|
206
|
+
'Content-Type': 'application/json',
|
|
207
|
+
},
|
|
208
|
+
params: {
|
|
209
|
+
limit,
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
useRuntimeConfig(),
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
static async getVectorRelatedVideosByUrl(
|
|
216
|
+
limit: number,
|
|
217
|
+
lang: Language,
|
|
218
|
+
url: string,
|
|
219
|
+
niche = NicheHelper.getSiteDefaultNiche(),
|
|
220
|
+
): Promise<PaginatedResponse<IVideoCard>> {
|
|
221
|
+
return await ApiHelper.fetch(
|
|
222
|
+
`/videos/get-vector-related-videos-by-url/${url}`,
|
|
223
|
+
{
|
|
224
|
+
method: 'GET',
|
|
225
|
+
headers: {
|
|
226
|
+
'X-Niche': niche,
|
|
227
|
+
'X-Language': toHeaderLanguage(lang),
|
|
228
|
+
'X-Cache-Time': '259200',
|
|
229
|
+
'Content-Type': 'application/json',
|
|
230
|
+
},
|
|
231
|
+
params: {
|
|
232
|
+
limit,
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
useRuntimeConfig(),
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
192
239
|
static async getVideoById(
|
|
193
240
|
lang: Language,
|
|
194
241
|
id: string,
|