Add playlist in client
This commit is contained in:
parent
db36a2fb6a
commit
20f9f0f708
2
PeerTube
2
PeerTube
|
@ -1 +1 @@
|
||||||
Subproject commit 6b4359476c462ea178c99b0a04349f553ddb8d9d
|
Subproject commit cffa06fd28bbfb03980bc7d151cfd93130c3daaf
|
|
@ -0,0 +1,145 @@
|
||||||
|
<template>
|
||||||
|
<div class="playlist root-result">
|
||||||
|
|
||||||
|
<div class="thumbnail">
|
||||||
|
<a class="img" :title="watchMessage" target="_blank" rel="nofollow noreferrer noopener" v-bind:href="playlist.url">
|
||||||
|
<img v-bind:src="playlist.thumbnailUrl" alt="">
|
||||||
|
|
||||||
|
<span class="videos-length">{{ videosLengthLabel }}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="information">
|
||||||
|
<h5 class="title">
|
||||||
|
<a :title="watchMessage" target="_blank" rel="nofollow noreferrer noopener" v-bind:href="playlist.url">{{ playlist.displayName }}</a>
|
||||||
|
</h5>
|
||||||
|
|
||||||
|
<div class="description" v-html="renderMarkdown(playlist.description)"></div>
|
||||||
|
|
||||||
|
<div class="metadatas">
|
||||||
|
<div class="by-account">
|
||||||
|
<label v-translate>Created by</label>
|
||||||
|
<actor-miniature type="account" v-bind:actor="playlist.ownerAccount"></actor-miniature>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="by-channel">
|
||||||
|
<label v-translate>In</label>
|
||||||
|
|
||||||
|
<actor-miniature type="channel" v-bind:actor="playlist.videoChannel"></actor-miniature>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="publishedAt">
|
||||||
|
<label v-translate>Updated on</label>
|
||||||
|
|
||||||
|
<div class="value">{{ updateDate }}</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="button">
|
||||||
|
<a class="button-link" target="_blank" rel="nofollow noreferrer noopener" v-bind:href="playlist.url">
|
||||||
|
{{ watchMessage }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '../scss/_variables';
|
||||||
|
|
||||||
|
.playlist {
|
||||||
|
|
||||||
|
.thumbnail {
|
||||||
|
margin-right: 20px;
|
||||||
|
|
||||||
|
--thumbnail-width: #{$thumbnail-width};
|
||||||
|
--thumbnail-height: #{$thumbnail-height};
|
||||||
|
|
||||||
|
// For the videos length overlay
|
||||||
|
.img {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
width: var(--thumbnail-width);
|
||||||
|
height: var(--thumbnail-height);
|
||||||
|
border-radius: 3px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.videos-length {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 10px;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
color: #fff;
|
||||||
|
background-color: rgba(0,0,0,.7);
|
||||||
|
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: $small-view) {
|
||||||
|
--thumbnail-width: calc(100% + 10px);
|
||||||
|
--thumbnail-height: auto;
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue, { PropType } from 'vue'
|
||||||
|
import ActorMiniature from './ActorMiniature.vue'
|
||||||
|
import { VideoPlaylist } from '../../../PeerTube/shared/models'
|
||||||
|
import { renderMarkdown } from '../shared/markdown-render'
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
'actor-miniature': ActorMiniature
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
playlist: Object as PropType<VideoPlaylist>
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
host () {
|
||||||
|
const url = this.playlist.url
|
||||||
|
|
||||||
|
return new URL(url as string).host
|
||||||
|
},
|
||||||
|
|
||||||
|
updateDate () {
|
||||||
|
return new Date(this.playlist.updatedAt).toLocaleDateString()
|
||||||
|
},
|
||||||
|
|
||||||
|
watchMessage () {
|
||||||
|
return this.$gettextInterpolate(this.$gettext('Watch the playlist on %{host}'), { host: this.host })
|
||||||
|
},
|
||||||
|
|
||||||
|
videosLengthLabel () {
|
||||||
|
return this.$gettextInterpolate(this.$gettext('%{videosLength} videos'), { videosLength: this.playlist.videosLength })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
renderMarkdown(markdown: string) {
|
||||||
|
return renderMarkdown(markdown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -163,7 +163,7 @@
|
||||||
return this.video.previewUrl
|
return this.video.previewUrl
|
||||||
},
|
},
|
||||||
renderMarkdown(markdown: string) {
|
renderMarkdown(markdown: string) {
|
||||||
return renderMarkdown(markdown)
|
return renderMarkdown(markdown)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -12,8 +12,8 @@ $small-screen: 700px;
|
||||||
$primary: $orange;
|
$primary: $orange;
|
||||||
$secondary: $grey;
|
$secondary: $grey;
|
||||||
|
|
||||||
$thumbnail-width: 223px;
|
$thumbnail-width: 280px;
|
||||||
$thumbnail-height: 122px;
|
$thumbnail-height: 153px;
|
||||||
|
|
||||||
$small-font-size: 12px;
|
$small-font-size: 12px;
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ for (const rule of TEXT_RULES) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function renderMarkdown(markdown: string) {
|
export function renderMarkdown(markdown: string) {
|
||||||
let html = markdownIt.render(markdown);
|
if (!markdown) return ''
|
||||||
return html;
|
|
||||||
|
return markdownIt.render(markdown)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import { VideoPlaylistsSearchQuery } from '../../../PeerTube/shared/models'
|
||||||
import { ResultList } from '../../../PeerTube/shared/models/result-list.model'
|
import { ResultList } from '../../../PeerTube/shared/models/result-list.model'
|
||||||
import { VideoChannelsSearchQuery } from '../../../PeerTube/shared/models/search/video-channels-search-query.model'
|
import { VideoChannelsSearchQuery } from '../../../PeerTube/shared/models/search/video-channels-search-query.model'
|
||||||
import { VideosSearchQuery } from '../../../PeerTube/shared/models/search/videos-search-query.model'
|
import { VideosSearchQuery } from '../../../PeerTube/shared/models/search/videos-search-query.model'
|
||||||
import { EnhancedVideoChannel } from '../../../server/types/channel.model'
|
import { EnhancedVideoChannel } from '../../../server/types/channel.model'
|
||||||
import { EnhancedVideo } from '../../../server/types/video.model'
|
import { EnhancedVideo } from '../../../server/types/video.model'
|
||||||
|
import { EnhancedPlaylist } from '../../../server/types/playlist.model'
|
||||||
import { buildApiUrl } from './utils'
|
import { buildApiUrl } from './utils'
|
||||||
|
|
||||||
const baseVideosPath = '/api/v1/search/videos'
|
const baseVideosPath = '/api/v1/search/videos'
|
||||||
const baseVideoChannelsPath = '/api/v1/search/video-channels'
|
const baseVideoChannelsPath = '/api/v1/search/video-channels'
|
||||||
|
const baseVideoPlaylistsPath = '/api/v1/search/video-playlists'
|
||||||
|
|
||||||
function searchVideos (params: VideosSearchQuery) {
|
function searchVideos (params: VideosSearchQuery) {
|
||||||
const options = {
|
const options = {
|
||||||
|
@ -31,7 +34,19 @@ function searchVideoChannels (params: VideoChannelsSearchQuery) {
|
||||||
.then(res => res.data)
|
.then(res => res.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function searchVideoPlaylists (params: VideoPlaylistsSearchQuery) {
|
||||||
|
const options = {
|
||||||
|
params
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.search) Object.assign(options.params, { search: params.search })
|
||||||
|
|
||||||
|
return axios.get<ResultList<EnhancedPlaylist>>(buildApiUrl(baseVideoPlaylistsPath), options)
|
||||||
|
.then(res => res.data)
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
searchVideos,
|
searchVideos,
|
||||||
searchVideoChannels
|
searchVideoChannels,
|
||||||
|
searchVideoPlaylists
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<h3 v-if="!searchDone" v-bind:class="{ 'none-opacity': !instancesCount }" v-translate="{ instancesCount: instancesCount, indexedInstancesUrl: indexedInstancesUrl, indexName: indexName }">
|
<h3 v-if="!searchDone" v-bind:class="{ 'none-opacity': !instancesCount }" v-translate="{ instancesCount: instancesCount, indexedInstancesUrl: indexedInstancesUrl, indexName: indexName }">
|
||||||
Search for your favorite videos and channels on <a href="%{indexedInstancesUrl}" target="_blank">%{instancesCount} PeerTube websites</a> indexed by %{indexName}!
|
Search for your favorite videos, channels and playlists on <a href="%{indexedInstancesUrl}" target="_blank">%{instancesCount} PeerTube websites</a> indexed by %{indexName}!
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<search-warning class="search-warning" v-bind:indexName="indexName" v-bind:highlight="!searchDone"></search-warning>
|
<search-warning class="search-warning" v-bind:indexName="indexName" v-bind:highlight="!searchDone"></search-warning>
|
||||||
|
@ -213,7 +213,9 @@
|
||||||
<div v-for="result in results" :key="getResultKey(result)">
|
<div v-for="result in results" :key="getResultKey(result)">
|
||||||
<video-result v-if="isVideo(result)" v-bind:video="result"></video-result>
|
<video-result v-if="isVideo(result)" v-bind:video="result"></video-result>
|
||||||
|
|
||||||
<channel-result v-else v-bind:channel="result"></channel-result>
|
<channel-result v-else-if="isChannel(result)" v-bind:channel="result"></channel-result>
|
||||||
|
|
||||||
|
<playlist-result v-else v-bind:playlist="result"></playlist-result>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<pagination v-bind:maxPage="getMaxPage()" v-bind:searchDone="searchDone" v-model="currentPage" v-bind:pages="pages"></pagination>
|
<pagination v-bind:maxPage="getMaxPage()" v-bind:searchDone="searchDone" v-model="currentPage" v-bind:pages="pages"></pagination>
|
||||||
|
@ -442,7 +444,8 @@
|
||||||
import SearchWarning from '../components/SearchWarning.vue'
|
import SearchWarning from '../components/SearchWarning.vue'
|
||||||
import VideoResult from '../components/VideoResult.vue'
|
import VideoResult from '../components/VideoResult.vue'
|
||||||
import ChannelResult from '../components/ChannelResult.vue'
|
import ChannelResult from '../components/ChannelResult.vue'
|
||||||
import { searchVideos, searchVideoChannels } from '../shared/search'
|
import PlaylistResult from '../components/PlaylistResult.vue'
|
||||||
|
import { searchVideos, searchVideoChannels, searchVideoPlaylists } from '../shared/search'
|
||||||
import { getConfig } from '../shared/config'
|
import { getConfig } from '../shared/config'
|
||||||
import { pageToAPIParams, durationRangeToAPIParams, publishedDateRangeToAPIParams, extractTagsFromQuery, buildApiUrl } from '../shared/utils'
|
import { pageToAPIParams, durationRangeToAPIParams, publishedDateRangeToAPIParams, extractTagsFromQuery, buildApiUrl } from '../shared/utils'
|
||||||
import { SearchUrl } from '../models'
|
import { SearchUrl } from '../models'
|
||||||
|
@ -450,9 +453,10 @@
|
||||||
import { EnhancedVideoChannel } from '../../../server/types/channel.model'
|
import { EnhancedVideoChannel } from '../../../server/types/channel.model'
|
||||||
import VueTagsInput from '@johmun/vue-tags-input'
|
import VueTagsInput from '@johmun/vue-tags-input'
|
||||||
import Pagination from '../components/Pagination.vue'
|
import Pagination from '../components/Pagination.vue'
|
||||||
import { VideoChannelsSearchQuery, ResultList } from '../../../PeerTube/shared/models'
|
import { VideoChannelsSearchQuery, ResultList, VideosSearchQuery } from '../../../PeerTube/shared/models'
|
||||||
import Nprogress from 'nprogress'
|
import Nprogress from 'nprogress'
|
||||||
import { VideosSearchQuery } from '../../../server/types/video-search.model'
|
import { EnhancedPlaylist } from '../../../server/types/playlist.model'
|
||||||
|
import { PlaylistsSearchQuery } from '../../../server/types/search-query/playlist-search.model'
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
components: {
|
components: {
|
||||||
|
@ -460,6 +464,7 @@
|
||||||
'search-warning': SearchWarning,
|
'search-warning': SearchWarning,
|
||||||
'video-result': VideoResult,
|
'video-result': VideoResult,
|
||||||
'channel-result': ChannelResult,
|
'channel-result': ChannelResult,
|
||||||
|
'playlist-result': PlaylistResult,
|
||||||
'vue-tags-input': VueTagsInput,
|
'vue-tags-input': VueTagsInput,
|
||||||
'pagination': Pagination
|
'pagination': Pagination
|
||||||
},
|
},
|
||||||
|
@ -471,11 +476,12 @@
|
||||||
indexName: '',
|
indexName: '',
|
||||||
instancesCount: 0,
|
instancesCount: 0,
|
||||||
indexedInstancesUrl: '',
|
indexedInstancesUrl: '',
|
||||||
results: [] as (EnhancedVideo | EnhancedVideoChannel)[],
|
results: [] as (EnhancedVideo | EnhancedVideoChannel | EnhancedPlaylist)[],
|
||||||
|
|
||||||
resultsCount: null as number,
|
resultsCount: null as number,
|
||||||
channelsCount: null as number,
|
channelsCount: null as number,
|
||||||
videosCount: null as number,
|
videosCount: null as number,
|
||||||
|
playlistsCount: null as number,
|
||||||
|
|
||||||
searchedTerm: '',
|
searchedTerm: '',
|
||||||
|
|
||||||
|
@ -483,6 +489,7 @@
|
||||||
pages: [],
|
pages: [],
|
||||||
resultsPerVideosPage: 10,
|
resultsPerVideosPage: 10,
|
||||||
resultsPerChannelsPage: 3,
|
resultsPerChannelsPage: 3,
|
||||||
|
resultsPerPlaylistsPage: 3,
|
||||||
|
|
||||||
displayFilters: false,
|
displayFilters: false,
|
||||||
oldQuery: '',
|
oldQuery: '',
|
||||||
|
@ -553,7 +560,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
inputPlaceholder () {
|
inputPlaceholder () {
|
||||||
return this.$gettext('Keyword, channel, video, etc.')
|
return this.$gettext('Keyword, channel, video, playlist, etc.')
|
||||||
},
|
},
|
||||||
|
|
||||||
tagsPlaceholder () {
|
tagsPlaceholder () {
|
||||||
|
@ -691,6 +698,7 @@
|
||||||
this.currentPage = 1
|
this.currentPage = 1
|
||||||
this.channelsCount = null
|
this.channelsCount = null
|
||||||
this.videosCount = null
|
this.videosCount = null
|
||||||
|
this.playlistsCount = null
|
||||||
this.resultsCount = null
|
this.resultsCount = null
|
||||||
|
|
||||||
this.updateUrl()
|
this.updateUrl()
|
||||||
|
@ -705,9 +713,10 @@
|
||||||
Nprogress.start()
|
Nprogress.start()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [ videosResult, channelsResult ] = await Promise.all([
|
const [ videosResult, channelsResult, playlistsResult ] = await Promise.all([
|
||||||
this.searchVideos(),
|
this.searchVideos(),
|
||||||
this.searchChannels()
|
this.searchChannels(),
|
||||||
|
this.searchPlaylists()
|
||||||
])
|
])
|
||||||
|
|
||||||
Nprogress.done()
|
Nprogress.done()
|
||||||
|
@ -716,9 +725,11 @@
|
||||||
|
|
||||||
this.channelsCount = channelsResult.total
|
this.channelsCount = channelsResult.total
|
||||||
this.videosCount = videosResult.total
|
this.videosCount = videosResult.total
|
||||||
|
this.playlistsCount = playlistsResult.total
|
||||||
|
|
||||||
this.resultsCount = videosResult.total + channelsResult.total
|
this.resultsCount = videosResult.total + channelsResult.total + playlistsResult.total
|
||||||
this.results = channelsResult.data.concat(videosResult.data)
|
this.results = channelsResult.data.concat(playlistsResult.data)
|
||||||
|
.concat(videosResult.data)
|
||||||
|
|
||||||
if (this.formSort === '-match') {
|
if (this.formSort === '-match') {
|
||||||
this.results.sort((r1, r2) => {
|
this.results.sort((r1, r2) => {
|
||||||
|
@ -739,16 +750,30 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
isVideo (result: EnhancedVideo | EnhancedVideoChannel) {
|
isVideo (result: EnhancedVideo | EnhancedVideoChannel | EnhancedPlaylist) {
|
||||||
if ((result as EnhancedVideo).language) return true
|
if ((result as EnhancedVideo).language) return true
|
||||||
|
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
|
|
||||||
getResultKey (result: EnhancedVideo | EnhancedVideoChannel) {
|
isPlaylist (result: EnhancedVideo | EnhancedVideoChannel | EnhancedPlaylist) {
|
||||||
if (this.isVideo(result)) return (result as EnhancedVideo).uuid
|
if ((result as EnhancedPlaylist).videosLength !== undefined) return true
|
||||||
|
|
||||||
return result.id + (result as EnhancedVideoChannel).host
|
return false
|
||||||
|
},
|
||||||
|
|
||||||
|
isChannel (result: EnhancedVideo | EnhancedVideoChannel | EnhancedPlaylist) {
|
||||||
|
if ((result as EnhancedVideoChannel).followingCount !== undefined) return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
|
||||||
|
getResultKey (result: EnhancedVideo | EnhancedVideoChannel | EnhancedPlaylist) {
|
||||||
|
if (this.isVideo(result)) return (result as EnhancedVideo).uuid
|
||||||
|
if (this.isChannel(result)) return result.id + (result as EnhancedVideoChannel).host
|
||||||
|
if (this.isPlaylist(result)) return (result as EnhancedPlaylist).uuid
|
||||||
|
|
||||||
|
throw new Error('Unknown result')
|
||||||
},
|
},
|
||||||
|
|
||||||
updateUrl () {
|
updateUrl () {
|
||||||
|
@ -864,6 +889,22 @@
|
||||||
} as VideoChannelsSearchQuery
|
} as VideoChannelsSearchQuery
|
||||||
},
|
},
|
||||||
|
|
||||||
|
buildPlaylistSearchQuery () {
|
||||||
|
const { start, count } = pageToAPIParams(this.currentPage, this.resultsPerChannelsPage)
|
||||||
|
|
||||||
|
let sort: string
|
||||||
|
if (this.formSort === '-matched') sort = '-matched'
|
||||||
|
else if (this.formSort === '-publishedAt') sort = '-createdAt'
|
||||||
|
else if (this.formSort === 'publishedAt') sort = 'createdAt'
|
||||||
|
|
||||||
|
return {
|
||||||
|
search: this.formSearch,
|
||||||
|
start,
|
||||||
|
sort,
|
||||||
|
count
|
||||||
|
} as PlaylistsSearchQuery
|
||||||
|
},
|
||||||
|
|
||||||
searchVideos (): Promise<ResultList<EnhancedVideo>> {
|
searchVideos (): Promise<ResultList<EnhancedVideo>> {
|
||||||
if (!this.formSearch) {
|
if (!this.formSearch) {
|
||||||
return Promise.resolve({ data: [], total: 0 })
|
return Promise.resolve({ data: [], total: 0 })
|
||||||
|
@ -879,7 +920,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
searchChannels (): Promise<ResultList<EnhancedVideoChannel>> {
|
searchChannels (): Promise<ResultList<EnhancedVideoChannel>> {
|
||||||
if (!this.formSearch || this.isChannelSearchDisabled()) {
|
if (!this.formSearch || this.isChannelOrPlaylistSearchDisabled()) {
|
||||||
return Promise.resolve({ data: [], total: 0 })
|
return Promise.resolve({ data: [], total: 0 })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -892,6 +933,20 @@
|
||||||
return searchVideoChannels(query)
|
return searchVideoChannels(query)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
searchPlaylists (): Promise<ResultList<EnhancedPlaylist>> {
|
||||||
|
if (!this.formSearch || this.isChannelOrPlaylistSearchDisabled()) {
|
||||||
|
return Promise.resolve({ data: [], total: 0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.hasStillPlaylistsResult()) {
|
||||||
|
return Promise.resolve({ data: [], total: this.channelsCount })
|
||||||
|
}
|
||||||
|
|
||||||
|
const query = this.buildChannelSearchQuery()
|
||||||
|
|
||||||
|
return searchVideoPlaylists(query)
|
||||||
|
},
|
||||||
|
|
||||||
hasStillChannelsResult () {
|
hasStillChannelsResult () {
|
||||||
// Not searched yet
|
// Not searched yet
|
||||||
if (this.channelsCount === null) return true
|
if (this.channelsCount === null) return true
|
||||||
|
@ -899,6 +954,13 @@
|
||||||
return this.getChannelsMaxPage() >= this.currentPage
|
return this.getChannelsMaxPage() >= this.currentPage
|
||||||
},
|
},
|
||||||
|
|
||||||
|
hasStillPlaylistsResult () {
|
||||||
|
// Not searched yet
|
||||||
|
if (this.playlistsCount === null) return true
|
||||||
|
|
||||||
|
return this.getPlaylistsMaxPage() >= this.currentPage
|
||||||
|
},
|
||||||
|
|
||||||
hasStillMoreVideosResults () {
|
hasStillMoreVideosResults () {
|
||||||
// Not searched yet
|
// Not searched yet
|
||||||
if (this.videosCount === null) return true
|
if (this.videosCount === null) return true
|
||||||
|
@ -910,13 +972,17 @@
|
||||||
return Math.ceil(this.channelsCount / this.resultsPerChannelsPage)
|
return Math.ceil(this.channelsCount / this.resultsPerChannelsPage)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getPlaylistsMaxPage () {
|
||||||
|
return Math.ceil(this.playlistsCount / this.resultsPerPlaylistsPage)
|
||||||
|
},
|
||||||
|
|
||||||
getVideosMaxPage () {
|
getVideosMaxPage () {
|
||||||
return Math.ceil(this.videosCount / this.resultsPerVideosPage)
|
return Math.ceil(this.videosCount / this.resultsPerVideosPage)
|
||||||
},
|
},
|
||||||
|
|
||||||
getMaxPage () {
|
getMaxPage () {
|
||||||
// Limit to 10 pages
|
// Limit to 10 pages
|
||||||
return Math.min(10, Math.max(this.getChannelsMaxPage(), this.getVideosMaxPage()))
|
return Math.min(10, Math.max(this.getPlaylistsMaxPage(), this.getChannelsMaxPage(), this.getVideosMaxPage()))
|
||||||
},
|
},
|
||||||
|
|
||||||
buildPages () {
|
buildPages () {
|
||||||
|
@ -964,7 +1030,7 @@
|
||||||
return count
|
return count
|
||||||
},
|
},
|
||||||
|
|
||||||
isChannelSearchDisabled () {
|
isChannelOrPlaylistSearchDisabled () {
|
||||||
return this.countActiveFilters() > 0
|
return this.countActiveFilters() > 0
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ playlists-search:
|
||||||
# See https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html for more information
|
# See https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-multi-match-query.html for more information
|
||||||
# If boost == 0, the field will not be part of the search
|
# If boost == 0, the field will not be part of the search
|
||||||
search-fields:
|
search-fields:
|
||||||
name:
|
display-name:
|
||||||
boost: 5
|
boost: 5
|
||||||
description:
|
description:
|
||||||
boost: 1
|
boost: 1
|
||||||
|
|
|
@ -2,6 +2,7 @@ elastic-search:
|
||||||
indexes:
|
indexes:
|
||||||
videos: 'peertube-index-videos-test1'
|
videos: 'peertube-index-videos-test1'
|
||||||
channels: 'peertube-index-channels-test1'
|
channels: 'peertube-index-channels-test1'
|
||||||
|
playlists: 'peertube-index-playlists-test1'
|
||||||
|
|
||||||
search-instance:
|
search-instance:
|
||||||
name_image: '/theme/framasoft/img/title.svg'
|
name_image: '/theme/framasoft/img/title.svg'
|
||||||
|
|
|
@ -89,9 +89,9 @@ const CONFIG = {
|
||||||
},
|
},
|
||||||
PLAYLISTS_SEARCH: {
|
PLAYLISTS_SEARCH: {
|
||||||
SEARCH_FIELDS: {
|
SEARCH_FIELDS: {
|
||||||
NAME: {
|
DISPLAY_NAME: {
|
||||||
FIELD_NAME: 'name',
|
FIELD_NAME: 'displayName',
|
||||||
BOOST: config.get<number>('playlists-search.search-fields.name.boost')
|
BOOST: config.get<number>('playlists-search.search-fields.display-name.boost')
|
||||||
},
|
},
|
||||||
DESCRIPTION: {
|
DESCRIPTION: {
|
||||||
FIELD_NAME: 'description',
|
FIELD_NAME: 'description',
|
||||||
|
@ -124,14 +124,13 @@ const SORTABLE_COLUMNS = {
|
||||||
const PAGINATION_COUNT_DEFAULT = 20
|
const PAGINATION_COUNT_DEFAULT = 20
|
||||||
|
|
||||||
const SCHEDULER_INTERVALS_MS = {
|
const SCHEDULER_INTERVALS_MS = {
|
||||||
videosIndexer: 60000 * 60 * 24 // 24 hours
|
indexation: 60000 * 60 * 24 // 24 hours
|
||||||
}
|
}
|
||||||
|
|
||||||
const INDEXER_COUNT = 10
|
const INDEXER_COUNT = 10
|
||||||
const INDEXER_LIMIT = 500000
|
const INDEXER_LIMIT = 500000
|
||||||
|
|
||||||
const INDEXER_CONCURRENCY = 3
|
const INDEXER_HOST_CONCURRENCY = 3
|
||||||
|
|
||||||
const INDEXER_QUEUE_CONCURRENCY = 3
|
const INDEXER_QUEUE_CONCURRENCY = 3
|
||||||
|
|
||||||
const REQUESTS = {
|
const REQUESTS = {
|
||||||
|
@ -167,7 +166,7 @@ function buildMultiMatchFields (fields: { [name: string]: { BOOST: number, FIELD
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTestInstance()) {
|
if (isTestInstance()) {
|
||||||
SCHEDULER_INTERVALS_MS.videosIndexer = 1000 * 60 * 5 // 5 minutes
|
SCHEDULER_INTERVALS_MS.indexation = 1000 * 60 * 5 // 5 minutes
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -179,7 +178,7 @@ export {
|
||||||
SORTABLE_COLUMNS,
|
SORTABLE_COLUMNS,
|
||||||
INDEXER_QUEUE_CONCURRENCY,
|
INDEXER_QUEUE_CONCURRENCY,
|
||||||
SCHEDULER_INTERVALS_MS,
|
SCHEDULER_INTERVALS_MS,
|
||||||
INDEXER_CONCURRENCY,
|
INDEXER_HOST_CONCURRENCY,
|
||||||
INDEXER_COUNT,
|
INDEXER_COUNT,
|
||||||
INDEXER_LIMIT,
|
INDEXER_LIMIT,
|
||||||
REQUESTS,
|
REQUESTS,
|
||||||
|
|
|
@ -2,7 +2,7 @@ import * as Bluebird from 'bluebird'
|
||||||
import { IndexablePlaylist } from 'server/types/playlist.model'
|
import { IndexablePlaylist } from 'server/types/playlist.model'
|
||||||
import { inspect } from 'util'
|
import { inspect } from 'util'
|
||||||
import { logger } from '../../helpers/logger'
|
import { logger } from '../../helpers/logger'
|
||||||
import { INDEXER_CONCURRENCY, INDEXER_COUNT, INDEXER_LIMIT, SCHEDULER_INTERVALS_MS } from '../../initializers/constants'
|
import { INDEXER_HOST_CONCURRENCY, INDEXER_COUNT, INDEXER_LIMIT, SCHEDULER_INTERVALS_MS } from '../../initializers/constants'
|
||||||
import { IndexableVideo } from '../../types/video.model'
|
import { IndexableVideo } from '../../types/video.model'
|
||||||
import { buildInstanceHosts } from '../elastic-search/elastic-search-instances'
|
import { buildInstanceHosts } from '../elastic-search/elastic-search-instances'
|
||||||
import { ChannelIndexer } from '../indexers/channel-indexer'
|
import { ChannelIndexer } from '../indexers/channel-indexer'
|
||||||
|
@ -15,7 +15,7 @@ export class IndexationScheduler extends AbstractScheduler {
|
||||||
|
|
||||||
private static instance: IndexationScheduler
|
private static instance: IndexationScheduler
|
||||||
|
|
||||||
protected schedulerIntervalMs = SCHEDULER_INTERVALS_MS.videosIndexer
|
protected schedulerIntervalMs = SCHEDULER_INTERVALS_MS.indexation
|
||||||
|
|
||||||
private indexedHosts: string[] = []
|
private indexedHosts: string[] = []
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ export class IndexationScheduler extends AbstractScheduler {
|
||||||
console.error(inspect(err, { depth: 10 }))
|
console.error(inspect(err, { depth: 10 }))
|
||||||
logger.warn({ err: inspect(err) }, 'Cannot index videos from %s.', host)
|
logger.warn({ err: inspect(err) }, 'Cannot index videos from %s.', host)
|
||||||
}
|
}
|
||||||
}, { concurrency: INDEXER_CONCURRENCY })
|
}, { concurrency: INDEXER_HOST_CONCURRENCY })
|
||||||
|
|
||||||
for (const o of this.indexers) {
|
for (const o of this.indexers) {
|
||||||
await o.refreshIndex()
|
await o.refreshIndex()
|
||||||
|
@ -91,10 +91,10 @@ export class IndexationScheduler extends AbstractScheduler {
|
||||||
logger.debug('Getting video results from %s (from = %d).', host, start)
|
logger.debug('Getting video results from %s (from = %d).', host, start)
|
||||||
|
|
||||||
videos = await getVideos(host, start)
|
videos = await getVideos(host, start)
|
||||||
start += videos.length
|
|
||||||
|
|
||||||
logger.debug('Got %d video results from %s (from = %d).', videos.length, host, start)
|
logger.debug('Got %d video results from %s (from = %d).', videos.length, host, start)
|
||||||
|
|
||||||
|
start += videos.length
|
||||||
|
|
||||||
if (videos.length !== 0) {
|
if (videos.length !== 0) {
|
||||||
const { created } = await this.videoIndexer.indexElements(videos)
|
const { created } = await this.videoIndexer.indexElements(videos)
|
||||||
|
|
||||||
|
@ -139,10 +139,10 @@ export class IndexationScheduler extends AbstractScheduler {
|
||||||
logger.debug('Getting playlist results from %s (from = %d, channelHandle = %s).', host, start, channelHandle)
|
logger.debug('Getting playlist results from %s (from = %d, channelHandle = %s).', host, start, channelHandle)
|
||||||
|
|
||||||
playlists = await getPlaylistsOf(host, channelHandle, start)
|
playlists = await getPlaylistsOf(host, channelHandle, start)
|
||||||
start += playlists.length
|
|
||||||
|
|
||||||
logger.debug('Got %d playlist results from %s (from = %d, channelHandle = %s).', playlists.length, host, start, channelHandle)
|
logger.debug('Got %d playlist results from %s (from = %d, channelHandle = %s).', playlists.length, host, start, channelHandle)
|
||||||
|
|
||||||
|
start += playlists.length
|
||||||
|
|
||||||
if (playlists.length !== 0) {
|
if (playlists.length !== 0) {
|
||||||
await this.playlistIndexer.indexElements(playlists)
|
await this.playlistIndexer.indexElements(playlists)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue