itube-specs 0.0.1052 → 0.0.1053
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/SITE_UPDATES.md +15 -0
- package/app.config.ts +2 -0
- package/components/player/core.vue +1 -0
- package/components/view/model-videos.vue +2 -4
- package/components/view/video.vue +20 -5
- package/components/view/videos.vue +2 -4
- package/composables/page/use-category-page.ts +10 -4
- package/composables/page/use-channel-page.ts +8 -3
- package/composables/page/use-model-page.ts +8 -3
- package/package.json +1 -1
- package/pages/categories/[slug].vue +2 -4
- package/pages/channels/[channel].vue +2 -4
- package/pages/search/[slug]/index.vue +2 -4
- package/types/player-site-hooks.d.ts +2 -0
package/SITE_UPDATES.md
CHANGED
|
@@ -11,6 +11,21 @@
|
|
|
11
11
|
коммиты до бампа); `npm run patch` сам проставит ей номер версии. Формат пункта: что сделать и где,
|
|
12
12
|
кому именно (всем / сайтам с N), что будет, если не сделать.
|
|
13
13
|
|
|
14
|
+
## 0.0.1053 — 2026-10-26
|
|
15
|
+
|
|
16
|
+
- **Всем — к сведению:** нативка в конце сеток листингов и похожих (`AdvNativeBottom` в `view/video`, `view/videos`,
|
|
17
|
+
`view/model-videos`, страницах категории, канала, поиска) больше не ленивая. Контейнер приезжает в серверной
|
|
18
|
+
разметке, и сеть заполняет его до гидрации, а ленивая гидрация пересобирала уже заполненный iframe — креатив
|
|
19
|
+
терялся, слот оставался пустым (найдено на hdsex-remaster). Делать ничего не надо.
|
|
20
|
+
- **Сайтам со своими сортировками на страницах на page-composables:** у `useCategoryPage`, `useChannelPage`,
|
|
21
|
+
`useModelPage` новая опция `sortOptions` (по умолчанию `sortItemsDefault`) — по ней ключи меты и проверка `?sort`.
|
|
22
|
+
Без неё свои значения `?sort` такой страницы — 404. Образец — коммит hdsex-remaster с этим изменением.
|
|
23
|
+
- **Сайтам с частотным капом преролла (hdsex-remaster):** в `usePlayerSiteHooks()` новый хук `onAdStart()` — старт
|
|
24
|
+
рекламы в плеере (например, поставить куку капа). Остальным — ничего.
|
|
25
|
+
- **Сайтам с боковой колонкой моделей у плеера (hdsex-remaster):** `appConfig.videoPage.hideAsideModelChips: true` —
|
|
26
|
+
с 1024px не дублировать чипсой над плеером модель, у которой есть блок в колонке. По умолчанию `false`, ничего не
|
|
27
|
+
меняется.
|
|
28
|
+
|
|
14
29
|
## 0.0.1052 — 2026-10-22
|
|
15
30
|
|
|
16
31
|
- **Всем — к сведению:** мусорные адреса листингов теперь 404. `?page` не натуральное число (`0`, `-1`, `abc`, `01`) —
|
package/app.config.ts
CHANGED
|
@@ -39,6 +39,8 @@ export default defineAppConfig({
|
|
|
39
39
|
webcamsBlock: false,
|
|
40
40
|
/** Сколько моделей из хвоста списка уходит во врезки похожих, когда блок Live Cams включён. */
|
|
41
41
|
webcamInserts: 8,
|
|
42
|
+
/** С 1024px прятать чипсы моделей над плеером, у которых есть блок в боковой колонке (не дублировать ссылку). */
|
|
43
|
+
hideAsideModelChips: false,
|
|
42
44
|
/** Мета ролика как на старых движках: description — до трёх фраз перехода (недостающие — из тегов), без них —
|
|
43
45
|
* категории, склейка ключами `meta.videos-slug.tags_and` / `tags_tail`; в title доступен `{thirdText}` — название
|
|
44
46
|
* в Title Case. Выключено — description из описания ролика. */
|
|
@@ -38,10 +38,8 @@
|
|
|
38
38
|
priority
|
|
39
39
|
>
|
|
40
40
|
<template #grid-end>
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
hydrate-on-visible
|
|
44
|
-
/>
|
|
41
|
+
<!-- не ленивая: ленивая гидрация пересобирала заполненный сетью iframe, креатив терялся -->
|
|
42
|
+
<AdvNativeBottom v-if="featureFlags.AdsNativeEnabled"/>
|
|
45
43
|
</template>
|
|
46
44
|
</GridVideos>
|
|
47
45
|
<div
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
v-for="(model, index) in data.models"
|
|
30
30
|
:key="index"
|
|
31
31
|
:item="getModelChips(model)"
|
|
32
|
+
:class="{'view-video__model-chips': hasAsideBlock(model.title)}"
|
|
32
33
|
is-link
|
|
33
34
|
/>
|
|
34
35
|
</template>
|
|
@@ -75,11 +76,10 @@
|
|
|
75
76
|
/>
|
|
76
77
|
</template>
|
|
77
78
|
|
|
79
|
+
<!-- Без ленивой гидрации: контейнер приезжает в серверной разметке и сеть заполняет его до гидрации,
|
|
80
|
+
а ленивая гидрация пересобирала уже заполненный iframe — креатив терялся, слот оставался пустым. -->
|
|
78
81
|
<template #grid-end>
|
|
79
|
-
<
|
|
80
|
-
v-if="featureFlags.AdsNativeEnabled"
|
|
81
|
-
hydrate-on-visible
|
|
82
|
-
/>
|
|
82
|
+
<AdvNativeBottom v-if="featureFlags.AdsNativeEnabled"/>
|
|
83
83
|
</template>
|
|
84
84
|
</LazyGridVideos>
|
|
85
85
|
</section>
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
|
|
127
127
|
<script setup lang="ts">
|
|
128
128
|
import type { IChipsItem, IModelCard, IStripcashModel, IVideoCard, IVideoData, PaginatedResponse } from '../../types';
|
|
129
|
-
import { convertModelToRoundCard } from '../../runtime';
|
|
129
|
+
import { convertModelToRoundCard, convertString } from '../../runtime';
|
|
130
130
|
import { VideosApiService } from '../../services/api/videos.service';
|
|
131
131
|
import type { AsyncDataRequestStatus } from '#app';
|
|
132
132
|
|
|
@@ -232,6 +232,15 @@ const topChips = computed(() => {
|
|
|
232
232
|
const topChipsStrings = computed(() => topChips.value?.map(item => item.value));
|
|
233
233
|
|
|
234
234
|
/** Портрет в чипсе — квадратный кроп по лицу; своего у видео нет, его подмешивает BFF. */
|
|
235
|
+
// `videoPage.hideAsideModelChips`: с 1024px чипса модели не нужна тем, у кого есть блок в боковой колонке плеера
|
|
236
|
+
// (`useFetchAsideModels`). Снимок, а не реактивное чтение: список заполняется эффектом, и реактивный класс
|
|
237
|
+
// пересчитывался бы во время гидрации — разметка расходилась бы с серверной.
|
|
238
|
+
const asideModelSlugs = new Set(videoPage.hideAsideModelChips ? useAsideModelSlugs().value : []);
|
|
239
|
+
|
|
240
|
+
function hasAsideBlock(title: string): boolean {
|
|
241
|
+
return asideModelSlugs.has(convertString().toSlug(title));
|
|
242
|
+
}
|
|
243
|
+
|
|
235
244
|
const placeholderOf = useModelPlaceholder();
|
|
236
245
|
|
|
237
246
|
function getModelChips(model: IVideoData['models'][number]): IChipsItem {
|
|
@@ -283,6 +292,12 @@ function getModelChips(model: IVideoData['models'][number]): IChipsItem {
|
|
|
283
292
|
margin-top: 32px;
|
|
284
293
|
}
|
|
285
294
|
|
|
295
|
+
.view-video__model-chips {
|
|
296
|
+
@include from-br(sd) {
|
|
297
|
+
display: none;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
286
301
|
.view-video__popular-models {
|
|
287
302
|
margin-top: 32px;
|
|
288
303
|
}
|
|
@@ -36,10 +36,8 @@
|
|
|
36
36
|
priority
|
|
37
37
|
>
|
|
38
38
|
<template #grid-end>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
hydrate-on-visible
|
|
42
|
-
/>
|
|
39
|
+
<!-- не ленивая: ленивая гидрация пересобирала заполненный сетью iframe, креатив терялся -->
|
|
40
|
+
<AdvNativeBottom v-if="featureFlags.AdsNativeEnabled"/>
|
|
43
41
|
</template>
|
|
44
42
|
|
|
45
43
|
<LazySectionSwiperCards
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CategoriesApiService } from '../../services/api/categories.service';
|
|
2
2
|
import { VideosApiService } from '../../services/api/videos.service';
|
|
3
3
|
import { sortItemsDefault } from '../../lib';
|
|
4
|
-
import type { IBreadcrumbItem, ICardInfo, IChipsItem } from '../../types';
|
|
4
|
+
import type { IBreadcrumbItem, ICardInfo, IChipsItem, INavLinkItem } from '../../types';
|
|
5
5
|
|
|
6
6
|
/** Слаги категорий, у которых не показываем связанные поисковые фразы. */
|
|
7
7
|
const DISALLOWED_SLUGS = ['18', '19'];
|
|
@@ -35,7 +35,11 @@ export async function useCategoryPageData() {
|
|
|
35
35
|
*/
|
|
36
36
|
export function useCategoryPage(
|
|
37
37
|
pageData: Awaited<ReturnType<typeof useCategoryPageData>>,
|
|
38
|
-
options: {
|
|
38
|
+
options: {
|
|
39
|
+
metaKeys?: boolean
|
|
40
|
+
/** Сортировки страницы (по умолчанию `sortItemsDefault`): по ним ключи меты и проверка `?sort` (чужой — 404). */
|
|
41
|
+
sortOptions?: INavLinkItem[]
|
|
42
|
+
} = {},
|
|
39
43
|
) {
|
|
40
44
|
const { categoryInfo, data, status, relatedData } = pageData;
|
|
41
45
|
const { t } = useI18n();
|
|
@@ -52,14 +56,16 @@ export function useCategoryPage(
|
|
|
52
56
|
useStatsPage(() => ({ rotatorID: 'category', categories: slug.value ? [slug.value] : [] }));
|
|
53
57
|
|
|
54
58
|
// с metaKeys `?sort` проверяет useMeta
|
|
55
|
-
|
|
59
|
+
const sortOptions = options.sortOptions || sortItemsDefault;
|
|
60
|
+
|
|
61
|
+
if (!options.metaKeys) useSortGuard(sortOptions);
|
|
56
62
|
|
|
57
63
|
const keysMeta = options.metaKeys
|
|
58
64
|
? useMeta({
|
|
59
65
|
t,
|
|
60
66
|
pageKey: useGetPureRouteName(),
|
|
61
67
|
brandName: useAppConfig().brandName,
|
|
62
|
-
sortOptions
|
|
68
|
+
sortOptions,
|
|
63
69
|
text: title,
|
|
64
70
|
nicheSafeText: title,
|
|
65
71
|
seo: () => ({
|
|
@@ -4,7 +4,7 @@ import type { Language } from '../../runtime';
|
|
|
4
4
|
import { sortItemsDefault } from '../../lib';
|
|
5
5
|
import { ChannelsApiService } from '../../services/api/channels.service';
|
|
6
6
|
import { VideosApiService } from '../../services/api/videos.service';
|
|
7
|
-
import type { IBreadcrumbItem, IChipsItem, IPopularTags } from '../../types';
|
|
7
|
+
import type { IBreadcrumbItem, IChipsItem, IPopularTags, INavLinkItem } from '../../types';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Данные страницы канала: карточка канала, видео, популярные теги. Только запросы — их можно запускать в
|
|
@@ -37,7 +37,12 @@ export async function useChannelPageData() {
|
|
|
37
37
|
*/
|
|
38
38
|
export function useChannelPage(
|
|
39
39
|
pageData: Awaited<ReturnType<typeof useChannelPageData>>,
|
|
40
|
-
options: {
|
|
40
|
+
options: {
|
|
41
|
+
metaSort?: MaybeRefOrGetter<string>
|
|
42
|
+
metaSecondText?: Ref<string>
|
|
43
|
+
/** Сортировки страницы (по умолчанию `sortItemsDefault`): по ним ключи меты и проверка `?sort` (чужой — 404). */
|
|
44
|
+
sortOptions?: INavLinkItem[]
|
|
45
|
+
} = {},
|
|
41
46
|
) {
|
|
42
47
|
const { cardInfo, status, channelTagsData } = pageData;
|
|
43
48
|
const route = useRoute();
|
|
@@ -50,7 +55,7 @@ export function useChannelPage(
|
|
|
50
55
|
t,
|
|
51
56
|
pageKey: useGetPureRouteName(),
|
|
52
57
|
brandName: useAppConfig().brandName,
|
|
53
|
-
sortOptions: sortItemsDefault,
|
|
58
|
+
sortOptions: options.sortOptions || sortItemsDefault,
|
|
54
59
|
sort: options.metaSort,
|
|
55
60
|
text: metaTitle,
|
|
56
61
|
secondText: options.metaSecondText,
|
|
@@ -4,7 +4,7 @@ import type { Language } from '../../runtime';
|
|
|
4
4
|
import { sortItemsDefault } from '../../lib';
|
|
5
5
|
import { ModelsApiService } from '../../services/api/models.service';
|
|
6
6
|
import { VideosApiService } from '../../services/api/videos.service';
|
|
7
|
-
import type { IBreadcrumbItem } from '../../types';
|
|
7
|
+
import type { IBreadcrumbItem, INavLinkItem } from '../../types';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Данные страницы модели: видео и карточка модели. Только запросы — их можно запускать в `Promise.all` вместе
|
|
@@ -34,7 +34,12 @@ export async function useModelPageData() {
|
|
|
34
34
|
*/
|
|
35
35
|
export function useModelPage(
|
|
36
36
|
pageData: Awaited<ReturnType<typeof useModelPageData>>,
|
|
37
|
-
options: {
|
|
37
|
+
options: {
|
|
38
|
+
metaSort?: MaybeRefOrGetter<string>
|
|
39
|
+
metaSecondText?: Ref<string>
|
|
40
|
+
/** Сортировки страницы (по умолчанию `sortItemsDefault`): по ним ключи меты и проверка `?sort` (чужой — 404). */
|
|
41
|
+
sortOptions?: INavLinkItem[]
|
|
42
|
+
} = {},
|
|
38
43
|
) {
|
|
39
44
|
const { status, cardInfo } = pageData;
|
|
40
45
|
const route = useRoute();
|
|
@@ -48,7 +53,7 @@ export function useModelPage(
|
|
|
48
53
|
t,
|
|
49
54
|
pageKey: useGetPureRouteName(),
|
|
50
55
|
brandName: useAppConfig().brandName,
|
|
51
|
-
sortOptions: sortItemsDefault,
|
|
56
|
+
sortOptions: options.sortOptions || sortItemsDefault,
|
|
52
57
|
sort: options.metaSort,
|
|
53
58
|
text: metaTitle,
|
|
54
59
|
secondText: options.metaSecondText,
|
package/package.json
CHANGED
|
@@ -33,10 +33,8 @@
|
|
|
33
33
|
:class="{'_loading': status === 'pending'}"
|
|
34
34
|
>
|
|
35
35
|
<template #grid-end>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
hydrate-on-visible
|
|
39
|
-
/>
|
|
36
|
+
<!-- не ленивая: ленивая гидрация пересобирала заполненный сетью iframe, креатив терялся -->
|
|
37
|
+
<AdvNativeBottom v-if="featureFlags.AdsNativeEnabled"/>
|
|
40
38
|
</template>
|
|
41
39
|
<template #default>
|
|
42
40
|
<UiExpandRow
|
|
@@ -41,10 +41,8 @@
|
|
|
41
41
|
priority
|
|
42
42
|
>
|
|
43
43
|
<template #grid-end>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
hydrate-on-visible
|
|
47
|
-
/>
|
|
44
|
+
<!-- не ленивая: ленивая гидрация пересобирала заполненный сетью iframe, креатив терялся -->
|
|
45
|
+
<AdvNativeBottom v-if="featureFlags.AdsNativeEnabled"/>
|
|
48
46
|
</template>
|
|
49
47
|
<template #default>
|
|
50
48
|
<LazySectionSwiperCards
|
|
@@ -78,10 +78,8 @@
|
|
|
78
78
|
priority
|
|
79
79
|
>
|
|
80
80
|
<template #grid-end>
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
hydrate-on-visible
|
|
84
|
-
/>
|
|
81
|
+
<!-- не ленивая: ленивая гидрация пересобирала заполненный сетью iframe, креатив терялся -->
|
|
82
|
+
<AdvNativeBottom v-if="featureFlags.AdsNativeEnabled"/>
|
|
85
83
|
<UiViewAll
|
|
86
84
|
v-if="videosData.items.length > MAXIMUM_VIDEOS || videosData.total > MAXIMUM_VIDEOS"
|
|
87
85
|
:to="`/search/${slug}/videos`"
|