Fix anonymous nsfw policy

This commit is contained in:
Chocobozzz 2020-06-16 11:00:35 +02:00
parent 64e0f8cf12
commit 5c20a45518
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
23 changed files with 232 additions and 165 deletions

View File

@ -21,7 +21,7 @@
<my-video-miniature <my-video-miniature
*ngFor="let video of getVideosOf(videoChannel)" *ngFor="let video of getVideosOf(videoChannel)"
[video]="video" [user]="user" [displayVideoActions]="true" [video]="video" [user]="userMiniature" [displayVideoActions]="true"
></my-video-miniature> ></my-video-miniature>
</div> </div>

View File

@ -1,17 +1,17 @@
import { from, Subject, Subscription } from 'rxjs'
import { concatMap, map, switchMap, tap } from 'rxjs/operators'
import { Component, OnDestroy, OnInit } from '@angular/core' import { Component, OnDestroy, OnInit } from '@angular/core'
import { ActivatedRoute } from '@angular/router' import { UserService } from '@app/shared'
import { Account } from '@app/shared/account/account.model' import { Account } from '@app/shared/account/account.model'
import { AccountService } from '@app/shared/account/account.service' import { AccountService } from '@app/shared/account/account.service'
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
import { concatMap, map, switchMap, tap } from 'rxjs/operators'
import { from, Subject, Subscription } from 'rxjs'
import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
import { Video } from '@app/shared/video/video.model'
import { AuthService } from '@app/core'
import { VideoService } from '@app/shared/video/video.service'
import { VideoSortField } from '@app/shared/video/sort-field.type'
import { ComponentPagination, hasMoreItems } from '@app/shared/rest/component-pagination.model'
import { ScreenService } from '@app/shared/misc/screen.service' import { ScreenService } from '@app/shared/misc/screen.service'
import { ComponentPagination, hasMoreItems } from '@app/shared/rest/component-pagination.model'
import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
import { VideoSortField } from '@app/shared/video/sort-field.type'
import { Video } from '@app/shared/video/video.model'
import { VideoService } from '@app/shared/video/video.service'
import { User } from '@shared/models'
@Component({ @Component({
selector: 'my-account-video-channels', selector: 'my-account-video-channels',
@ -38,21 +38,18 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy {
onChannelDataSubject = new Subject<any>() onChannelDataSubject = new Subject<any>()
userMiniature: User
private accountSub: Subscription private accountSub: Subscription
constructor ( constructor (
private route: ActivatedRoute,
private authService: AuthService,
private accountService: AccountService, private accountService: AccountService,
private videoChannelService: VideoChannelService, private videoChannelService: VideoChannelService,
private videoService: VideoService, private videoService: VideoService,
private screenService: ScreenService private screenService: ScreenService,
private userService: UserService
) { } ) { }
get user () {
return this.authService.getUser()
}
ngOnInit () { ngOnInit () {
// Parent get the account for us // Parent get the account for us
this.accountSub = this.accountService.accountLoaded this.accountSub = this.accountService.accountLoaded
@ -61,6 +58,9 @@ export class AccountVideoChannelsComponent implements OnInit, OnDestroy {
this.loadMoreChannels() this.loadMoreChannels()
}) })
this.userService.getAnonymousOrLoggedUser()
.subscribe(user => this.userMiniature = user)
} }
ngOnDestroy () { ngOnDestroy () {

View File

@ -18,6 +18,7 @@
<div class="video" *ngFor="let video of videos"> <div class="video" *ngFor="let video of videos">
<my-video-miniature <my-video-miniature
[video]="video" [displayAsRow]="true" [video]="video" [displayAsRow]="true"
(videoRemoved)="removeVideoFromArray(video)" (videoBlocked)="removeVideoFromArray(video)"></my-video-miniature> (videoRemoved)="removeVideoFromArray(video)" (videoBlocked)="removeVideoFromArray(video)"
></my-video-miniature>
</div> </div>
</div> </div>

View File

@ -77,7 +77,7 @@ export class VideoChannelVideosComponent extends AbstractVideoList implements On
const newPagination = immutableAssign(this.pagination, { currentPage: page }) const newPagination = immutableAssign(this.pagination, { currentPage: page })
return this.videoService return this.videoService
.getVideoChannelVideos(this.videoChannel, newPagination, this.sort) .getVideoChannelVideos(this.videoChannel, newPagination, this.sort, this.nsfwPolicy)
.pipe( .pipe(
tap(({ total }) => { tap(({ total }) => {
this.titlePage = this.i18n(`{total, plural, =1 {Published 1 video} other {Published {{total}} videos}}`, { total }) this.titlePage = this.i18n(`{total, plural, =1 {Published 1 video} other {Published {{total}} videos}}`, { total })

View File

@ -123,7 +123,6 @@ export class AppComponent implements OnInit, AfterViewInit {
const scrollEvent = eventsObs.pipe(filter((e: Event): e is Scroll => e instanceof Scroll)) const scrollEvent = eventsObs.pipe(filter((e: Event): e is Scroll => e instanceof Scroll))
scrollEvent.subscribe(e => { scrollEvent.subscribe(e => {
console.log(e)
if (e.position) { if (e.position) {
return this.viewportScroller.scrollToPosition(e.position) return this.viewportScroller.scrollToPosition(e.position)
} }

View File

@ -7,7 +7,11 @@
<div class="modal-body"> <div class="modal-body">
<div i18n class="mb-4 quick-settings-title">Display settings</div> <div i18n class="mb-4 quick-settings-title">Display settings</div>
<my-account-video-settings *ngIf="!isUserLoggedIn()" [user]="user" [userInformationLoaded]="userInformationLoaded" [reactiveUpdate]="true" [notifyOnUpdate]="true"> <my-account-video-settings
*ngIf="!isUserLoggedIn()"
[user]="user" [userInformationLoaded]="userInformationLoaded" [reactiveUpdate]="true" [notifyOnUpdate]="true"
>
<ng-container ngProjectAs="inner-title"> <ng-container ngProjectAs="inner-title">
<div i18n class="mb-4 mt-4 quick-settings-title">Video settings</div> <div i18n class="mb-4 mt-4 quick-settings-title">Video settings</div>
</ng-container> </ng-container>
@ -15,6 +19,9 @@
<div i18n class="mb-4 mt-4 quick-settings-title">Interface settings</div> <div i18n class="mb-4 mt-4 quick-settings-title">Interface settings</div>
<my-account-interface-settings *ngIf="!isUserLoggedIn()" [user]="user" [userInformationLoaded]="userInformationLoaded" [reactiveUpdate]="true" [notifyOnUpdate]="true"></my-account-interface-settings> <my-account-interface-settings
*ngIf="!isUserLoggedIn()"
[user]="user" [userInformationLoaded]="userInformationLoaded" [reactiveUpdate]="true" [notifyOnUpdate]="true"
></my-account-interface-settings>
</div> </div>
</ng-template> </ng-template>

View File

@ -32,9 +32,11 @@ export class QuickSettingsModalComponent extends FormReactive implements OnInit
ngOnInit () { ngOnInit () {
this.user = this.userService.getAnonymousUser() this.user = this.userService.getAnonymousUser()
this.localStorageService.watch().subscribe( this.localStorageService.watch()
() => this.user = this.userService.getAnonymousUser() .subscribe(
) () => this.user = this.userService.getAnonymousUser()
)
this.userInformationLoaded.next(true) this.userInformationLoaded.next(true)
this.authService.loginChangedSource this.authService.loginChangedSource

View File

@ -53,7 +53,7 @@
<div *ngIf="isVideo(result)" class="entry video"> <div *ngIf="isVideo(result)" class="entry video">
<my-video-miniature <my-video-miniature
[video]="result" [user]="user" [displayAsRow]="true" [displayVideoActions]="!hideActions()" [video]="result" [user]="userMiniature" [displayAsRow]="true" [displayVideoActions]="!hideActions()"
[displayOptions]="videoDisplayOptions" [useLazyLoadUrl]="advancedSearch.searchTarget === 'search-index'" [displayOptions]="videoDisplayOptions" [useLazyLoadUrl]="advancedSearch.searchTarget === 'search-index'"
(videoBlocked)="removeVideoFromArray(result)" (videoRemoved)="removeVideoFromArray(result)" (videoBlocked)="removeVideoFromArray(result)" (videoRemoved)="removeVideoFromArray(result)"
></my-video-miniature> ></my-video-miniature>

View File

@ -5,6 +5,7 @@ import { AuthService, Notifier, ServerService } from '@app/core'
import { HooksService } from '@app/core/plugins/hooks.service' import { HooksService } from '@app/core/plugins/hooks.service'
import { AdvancedSearch } from '@app/search/advanced-search.model' import { AdvancedSearch } from '@app/search/advanced-search.model'
import { SearchService } from '@app/search/search.service' import { SearchService } from '@app/search/search.service'
import { UserService } from '@app/shared'
import { immutableAssign } from '@app/shared/misc/utils' import { immutableAssign } from '@app/shared/misc/utils'
import { ComponentPagination } from '@app/shared/rest/component-pagination.model' import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
import { VideoChannel } from '@app/shared/video-channel/video-channel.model' import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
@ -12,7 +13,7 @@ import { MiniatureDisplayOptions } from '@app/shared/video/video-miniature.compo
import { Video } from '@app/shared/video/video.model' import { Video } from '@app/shared/video/video.model'
import { MetaService } from '@ngx-meta/core' import { MetaService } from '@ngx-meta/core'
import { I18n } from '@ngx-translate/i18n-polyfill' import { I18n } from '@ngx-translate/i18n-polyfill'
import { ServerConfig } from '@shared/models' import { ServerConfig, User } from '@shared/models'
import { SearchTargetType } from '@shared/models/search/search-target-query.model' import { SearchTargetType } from '@shared/models/search/search-target-query.model'
@Component({ @Component({
@ -46,6 +47,8 @@ export class SearchComponent implements OnInit, OnDestroy {
errorMessage: string errorMessage: string
serverConfig: ServerConfig serverConfig: ServerConfig
userMiniature: User
private subActivatedRoute: Subscription private subActivatedRoute: Subscription
private isInitialLoad = false // set to false to show the search filters on first arrival private isInitialLoad = false // set to false to show the search filters on first arrival
private firstSearch = true private firstSearch = true
@ -62,14 +65,11 @@ export class SearchComponent implements OnInit, OnDestroy {
private notifier: Notifier, private notifier: Notifier,
private searchService: SearchService, private searchService: SearchService,
private authService: AuthService, private authService: AuthService,
private userService: UserService,
private hooks: HooksService, private hooks: HooksService,
private serverService: ServerService private serverService: ServerService
) { } ) { }
get user () {
return this.authService.getUser()
}
ngOnInit () { ngOnInit () {
this.serverService.getConfig() this.serverService.getConfig()
.subscribe(config => this.serverConfig = config) .subscribe(config => this.serverConfig = config)
@ -103,6 +103,9 @@ export class SearchComponent implements OnInit, OnDestroy {
err => this.notifier.error(err.text) err => this.notifier.error(err.text)
) )
this.userService.getAnonymousOrLoggedUser()
.subscribe(user => this.userMiniature = user)
this.hooks.runAction('action:search.init', 'search') this.hooks.runAction('action:search.init', 'search')
} }

View File

@ -1,19 +1,20 @@
import { from, Observable } from 'rxjs' import { has } from 'lodash-es'
import { catchError, concatMap, map, shareReplay, toArray } from 'rxjs/operators' import { BytesPipe } from 'ngx-pipes'
import { SortMeta } from 'primeng/api'
import { from, Observable, of } from 'rxjs'
import { catchError, concatMap, first, map, shareReplay, toArray, throttleTime, filter } from 'rxjs/operators'
import { HttpClient, HttpParams } from '@angular/common/http' import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core' import { Injectable } from '@angular/core'
import { ResultList, User as UserServerModel, UserCreate, UserRole, UserUpdate, UserUpdateMe, UserVideoQuota } from '../../../../../shared' import { AuthService } from '@app/core/auth'
import { environment } from '../../../environments/environment'
import { RestExtractor, RestPagination, RestService } from '../rest'
import { Avatar } from '../../../../../shared/models/avatars/avatar.model'
import { SortMeta } from 'primeng/api'
import { BytesPipe } from 'ngx-pipes'
import { I18n } from '@ngx-translate/i18n-polyfill' import { I18n } from '@ngx-translate/i18n-polyfill'
import { UserRegister } from '@shared/models/users/user-register.model' import { UserRegister } from '@shared/models/users/user-register.model'
import { User } from './user.model'
import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type' import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type'
import { has } from 'lodash-es' import { ResultList, User as UserServerModel, UserCreate, UserRole, UserUpdate, UserUpdateMe, UserVideoQuota } from '../../../../../shared'
import { Avatar } from '../../../../../shared/models/avatars/avatar.model'
import { environment } from '../../../environments/environment'
import { LocalStorageService, SessionStorageService } from '../misc/storage.service' import { LocalStorageService, SessionStorageService } from '../misc/storage.service'
import { RestExtractor, RestPagination, RestService } from '../rest'
import { User } from './user.model'
@Injectable() @Injectable()
export class UserService { export class UserService {
@ -25,6 +26,7 @@ export class UserService {
constructor ( constructor (
private authHttp: HttpClient, private authHttp: HttpClient,
private authService: AuthService,
private restExtractor: RestExtractor, private restExtractor: RestExtractor,
private restService: RestService, private restService: RestService,
private localStorageService: LocalStorageService, private localStorageService: LocalStorageService,
@ -94,6 +96,21 @@ export class UserService {
} }
} }
listenAnonymousUpdate () {
return this.localStorageService.watch([
User.KEYS.NSFW_POLICY,
User.KEYS.WEBTORRENT_ENABLED,
User.KEYS.AUTO_PLAY_VIDEO,
User.KEYS.AUTO_PLAY_VIDEO_PLAYLIST,
User.KEYS.THEME,
User.KEYS.VIDEO_LANGUAGES
]).pipe(
throttleTime(200),
filter(() => this.authService.isLoggedIn() !== true),
map(() => this.getAnonymousUser())
)
}
deleteMe () { deleteMe () {
const url = UserService.BASE_USERS_URL + 'me' const url = UserService.BASE_USERS_URL + 'me'
@ -241,7 +258,7 @@ export class UserService {
} }
getAnonymousUser () { getAnonymousUser () {
let videoLanguages let videoLanguages: string[]
try { try {
videoLanguages = JSON.parse(this.localStorageService.getItem(User.KEYS.VIDEO_LANGUAGES)) videoLanguages = JSON.parse(this.localStorageService.getItem(User.KEYS.VIDEO_LANGUAGES))
@ -313,6 +330,18 @@ export class UserService {
) )
} }
getAnonymousOrLoggedUser () {
if (!this.authService.isLoggedIn()) {
return of(this.getAnonymousUser())
}
return this.authService.userInformationLoaded
.pipe(
first(),
map(() => this.authService.getUser())
)
}
private formatUser (user: UserServerModel) { private formatUser (user: UserServerModel) {
let videoQuota let videoQuota
if (user.videoQuota === -1) { if (user.videoQuota === -1) {

View File

@ -38,7 +38,7 @@
<div class="video-wrapper"> <div class="video-wrapper">
<my-video-miniature <my-video-miniature
[fitWidth]="true" [fitWidth]="true"
[video]="video" [user]="user" [ownerDisplayType]="ownerDisplayType" [video]="video" [user]="userMiniature" [ownerDisplayType]="ownerDisplayType"
[displayVideoActions]="displayVideoActions" [displayOptions]="displayOptions" [displayVideoActions]="displayVideoActions" [displayOptions]="displayOptions"
(videoBlocked)="removeVideoFromArray(video)" (videoRemoved)="removeVideoFromArray(video)" (videoBlocked)="removeVideoFromArray(video)" (videoRemoved)="removeVideoFromArray(video)"
> >

View File

@ -1,22 +1,23 @@
import { debounceTime, first, tap, throttleTime } from 'rxjs/operators' import { fromEvent, Observable, of, Subject, Subscription } from 'rxjs'
import { debounceTime, tap, throttleTime, switchMap } from 'rxjs/operators'
import { OnDestroy, OnInit } from '@angular/core' import { OnDestroy, OnInit } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { fromEvent, Observable, of, Subject, Subscription } from 'rxjs'
import { AuthService } from '../../core/auth'
import { ComponentPaginationLight } from '../rest/component-pagination.model'
import { VideoSortField } from './sort-field.type'
import { Video } from './video.model'
import { ScreenService } from '@app/shared/misc/screen.service'
import { MiniatureDisplayOptions, OwnerDisplayType } from '@app/shared/video/video-miniature.component'
import { Syndication } from '@app/shared/video/syndication.model'
import { Notifier, ServerService } from '@app/core' import { Notifier, ServerService } from '@app/core'
import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook' import { DisableForReuseHook } from '@app/core/routing/disable-for-reuse-hook'
import { GlobalIconName } from '@app/shared/images/global-icon.component'
import { ScreenService } from '@app/shared/misc/screen.service'
import { Syndication } from '@app/shared/video/syndication.model'
import { MiniatureDisplayOptions, OwnerDisplayType } from '@app/shared/video/video-miniature.component'
import { I18n } from '@ngx-translate/i18n-polyfill' import { I18n } from '@ngx-translate/i18n-polyfill'
import { isLastMonth, isLastWeek, isToday, isYesterday } from '@shared/core-utils/miscs/date' import { isLastMonth, isLastWeek, isToday, isYesterday } from '@shared/core-utils/miscs/date'
import { ServerConfig } from '@shared/models' import { ServerConfig } from '@shared/models'
import { GlobalIconName } from '@app/shared/images/global-icon.component' import { NSFWPolicyType } from '@shared/models/videos/nsfw-policy.type'
import { UserService, User } from '../users' import { AuthService } from '../../core/auth'
import { LocalStorageService } from '../misc/storage.service' import { LocalStorageService } from '../misc/storage.service'
import { ComponentPaginationLight } from '../rest/component-pagination.model'
import { User, UserService } from '../users'
import { VideoSortField } from './sort-field.type'
import { Video } from './video.model'
enum GroupDate { enum GroupDate {
UNKNOWN = 0, UNKNOWN = 0,
@ -34,14 +35,15 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
} }
sort: VideoSortField = '-publishedAt' sort: VideoSortField = '-publishedAt'
categoryOneOf?: number categoryOneOf?: number[]
languageOneOf?: string[] languageOneOf?: string[]
nsfwPolicy?: NSFWPolicyType
defaultSort: VideoSortField = '-publishedAt' defaultSort: VideoSortField = '-publishedAt'
syndicationItems: Syndication[] = [] syndicationItems: Syndication[] = []
loadOnInit = true loadOnInit = true
useUserVideoLanguagePreferences = false useUserVideoPreferences = false
ownerDisplayType: OwnerDisplayType = 'account' ownerDisplayType: OwnerDisplayType = 'account'
displayModerationBlock = false displayModerationBlock = false
titleTooltip: string titleTooltip: string
@ -71,6 +73,8 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
onDataSubject = new Subject<any[]>() onDataSubject = new Subject<any[]>()
userMiniature: User
protected serverConfig: ServerConfig protected serverConfig: ServerConfig
protected abstract notifier: Notifier protected abstract notifier: Notifier
@ -96,10 +100,6 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
abstract generateSyndicationList (): void abstract generateSyndicationList (): void
get user () {
return this.authService.getUser()
}
ngOnInit () { ngOnInit () {
this.serverConfig = this.serverService.getTmpConfig() this.serverConfig = this.serverService.getTmpConfig()
this.serverService.getConfig() this.serverService.getConfig()
@ -124,21 +124,17 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
this.calcPageSizes() this.calcPageSizes()
const loadUserObservable = this.loadUserVideoLanguagesIfNeeded() const loadUserObservable = this.loadUserAndSettings()
if (this.loadOnInit === true) { if (this.loadOnInit === true) {
loadUserObservable.subscribe(() => this.loadMoreVideos()) loadUserObservable.subscribe(() => this.loadMoreVideos())
} }
this.storageService.watch([ this.userService.listenAnonymousUpdate()
User.KEYS.NSFW_POLICY, .pipe(switchMap(() => this.loadUserAndSettings()))
User.KEYS.VIDEO_LANGUAGES .subscribe(() => {
]).pipe(throttleTime(200)).subscribe(
() => {
this.loadUserVideoLanguagesIfNeeded()
if (this.hasDoneFirstQuery) this.reloadVideos() if (this.hasDoneFirstQuery) this.reloadVideos()
} })
)
// Display avatar in mobile view // Display avatar in mobile view
if (this.screenService.isInMobileView()) { if (this.screenService.isInMobileView()) {
@ -298,20 +294,15 @@ export abstract class AbstractVideoList implements OnInit, OnDestroy, DisableFor
this.router.navigate([ path ], { queryParams, replaceUrl: true, queryParamsHandling: 'merge' }) this.router.navigate([ path ], { queryParams, replaceUrl: true, queryParamsHandling: 'merge' })
} }
private loadUserVideoLanguagesIfNeeded () { private loadUserAndSettings () {
if (!this.useUserVideoLanguagePreferences) { return this.userService.getAnonymousOrLoggedUser()
return of(true) .pipe(tap(user => {
} this.userMiniature = user
if (!this.authService.isLoggedIn()) { if (!this.useUserVideoPreferences) return
this.languageOneOf = this.userService.getAnonymousUser().videoLanguages
return of(true)
}
return this.authService.userInformationLoaded this.languageOneOf = user.videoLanguages
.pipe( this.nsfwPolicy = user.nsfwPolicy
first(), }))
tap(() => this.languageOneOf = this.user.videoLanguages)
)
} }
} }

View File

@ -39,8 +39,9 @@ export interface VideosProvider {
videoPagination: ComponentPaginationLight, videoPagination: ComponentPaginationLight,
sort: VideoSortField, sort: VideoSortField,
filter?: VideoFilter, filter?: VideoFilter,
categoryOneOf?: number, categoryOneOf?: number[],
languageOneOf?: string[] languageOneOf?: string[]
nsfwPolicy: NSFWPolicyType
}): Observable<ResultList<Video>> }): Observable<ResultList<Video>>
} }
@ -161,13 +162,18 @@ export class VideoService implements VideosProvider {
getVideoChannelVideos ( getVideoChannelVideos (
videoChannel: VideoChannel, videoChannel: VideoChannel,
videoPagination: ComponentPaginationLight, videoPagination: ComponentPaginationLight,
sort: VideoSortField sort: VideoSortField,
nsfwPolicy?: NSFWPolicyType
): Observable<ResultList<Video>> { ): Observable<ResultList<Video>> {
const pagination = this.restService.componentPaginationToRestPagination(videoPagination) const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
let params = new HttpParams() let params = new HttpParams()
params = this.restService.addRestGetParams(params, pagination, sort) params = this.restService.addRestGetParams(params, pagination, sort)
if (nsfwPolicy) {
params = params.set('nsfw', this.nsfwPolicyToParam(nsfwPolicy))
}
return this.authHttp return this.authHttp
.get<ResultList<Video>>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.nameWithHost + '/videos', { params }) .get<ResultList<Video>>(VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannel.nameWithHost + '/videos', { params })
.pipe( .pipe(
@ -201,12 +207,12 @@ export class VideoService implements VideosProvider {
videoPagination: ComponentPaginationLight, videoPagination: ComponentPaginationLight,
sort: VideoSortField, sort: VideoSortField,
filter?: VideoFilter, filter?: VideoFilter,
categoryOneOf?: number, categoryOneOf?: number[],
languageOneOf?: string[], languageOneOf?: string[],
skipCount?: boolean, skipCount?: boolean,
nsfw?: boolean nsfwPolicy?: NSFWPolicyType
}): Observable<ResultList<Video>> { }): Observable<ResultList<Video>> {
const { videoPagination, sort, filter, categoryOneOf, languageOneOf, skipCount, nsfw } = parameters const { videoPagination, sort, filter, categoryOneOf, languageOneOf, skipCount, nsfwPolicy } = parameters
const pagination = this.restService.componentPaginationToRestPagination(videoPagination) const pagination = this.restService.componentPaginationToRestPagination(videoPagination)
@ -214,16 +220,10 @@ export class VideoService implements VideosProvider {
params = this.restService.addRestGetParams(params, pagination, sort) params = this.restService.addRestGetParams(params, pagination, sort)
if (filter) params = params.set('filter', filter) if (filter) params = params.set('filter', filter)
if (categoryOneOf) params = params.set('categoryOneOf', categoryOneOf + '')
if (skipCount) params = params.set('skipCount', skipCount + '') if (skipCount) params = params.set('skipCount', skipCount + '')
if (nsfw) { if (nsfwPolicy) {
params = params.set('nsfw', nsfw + '') params = params.set('nsfw', this.nsfwPolicyToParam(nsfwPolicy))
} else {
const nsfwPolicy = this.authService.isLoggedIn()
? this.authService.getUser().nsfwPolicy
: this.userService.getAnonymousUser().nsfwPolicy
if (this.nsfwPolicyToFilter(nsfwPolicy)) params.set('nsfw', 'false')
} }
if (languageOneOf) { if (languageOneOf) {
@ -232,6 +232,12 @@ export class VideoService implements VideosProvider {
} }
} }
if (categoryOneOf) {
for (const c of categoryOneOf) {
params = params.append('categoryOneOf[]', c + '')
}
}
return this.authHttp return this.authHttp
.get<ResultList<Video>>(VideoService.BASE_VIDEO_URL, { params }) .get<ResultList<Video>>(VideoService.BASE_VIDEO_URL, { params })
.pipe( .pipe(
@ -268,12 +274,16 @@ export class VideoService implements VideosProvider {
return feeds return feeds
} }
getVideoFeedUrls (sort: VideoSortField, filter?: VideoFilter, categoryOneOf?: number) { getVideoFeedUrls (sort: VideoSortField, filter?: VideoFilter, categoryOneOf?: number[]) {
let params = this.restService.addRestGetParams(new HttpParams(), undefined, sort) let params = this.restService.addRestGetParams(new HttpParams(), undefined, sort)
if (filter) params = params.set('filter', filter) if (filter) params = params.set('filter', filter)
if (categoryOneOf) params = params.set('categoryOneOf', categoryOneOf + '') if (categoryOneOf) {
for (const c of categoryOneOf) {
params = params.append('categoryOneOf[]', c + '')
}
}
return this.buildBaseFeedUrls(params) return this.buildBaseFeedUrls(params)
} }
@ -377,6 +387,12 @@ export class VideoService implements VideosProvider {
return base.filter(o => !!privacies.find(p => p.id === o.id)) return base.filter(o => !!privacies.find(p => p.id === o.id))
} }
nsfwPolicyToParam (nsfwPolicy: NSFWPolicyType) {
return nsfwPolicy === 'do_not_list'
? 'false'
: 'both'
}
private setVideoRate (id: number, rateType: UserVideoRateType) { private setVideoRate (id: number, rateType: UserVideoRateType) {
const url = VideoService.BASE_VIDEO_URL + id + '/rate' const url = VideoService.BASE_VIDEO_URL + id + '/rate'
const body: UserVideoRateUpdate = { const body: UserVideoRateUpdate = {
@ -390,8 +406,4 @@ export class VideoService implements VideosProvider {
catchError(err => this.restExtractor.handleError(err)) catchError(err => this.restExtractor.handleError(err))
) )
} }
private nsfwPolicyToFilter (policy: NSFWPolicyType) {
return policy === 'do_not_list'
}
} }

View File

@ -248,10 +248,10 @@
</div> </div>
<my-recommended-videos <my-recommended-videos
[inputRecommendation]="{ uuid: video.uuid, tags: video.tags }" [inputRecommendation]="{ uuid: video.uuid, tags: video.tags }"
[user]="user" [user]="user"
[playlist]="playlist" [playlist]="playlist"
(gotRecommendations)="onRecommendations($event)" (gotRecommendations)="onRecommendations($event)"
></my-recommended-videos> ></my-recommended-videos>
</div> </div>

View File

@ -1,15 +1,15 @@
import { Injectable, OnInit } from '@angular/core'
import { RecommendationService } from '@app/videos/recommendations/recommendations.service'
import { Video } from '@app/shared/video/video.model'
import { RecommendationInfo } from '@app/shared/video/recommendation-info.model'
import { VideoService } from '@app/shared/video/video.service'
import { map, switchMap } from 'rxjs/operators'
import { Observable, of } from 'rxjs' import { Observable, of } from 'rxjs'
import { SearchService } from '@app/search/search.service' import { map, switchMap } from 'rxjs/operators'
import { AdvancedSearch } from '@app/search/advanced-search.model' import { Injectable } from '@angular/core'
import { ServerService } from '@app/core' import { ServerService } from '@app/core'
import { AdvancedSearch } from '@app/search/advanced-search.model'
import { SearchService } from '@app/search/search.service'
import { UserService } from '@app/shared'
import { RecommendationInfo } from '@app/shared/video/recommendation-info.model'
import { Video } from '@app/shared/video/video.model'
import { VideoService } from '@app/shared/video/video.service'
import { RecommendationService } from '@app/videos/recommendations/recommendations.service'
import { ServerConfig } from '@shared/models' import { ServerConfig } from '@shared/models'
import { truncate } from 'lodash'
/** /**
* Provides "recommendations" by providing the most recently uploaded videos. * Provides "recommendations" by providing the most recently uploaded videos.
@ -23,13 +23,14 @@ export class RecentVideosRecommendationService implements RecommendationService
constructor ( constructor (
private videos: VideoService, private videos: VideoService,
private searchService: SearchService, private searchService: SearchService,
private userService: UserService,
private serverService: ServerService private serverService: ServerService
) { ) {
this.config = this.serverService.getTmpConfig() this.config = this.serverService.getTmpConfig()
this.serverService.getConfig() this.serverService.getConfig()
.subscribe(config => this.config = config) .subscribe(config => this.config = config)
} }
getRecommendations (recommendation: RecommendationInfo): Observable<Video[]> { getRecommendations (recommendation: RecommendationInfo): Observable<Video[]> {
return this.fetchPage(1, recommendation) return this.fetchPage(1, recommendation)
@ -55,20 +56,29 @@ export class RecentVideosRecommendationService implements RecommendationService
return defaultSubscription return defaultSubscription
} }
const params = { return this.userService.getAnonymousOrLoggedUser()
search: '', .pipe(
componentPagination: pagination, map(user => {
advancedSearch: new AdvancedSearch({ tagsOneOf: recommendation.tags.join(','), sort: '-createdAt', searchTarget: 'local' }) return {
} search: '',
componentPagination: pagination,
advancedSearch: new AdvancedSearch({
tagsOneOf: recommendation.tags.join(','),
sort: '-createdAt',
searchTarget: 'local',
nsfw: user.nsfwPolicy
? this.videos.nsfwPolicyToParam(user.nsfwPolicy)
: undefined
})
}
}),
switchMap(params => this.searchService.searchVideos(params)),
map(v => v.data),
switchMap(videos => {
if (videos.length <= 1) return defaultSubscription
return this.searchService.searchVideos(params) return of(videos)
.pipe( })
map(v => v.data), )
switchMap(videos => {
if (videos.length <= 1) return defaultSubscription
return of(videos)
})
)
} }
} }

View File

@ -14,7 +14,7 @@
<ng-container *ngFor="let video of (videos$ | async); let i = index; let length = count"> <ng-container *ngFor="let video of (videos$ | async); let i = index; let length = count">
<my-video-miniature <my-video-miniature
[displayOptions]="displayOptions" [video]="video" [user]="user" [displayOptions]="displayOptions" [video]="video" [user]="userMiniature"
(videoBlocked)="onVideoRemoved()" (videoRemoved)="onVideoRemoved()"> (videoBlocked)="onVideoRemoved()" (videoRemoved)="onVideoRemoved()">
</my-video-miniature> </my-video-miniature>

View File

@ -1,24 +1,23 @@
import { Component, Input, Output, OnChanges, EventEmitter } from '@angular/core'
import { Observable } from 'rxjs' import { Observable } from 'rxjs'
import { Video } from '@app/shared/video/video.model' import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'
import { AuthService, Notifier } from '@app/core'
import { User } from '@app/shared'
import { SessionStorageService } from '@app/shared/misc/storage.service'
import { UserService } from '@app/shared/users/user.service'
import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model' import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
import { RecommendationInfo } from '@app/shared/video/recommendation-info.model' import { RecommendationInfo } from '@app/shared/video/recommendation-info.model'
import { RecommendedVideosStore } from '@app/videos/recommendations/recommended-videos.store'
import { User } from '@app/shared'
import { AuthService, Notifier } from '@app/core'
import { UserService } from '@app/shared/users/user.service'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { SessionStorageService } from '@app/shared/misc/storage.service'
import { MiniatureDisplayOptions } from '@app/shared/video/video-miniature.component' import { MiniatureDisplayOptions } from '@app/shared/video/video-miniature.component'
import { Video } from '@app/shared/video/video.model'
import { RecommendedVideosStore } from '@app/videos/recommendations/recommended-videos.store'
import { I18n } from '@ngx-translate/i18n-polyfill'
@Component({ @Component({
selector: 'my-recommended-videos', selector: 'my-recommended-videos',
templateUrl: './recommended-videos.component.html', templateUrl: './recommended-videos.component.html',
styleUrls: [ './recommended-videos.component.scss' ] styleUrls: [ './recommended-videos.component.scss' ]
}) })
export class RecommendedVideosComponent implements OnChanges { export class RecommendedVideosComponent implements OnInit, OnChanges {
@Input() inputRecommendation: RecommendationInfo @Input() inputRecommendation: RecommendationInfo
@Input() user: User
@Input() playlist: VideoPlaylist @Input() playlist: VideoPlaylist
@Output() gotRecommendations = new EventEmitter<Video[]>() @Output() gotRecommendations = new EventEmitter<Video[]>()
@ -32,6 +31,8 @@ export class RecommendedVideosComponent implements OnChanges {
avatar: true avatar: true
} }
userMiniature: User
readonly hasVideos$: Observable<boolean> readonly hasVideos$: Observable<boolean>
readonly videos$: Observable<Video[]> readonly videos$: Observable<Video[]>
@ -59,7 +60,12 @@ export class RecommendedVideosComponent implements OnChanges {
this.autoPlayNextVideoTooltip = this.i18n('When active, the next video is automatically played after the current one.') this.autoPlayNextVideoTooltip = this.i18n('When active, the next video is automatically played after the current one.')
} }
public ngOnChanges (): void { ngOnInit () {
this.userService.getAnonymousOrLoggedUser()
.subscribe(user => this.userMiniature = user)
}
ngOnChanges () {
if (this.inputRecommendation) { if (this.inputRecommendation) {
this.store.requestNewRecommendations(this.inputRecommendation) this.store.requestNewRecommendations(this.inputRecommendation)
} }

View File

@ -24,7 +24,7 @@ export class VideoLocalComponent extends AbstractVideoList implements OnInit, On
sort = '-publishedAt' as VideoSortField sort = '-publishedAt' as VideoSortField
filter: VideoFilter = 'local' filter: VideoFilter = 'local'
useUserVideoLanguagePreferences = true useUserVideoPreferences = true
constructor ( constructor (
protected i18n: I18n, protected i18n: I18n,
@ -67,6 +67,7 @@ export class VideoLocalComponent extends AbstractVideoList implements OnInit, On
filter: this.filter, filter: this.filter,
categoryOneOf: this.categoryOneOf, categoryOneOf: this.categoryOneOf,
languageOneOf: this.languageOneOf, languageOneOf: this.languageOneOf,
nsfwPolicy: this.nsfwPolicy,
skipCount: true skipCount: true
} }

View File

@ -21,7 +21,7 @@ export class VideoMostLikedComponent extends AbstractVideoList implements OnInit
titlePage: string titlePage: string
defaultSort: VideoSortField = '-likes' defaultSort: VideoSortField = '-likes'
useUserVideoLanguagePreferences = true useUserVideoPreferences = true
constructor ( constructor (
protected i18n: I18n, protected i18n: I18n,
@ -55,6 +55,7 @@ export class VideoMostLikedComponent extends AbstractVideoList implements OnInit
sort: this.sort, sort: this.sort,
categoryOneOf: this.categoryOneOf, categoryOneOf: this.categoryOneOf,
languageOneOf: this.languageOneOf, languageOneOf: this.languageOneOf,
nsfwPolicy: this.nsfwPolicy,
skipCount: true skipCount: true
} }

View File

@ -14,7 +14,7 @@
</h1> </h1>
<div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)"> <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)">
<my-video-miniature [video]="video" [fitWidth]="true" [user]="user" [displayVideoActions]="false"> <my-video-miniature [video]="video" [fitWidth]="true" [user]="userMiniature" [displayVideoActions]="false">
</my-video-miniature> </my-video-miniature>
</div> </div>
</div> </div>
@ -25,7 +25,7 @@
</h2> </h2>
<div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)"> <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)">
<my-video-miniature [video]="video" [fitWidth]="true" [user]="user" [displayVideoActions]="false"> <my-video-miniature [video]="video" [fitWidth]="true" [user]="userMiniature" [displayVideoActions]="false">
</my-video-miniature> </my-video-miniature>
</div> </div>
</div> </div>
@ -40,7 +40,7 @@
</div> </div>
<div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)"> <div class="video-wrapper" *ngFor="let video of buildVideos(object.videos)">
<my-video-miniature [video]="video" [fitWidth]="true" [user]="user" [displayVideoActions]="false"> <my-video-miniature [video]="video" [fitWidth]="true" [user]="userMiniature" [displayVideoActions]="false">
</my-video-miniature> </my-video-miniature>
</div> </div>
</div> </div>

View File

@ -1,11 +1,11 @@
import { Component, OnInit } from '@angular/core'
import { AuthService, Notifier } from '@app/core'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { VideosOverview } from '@app/shared/overview/videos-overview.model'
import { OverviewService } from '@app/shared/overview'
import { Video } from '@app/shared/video/video.model'
import { ScreenService } from '@app/shared/misc/screen.service'
import { Subject } from 'rxjs' import { Subject } from 'rxjs'
import { Component, OnInit } from '@angular/core'
import { Notifier } from '@app/core'
import { User, UserService } from '@app/shared'
import { ScreenService } from '@app/shared/misc/screen.service'
import { OverviewService } from '@app/shared/overview'
import { VideosOverview } from '@app/shared/overview/videos-overview.model'
import { Video } from '@app/shared/video/video.model'
@Component({ @Component({
selector: 'my-video-overview', selector: 'my-video-overview',
@ -18,6 +18,8 @@ export class VideoOverviewComponent implements OnInit {
overviews: VideosOverview[] = [] overviews: VideosOverview[] = []
notResults = false notResults = false
userMiniature: User
private loaded = false private loaded = false
private currentPage = 1 private currentPage = 1
private maxPage = 20 private maxPage = 20
@ -25,19 +27,20 @@ export class VideoOverviewComponent implements OnInit {
private isLoading = false private isLoading = false
constructor ( constructor (
private i18n: I18n,
private notifier: Notifier, private notifier: Notifier,
private authService: AuthService, private userService: UserService,
private overviewService: OverviewService, private overviewService: OverviewService,
private screenService: ScreenService private screenService: ScreenService
) { } ) { }
get user () {
return this.authService.getUser()
}
ngOnInit () { ngOnInit () {
this.loadMoreResults() this.loadMoreResults()
this.userService.getAnonymousOrLoggedUser()
.subscribe(user => this.userMiniature = user)
this.userService.listenAnonymousUpdate()
.subscribe(user => this.userMiniature = user)
} }
buildVideoChannelBy (object: { videos: Video[] }) { buildVideoChannelBy (object: { videos: Video[] }) {

View File

@ -22,7 +22,7 @@ export class VideoRecentlyAddedComponent extends AbstractVideoList implements On
sort: VideoSortField = '-publishedAt' sort: VideoSortField = '-publishedAt'
groupByDate = true groupByDate = true
useUserVideoLanguagePreferences = true useUserVideoPreferences = true
constructor ( constructor (
protected i18n: I18n, protected i18n: I18n,
@ -59,6 +59,7 @@ export class VideoRecentlyAddedComponent extends AbstractVideoList implements On
sort: this.sort, sort: this.sort,
categoryOneOf: this.categoryOneOf, categoryOneOf: this.categoryOneOf,
languageOneOf: this.languageOneOf, languageOneOf: this.languageOneOf,
nsfwPolicy: this.nsfwPolicy,
skipCount: true skipCount: true
} }

View File

@ -21,7 +21,7 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
titlePage: string titlePage: string
defaultSort: VideoSortField = '-trending' defaultSort: VideoSortField = '-trending'
useUserVideoLanguagePreferences = true useUserVideoPreferences = true
constructor ( constructor (
protected i18n: I18n, protected i18n: I18n,
@ -72,6 +72,7 @@ export class VideoTrendingComponent extends AbstractVideoList implements OnInit,
sort: this.sort, sort: this.sort,
categoryOneOf: this.categoryOneOf, categoryOneOf: this.categoryOneOf,
languageOneOf: this.languageOneOf, languageOneOf: this.languageOneOf,
nsfwPolicy: this.nsfwPolicy,
skipCount: true skipCount: true
} }