itube-specs 0.0.914 → 0.0.915
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/assets/scss/themes/dark/_theme.scss +0 -2
- package/assets/scss/themes/light/_theme.scss +0 -2
- package/components/card/playlist.vue +29 -1
- package/components/card/round.vue +5 -4
- package/components/ui/label.vue +1 -10
- package/components/view/video.vue +0 -12
- package/composables/__tests__/use-playlist-collections.test.ts +63 -0
- package/composables/use-playlist-collections.ts +39 -0
- package/package.json +1 -1
- package/runtime/utils/cleaners/__tests__/clean-video-card.test.ts +0 -3
- package/runtime/utils/cleaners/__tests__/clean-video-data.test.ts +0 -1
- package/runtime/utils/cleaners/clean-video-card.ts +0 -1
- package/runtime/utils/cleaners/clean-video-data.ts +0 -1
- package/stubs/history.ts +0 -1
- package/stubs/videos.ts +2 -3
- package/types/raw/raw-video-card.d.ts +1 -1
- package/types/raw/raw-video-data.d.ts +1 -1
- package/types/video-card.d.ts +0 -1
- package/types/video-data.d.ts +0 -1
|
@@ -592,8 +592,6 @@ $label-primary-bg: $color-indigo-600;
|
|
|
592
592
|
$label-primary-text: $color-white;
|
|
593
593
|
$label-gradient-bg: $color-indigo-600;
|
|
594
594
|
$label-gradient-text: $color-white;
|
|
595
|
-
$label-yellow-bg: $color-amber-500;
|
|
596
|
-
$label-yellow-text: $mono-black-100;
|
|
597
595
|
$label-green-bg: $color-green-500;
|
|
598
596
|
$label-green-text: $color-white;
|
|
599
597
|
$label-white-bg: $mono-white-75;
|
|
@@ -643,8 +643,6 @@ $label-primary-bg: $color-indigo-600;
|
|
|
643
643
|
$label-primary-text: $color-white;
|
|
644
644
|
$label-gradient-bg: $color-indigo-600;
|
|
645
645
|
$label-gradient-text: $color-white;
|
|
646
|
-
$label-yellow-bg: $color-amber-500;
|
|
647
|
-
$label-yellow-text: $mono-black-100;
|
|
648
646
|
$label-green-bg: $color-green-500;
|
|
649
647
|
$label-green-text: $color-white;
|
|
650
648
|
$label-white-bg: $mono-white-75;
|
|
@@ -135,6 +135,17 @@
|
|
|
135
135
|
/>
|
|
136
136
|
</template>
|
|
137
137
|
<template #items="className">
|
|
138
|
+
<button
|
|
139
|
+
:class="className"
|
|
140
|
+
type="button"
|
|
141
|
+
@click="toggleFavorite(card.id, card.name)"
|
|
142
|
+
>
|
|
143
|
+
<UiIcon
|
|
144
|
+
name="heart-stroke"
|
|
145
|
+
size="16"
|
|
146
|
+
/>
|
|
147
|
+
{{ favoritesLabel }}
|
|
148
|
+
</button>
|
|
138
149
|
<button
|
|
139
150
|
:class="className"
|
|
140
151
|
type="button"
|
|
@@ -180,7 +191,7 @@ import { CSS_BREAKPOINTS } from '../../config/css-breakpoints';
|
|
|
180
191
|
const props = defineProps<{
|
|
181
192
|
card: IPlaylistCard
|
|
182
193
|
topPlaylists?: boolean
|
|
183
|
-
/** Без меню «три точки» — вместе с ним уходят share и report */
|
|
194
|
+
/** Без меню «три точки» — вместе с ним уходят избранное, share и report */
|
|
184
195
|
noActions?: boolean
|
|
185
196
|
priority?: boolean
|
|
186
197
|
}>();
|
|
@@ -205,6 +216,23 @@ const userProfileData = computed(() => useState<IProfileData>('profileData').val
|
|
|
205
216
|
|
|
206
217
|
const { isAuthorized } = useUser(AuthorizationApiService);
|
|
207
218
|
|
|
219
|
+
const { isFavorite, toggleFavorite } = usePlaylistCollections();
|
|
220
|
+
|
|
221
|
+
const favorite = computed(() => isFavorite(props.card.id));
|
|
222
|
+
|
|
223
|
+
// Тем же кликом, что переключает коллекцию, меню закрывается. Если менять надпись сразу, она
|
|
224
|
+
// дёргается прямо во время анимации закрытия, поэтому в меню живёт снапшот состояния — он
|
|
225
|
+
// догоняет реальное только к следующему открытию, когда пункт всё равно перерисовывается с нуля.
|
|
226
|
+
const shownFavorite = ref(favorite.value);
|
|
227
|
+
|
|
228
|
+
const favoritesLabel = computed(() => shownFavorite.value
|
|
229
|
+
? t('video_actions.remove_favorites')
|
|
230
|
+
: t('video_actions.add_favorites'));
|
|
231
|
+
|
|
232
|
+
watch(() => dropdownRef.value?.open, (isOpen) => {
|
|
233
|
+
if (isOpen) shownFavorite.value = favorite.value;
|
|
234
|
+
});
|
|
235
|
+
|
|
208
236
|
const avatar = computed(() => {
|
|
209
237
|
if (isAuthorized.value && props.topPlaylists && userProfileData.value?.avatar) {
|
|
210
238
|
return userProfileData.value.avatar;
|
|
@@ -70,10 +70,11 @@ function onImageError() {
|
|
|
70
70
|
</script>
|
|
71
71
|
|
|
72
72
|
<style lang="scss">
|
|
73
|
-
/* Заход бейджа внутрь
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
$badge-inset
|
|
73
|
+
/* Заход бейджа внутрь круга: отступ от угла карточки подобран так, чтобы центр
|
|
74
|
+
бейджа сел на границу окружности по диагонали — примерно половина снаружи.
|
|
75
|
+
На мобиле аватар 80px против 96px, а бейдж не уменьшается, поэтому отступ меньше. */
|
|
76
|
+
$badge-inset: 3px;
|
|
77
|
+
$badge-inset--mobile: 1px;
|
|
77
78
|
|
|
78
79
|
.card-round {
|
|
79
80
|
position: relative;
|
package/components/ui/label.vue
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<script setup lang="ts">
|
|
12
12
|
withDefaults(
|
|
13
13
|
defineProps<{
|
|
14
|
-
theme?: 'default' | 'primary' | 'primary-gradient' | '
|
|
14
|
+
theme?: 'default' | 'primary' | 'primary-gradient' | 'green' | 'white' | 'black' | 'backdrop' | 'secondary' | 'success' | 'blur' | 'grey'
|
|
15
15
|
text: string | number
|
|
16
16
|
}>(), {
|
|
17
17
|
theme: 'default'
|
|
@@ -47,15 +47,6 @@ withDefaults(
|
|
|
47
47
|
color: $label-gradient-text;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
.ui-label.--yellow {
|
|
51
|
-
@include font-3xs;
|
|
52
|
-
|
|
53
|
-
background: $label-yellow-bg;
|
|
54
|
-
color: $label-yellow-text;
|
|
55
|
-
height: 15px;
|
|
56
|
-
display: inline-block;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
50
|
.ui-label.--green {
|
|
60
51
|
background-color: $label-green-bg;
|
|
61
52
|
color: $label-green-text;
|
|
@@ -4,12 +4,6 @@
|
|
|
4
4
|
<header class="view-video__header">
|
|
5
5
|
<h1 class="view-video__title">
|
|
6
6
|
<span class="_truncate">{{ data.title }}</span>
|
|
7
|
-
<UiLabel
|
|
8
|
-
v-if="data.quality"
|
|
9
|
-
:text="data.quality"
|
|
10
|
-
class="view-video__title-label"
|
|
11
|
-
theme="yellow"
|
|
12
|
-
/>
|
|
13
7
|
</h1>
|
|
14
8
|
<div class="view-video__header-wrapper">
|
|
15
9
|
<UiExpandRow
|
|
@@ -248,12 +242,6 @@ function getModelChips(model: IVideoData['models'][number], index: number): IChi
|
|
|
248
242
|
letter-spacing: var(--tracking-wide);
|
|
249
243
|
}
|
|
250
244
|
|
|
251
|
-
.view-video__title-label {
|
|
252
|
-
display: inline;
|
|
253
|
-
vertical-align: middle;
|
|
254
|
-
margin-left: 12px;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
245
|
.view-video__header-wrapper {
|
|
258
246
|
margin-bottom: 16px;
|
|
259
247
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// @vitest-environment nuxt
|
|
2
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
3
|
+
import { usePlaylistCollections } from '../use-playlist-collections';
|
|
4
|
+
import { useVideoCollections } from '../use-video-collections';
|
|
5
|
+
import { useSnackbar } from '../use-snackbar';
|
|
6
|
+
|
|
7
|
+
describe('usePlaylistCollections', () => {
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
useNuxtApp().payload.state = {};
|
|
10
|
+
vi.useFakeTimers();
|
|
11
|
+
useSnackbar().resetSnackbar();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
vi.useRealTimers();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('добавляет в избранное и показывает снекбар с кнопкой отмены', () => {
|
|
19
|
+
const { favorites, isFavorite, toggleFavorite } = usePlaylistCollections();
|
|
20
|
+
const { snackbarText, snackbarIcon, snackbarHighlight, snackbarActionText } = useSnackbar();
|
|
21
|
+
|
|
22
|
+
toggleFavorite('playlist-1', 'Playlist name');
|
|
23
|
+
|
|
24
|
+
expect(favorites.value).toEqual([ 'playlist-1' ]);
|
|
25
|
+
expect(isFavorite('playlist-1')).toBe(true);
|
|
26
|
+
expect(snackbarText.value).toBe('added');
|
|
27
|
+
expect(snackbarIcon.value).toBe('heart');
|
|
28
|
+
expect(snackbarHighlight.value).toBe('Playlist name');
|
|
29
|
+
expect(snackbarActionText.value).toBe('undo');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('удаление показывает снекбар «removed»', () => {
|
|
33
|
+
const { favorites, toggleFavorite } = usePlaylistCollections();
|
|
34
|
+
const { snackbarText } = useSnackbar();
|
|
35
|
+
|
|
36
|
+
toggleFavorite('playlist-1', 'Playlist name');
|
|
37
|
+
toggleFavorite('playlist-1', 'Playlist name');
|
|
38
|
+
|
|
39
|
+
expect(favorites.value).toEqual([]);
|
|
40
|
+
expect(snackbarText.value).toBe('removed');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('отмена возвращает список и не показывает новый снекбар', () => {
|
|
44
|
+
const { favorites, toggleFavorite } = usePlaylistCollections();
|
|
45
|
+
const { runSnackbarAction, snackbarText } = useSnackbar();
|
|
46
|
+
|
|
47
|
+
toggleFavorite('playlist-2', 'Another name');
|
|
48
|
+
runSnackbarAction();
|
|
49
|
+
|
|
50
|
+
expect(favorites.value).toEqual([]);
|
|
51
|
+
expect(snackbarText.value).toBe('');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('избранные плейлисты не смешиваются с избранными видео', () => {
|
|
55
|
+
const { favorites: playlistFavorites, toggleFavorite: togglePlaylist } = usePlaylistCollections();
|
|
56
|
+
const { favorites: videoFavorites } = useVideoCollections();
|
|
57
|
+
|
|
58
|
+
togglePlaylist('playlist-3', 'Third name');
|
|
59
|
+
|
|
60
|
+
expect(playlistFavorites.value).toEqual([ 'playlist-3' ]);
|
|
61
|
+
expect(videoFavorites.value).toEqual([]);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Заглушка избранных плейлистов — механика та же, что у видео (см. `useVideoCollections`),
|
|
3
|
+
* но список свой: плейлист не ролик, и на странице избранного они не должны смешиваться.
|
|
4
|
+
* Ручек в API пока нет, поэтому состояние живёт только на клиенте и сбрасывается при перезагрузке.
|
|
5
|
+
*/
|
|
6
|
+
export const usePlaylistCollections = () => {
|
|
7
|
+
const favorites = useState<string[]>('playlist-collections-favorites', () => []);
|
|
8
|
+
|
|
9
|
+
const { show } = useSnackbar();
|
|
10
|
+
|
|
11
|
+
/** Возвращает true, если плейлист добавлен, и false — если убран. */
|
|
12
|
+
function toggle(id: string) {
|
|
13
|
+
const added = !favorites.value.includes(id);
|
|
14
|
+
|
|
15
|
+
favorites.value = added
|
|
16
|
+
? [ ...favorites.value, id ]
|
|
17
|
+
: favorites.value.filter(item => item !== id);
|
|
18
|
+
|
|
19
|
+
return added;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const isFavorite = (id: string) => favorites.value.includes(id);
|
|
23
|
+
|
|
24
|
+
// Снекбар показывается только на исходное действие: откат возвращает список как был,
|
|
25
|
+
// повторно сообщать об этом нечего — иначе отмена сама выглядит как новое изменение.
|
|
26
|
+
const toggleFavorite = (id: string, name = '') => {
|
|
27
|
+
show(toggle(id) ? 'added' : 'removed', {
|
|
28
|
+
icon: 'heart',
|
|
29
|
+
highlight: name,
|
|
30
|
+
onAction: () => toggle(id),
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
favorites,
|
|
36
|
+
isFavorite,
|
|
37
|
+
toggleFavorite,
|
|
38
|
+
};
|
|
39
|
+
};
|
package/package.json
CHANGED
|
@@ -5,7 +5,6 @@ describe('cleanVideoCard', () => {
|
|
|
5
5
|
it('полные данные', () => {
|
|
6
6
|
const raw = {
|
|
7
7
|
guid: 'v-1',
|
|
8
|
-
quality: '1080p',
|
|
9
8
|
duration: 600,
|
|
10
9
|
title: 'Video Title',
|
|
11
10
|
views: 1000,
|
|
@@ -27,7 +26,6 @@ describe('cleanVideoCard', () => {
|
|
|
27
26
|
};
|
|
28
27
|
const result = cleanVideoCard(raw as any);
|
|
29
28
|
expect(result.guid).toBe('v-1');
|
|
30
|
-
expect(result.quality).toBe('1080p');
|
|
31
29
|
expect(result.isNew).toBe(true);
|
|
32
30
|
expect(result.channelName).toBe('Channel');
|
|
33
31
|
expect(result.channelAvatar).toBe('https://img.com/ch.jpg');
|
|
@@ -44,7 +42,6 @@ describe('cleanVideoCard', () => {
|
|
|
44
42
|
it('пустые данные → дефолты', () => {
|
|
45
43
|
const result = cleanVideoCard({} as any);
|
|
46
44
|
expect(result.guid).toBe('');
|
|
47
|
-
expect(result.quality).toBe('');
|
|
48
45
|
expect(result.duration).toBe(0);
|
|
49
46
|
expect(result.views).toBe(0);
|
|
50
47
|
expect(result.isNew).toBe(false);
|
|
@@ -4,7 +4,6 @@ import { ThumbSize } from '../../constants/thumb-size';
|
|
|
4
4
|
/** Нормализует сырую карточку видео к типу IVideoCard. */
|
|
5
5
|
export const cleanVideoCard = (card: IRawVideoCard): IVideoCard => ({
|
|
6
6
|
guid: card.guid || '',
|
|
7
|
-
quality: card.quality || '',
|
|
8
7
|
duration: card.duration || 0,
|
|
9
8
|
title: card.title || '',
|
|
10
9
|
views: card.views || 0,
|
|
@@ -39,7 +39,6 @@ const cleanHotspots = (raw: string[] | null): number[] | null => {
|
|
|
39
39
|
export const cleanVideoData = (data: IRawVideoData): IVideoData => ({
|
|
40
40
|
guid: data.guid || '',
|
|
41
41
|
md5: data.md5 || '',
|
|
42
|
-
quality: data.quality || '',
|
|
43
42
|
duration: data.duration || 0,
|
|
44
43
|
title: data.title || '',
|
|
45
44
|
header: data.header || '',
|
package/stubs/history.ts
CHANGED
package/stubs/videos.ts
CHANGED
|
@@ -5,7 +5,6 @@ export type StubVideo = {
|
|
|
5
5
|
id: string;
|
|
6
6
|
title: string;
|
|
7
7
|
duration: number;
|
|
8
|
-
quality?: string;
|
|
9
8
|
thumbUrls: IThumbUrls;
|
|
10
9
|
previewUrl: string;
|
|
11
10
|
channelName?: string;
|
|
@@ -15,7 +14,7 @@ export type StubVideo = {
|
|
|
15
14
|
/** Общий пул роликов для всех заглушек — порядок фиксирован, на него завязаны наборы-надстройки. */
|
|
16
15
|
export const VIDEO_POOL: StubVideo[] = [
|
|
17
16
|
{ id: '445839845', title: 'Drew Sebastian Barebacks Nick Fitt: Muscle Daddy Tops Athletic Bottom on the Couch', duration: 486, thumbUrls: { webp: { '320x180': 'https://nlt01.gay.cool/a/8/d/a8dc253cad705d688508de096435a989/thumbs/320x180/3.webp', '480x270': 'https://nlt01.gay.cool/a/8/d/a8dc253cad705d688508de096435a989/thumbs/480x270/3.webp' } }, channelName: 'Icon Male', channelAvatar: 'https://nli01.joystorage.com/c/0/2/c02a3f8ef14943b5f3da537aaed46fcc.jpeg', previewUrl: 'https://nlp02.gay.cool/a/8/d/a8dc253cad705d688508de096435a989/preview/video.mp4' },
|
|
18
|
-
{ id: '171708405', title: 'Bareback bathroom fisting with black latex gloves, rimming, piss play and anal gape', duration: 358,
|
|
17
|
+
{ id: '171708405', title: 'Bareback bathroom fisting with black latex gloves, rimming, piss play and anal gape', duration: 358, thumbUrls: { webp: { '320x180': 'https://nlt03.gay.cool/b/7/5/b75c1bf33571126845502e2b9bb5d687/thumbs/320x180/33.webp', '480x270': 'https://nlt03.gay.cool/b/7/5/b75c1bf33571126845502e2b9bb5d687/thumbs/480x270/33.webp' } }, previewUrl: 'https://nlp02.gay.cool/b/7/5/b75c1bf33571126845502e2b9bb5d687/preview/video.mp4' },
|
|
19
18
|
{ id: '374534215', title: 'Jake Davis Takes Bareback Anal in Blue Harness from Athletic Partner', duration: 487, thumbUrls: { webp: { '320x180': 'https://nlt02.gay.cool/2/4/6/246f577796c7c955b8df94f6454de2a5/thumbs/320x180/12.webp', '480x270': 'https://nlt02.gay.cool/2/4/6/246f577796c7c955b8df94f6454de2a5/thumbs/480x270/12.webp' } }, channelName: 'Icon Male', channelAvatar: 'https://nli01.joystorage.com/c/0/2/c02a3f8ef14943b5f3da537aaed46fcc.jpeg', previewUrl: 'https://nlp05.gay.cool/2/4/6/246f577796c7c955b8df94f6454de2a5/preview/video.mp4' },
|
|
20
19
|
{ id: '3377550', title: 'Rego Bello and Devin Adams bareback bedroom flip: reciprocal oral then Devin tops Rego', duration: 426, thumbUrls: { webp: { '320x180': 'https://nlt01.gay.cool/3/e/b/3eb0f19f1e600a98a7f5a5e51edc4b46/thumbs/320x180/33.webp', '480x270': 'https://nlt01.gay.cool/3/e/b/3eb0f19f1e600a98a7f5a5e51edc4b46/thumbs/480x270/33.webp' } }, previewUrl: 'https://nlp05.gay.cool/3/e/b/3eb0f19f1e600a98a7f5a5e51edc4b46/preview/video.mp4' },
|
|
21
20
|
{ id: '443170695', title: 'Johnny Rapid and Malakai White lead four-man hotel bareback with Max Lorde and Kyler Drayke', duration: 487, thumbUrls: { webp: { '320x180': 'https://nlt04.gay.cool/9/f/f/9ff067b9ea8b157ff2478667423ff9bb/thumbs/320x180/29.webp', '480x270': 'https://nlt04.gay.cool/9/f/f/9ff067b9ea8b157ff2478667423ff9bb/thumbs/480x270/29.webp' } }, channelName: 'Johnny Rapid', channelAvatar: 'https://nli01.joystorage.com/e/6/d/e6d0abd734a1a2eaef0baf7ceccfdb5c.jpeg', previewUrl: 'https://nlp01.gay.cool/9/f/f/9ff067b9ea8b157ff2478667423ff9bb/preview/video.mp4' },
|
|
@@ -25,5 +24,5 @@ export const VIDEO_POOL: StubVideo[] = [
|
|
|
25
24
|
{ id: '444068845', title: 'Dean Young Bareback Flip Fuck: Versatile Anal, Deepthroat and Rimming in the Bedroom', duration: 604, thumbUrls: { webp: { '320x180': 'https://nlt03.gay.cool/7/d/a/7daacbe876b51c7cdc50101b0d504be8/thumbs/320x180/16.webp', '480x270': 'https://nlt03.gay.cool/7/d/a/7daacbe876b51c7cdc50101b0d504be8/thumbs/480x270/16.webp' } }, previewUrl: 'https://nlp03.gay.cool/7/d/a/7daacbe876b51c7cdc50101b0d504be8/preview/video.mp4' },
|
|
26
25
|
{ id: '374968825', title: 'Brody Fox sucks and rides daddy Cain Marko bareback', duration: 497, thumbUrls: { webp: { '320x180': 'https://nlt01.gay.cool/9/e/2/9e23f59e882e860da3ab2400d93ac73e/thumbs/320x180/10.webp', '480x270': 'https://nlt01.gay.cool/9/e/2/9e23f59e882e860da3ab2400d93ac73e/thumbs/480x270/10.webp' } }, previewUrl: 'https://nlp03.gay.cool/9/e/2/9e23f59e882e860da3ab2400d93ac73e/preview/video.mp4' },
|
|
27
26
|
{ id: '377844995', title: 'Rick Lovatelli: blond twink dominated, deepthroats and gets barebacked by a suited bearded top', duration: 1627, thumbUrls: { webp: { '320x180': 'https://nlt05.gay.cool/f/1/b/f1b09d77a2e23f4b4696d97169614099/thumbs/320x180/25.webp', '480x270': 'https://nlt05.gay.cool/f/1/b/f1b09d77a2e23f4b4696d97169614099/thumbs/480x270/25.webp' } }, previewUrl: 'https://nlp01.gay.cool/f/1/b/f1b09d77a2e23f4b4696d97169614099/preview/video.mp4' },
|
|
28
|
-
{ id: '375132285', title: 'Viktor Rom and Jordan Fox bareback blue-haired bottom in bedroom threesome', duration: 800,
|
|
27
|
+
{ id: '375132285', title: 'Viktor Rom and Jordan Fox bareback blue-haired bottom in bedroom threesome', duration: 800, thumbUrls: { webp: { '320x180': 'https://nlt03.gay.cool/5/f/b/5fbcdd007476cd0a1e9c85abc9fc0042/thumbs/320x180/1.webp', '480x270': 'https://nlt03.gay.cool/5/f/b/5fbcdd007476cd0a1e9c85abc9fc0042/thumbs/480x270/1.webp' } }, previewUrl: 'https://nlp04.gay.cool/5/f/b/5fbcdd007476cd0a1e9c85abc9fc0042/preview/video.mp4' },
|
|
29
28
|
];
|
package/types/video-card.d.ts
CHANGED