--- /dev/null
+import { getCheckbox } from '../utils'
+
+export class AnonymousSettingsPage {
+
+ async openSettings () {
+ const link = await $$('.menu-link').filter(async i => {
+ return await i.getText() === 'My settings'
+ }).then(links => links[0])
+
+ await link.click()
+
+ await $('my-user-video-settings').waitForDisplayed()
+ }
+
+ async clickOnP2PCheckbox () {
+ const p2p = getCheckbox('p2pEnabled')
+ await p2p.waitForClickable()
+
+ await p2p.click()
+ }
+}
-import { go } from '../utils'
+import { getCheckbox, go } from '../utils'
export class MyAccountPage {
await nsfw.scrollIntoView(false) // Avoid issues with fixed header on firefox
await nsfw.selectByAttribute('value', newValue)
+ await this.submitVideoSettings()
+ }
+
+ async clickOnP2PCheckbox () {
+ const p2p = getCheckbox('p2pEnabled')
+
+ await p2p.waitForClickable()
+ await p2p.scrollIntoView(false) // Avoid issues with fixed header on firefox
+
+ await p2p.click()
+
+ await this.submitVideoSettings()
+ }
+
+ private async submitVideoSettings () {
const submit = $('my-user-video-settings input[type=submit]')
await submit.scrollIntoView(false)
await submit.click()
import { join } from 'path'
-import { clickOnCheckbox } from '../utils'
+import { getCheckbox, selectCustomSelect } from '../utils'
export class VideoUploadPage {
async navigateTo () {
}
setAsNSFW () {
- return clickOnCheckbox('nsfw')
+ return getCheckbox('nsfw').click()
}
async validSecondUploadStep (videoName: string) {
})
}
+ setAsPublic () {
+ return selectCustomSelect('privacy', 'Public')
+ }
+
private getSecondStepSubmitButton () {
return $('.submit-container my-button')
}
return $('my-video-comment-add').isExisting()
}
+ isPrivacyWarningDisplayed () {
+ return $('my-privacy-concerns').isDisplayed()
+ }
+
async goOnAssociatedEmbed () {
let url = await browser.getUrl()
url = url.replace('/w/', '/videos/embed/')
url = url.replace(':3333', ':9001')
- return go(url)
+ await go(url)
+ await $('.vjs-big-play-button').waitForDisplayed()
+ }
+
+ async isEmbedWarningDisplayed () {
+ const text = await $('.vjs-dock-description').getText()
+
+ return !!text.trim()
}
goOnP2PMediaLoaderEmbed () {
import { LoginPage } from '../po/login.po'
-import { MyAccountPage } from '../po/my-account'
+import { MyAccountPage } from '../po/my-account.po'
import { PlayerPage } from '../po/player.po'
import { VideoListPage } from '../po/video-list.po'
import { VideoUpdatePage } from '../po/video-update.po'
import { LoginPage } from '../po/login.po'
import { VideoUploadPage } from '../po/video-upload.po'
import { VideoWatchPage } from '../po/video-watch.po'
-import { isMobileDevice, isSafari, waitServerUp } from '../utils'
+import { go, isMobileDevice, isSafari, waitServerUp } from '../utils'
describe('Custom server defaults', () => {
let videoUploadPage: VideoUploadPage
before(async () => {
await waitServerUp()
- })
- beforeEach(async () => {
loginPage = new LoginPage()
videoUploadPage = new VideoUploadPage()
videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari())
await browser.maximizeWindow()
})
- it('Should upload a video with custom default values', async function () {
- await loginPage.loginAsRootUser()
- await videoUploadPage.navigateTo()
- await videoUploadPage.uploadVideo()
- await videoUploadPage.validSecondUploadStep('video')
+ describe('Publish default values', function () {
+ before(async function () {
+ await loginPage.loginAsRootUser()
+ })
+
+ it('Should upload a video with custom default values', async function () {
+ await videoUploadPage.navigateTo()
+ await videoUploadPage.uploadVideo()
+ await videoUploadPage.validSecondUploadStep('video')
- await videoWatchPage.waitWatchVideoName('video')
+ await videoWatchPage.waitWatchVideoName('video')
- expect(await videoWatchPage.getPrivacy()).toBe('Internal')
- expect(await videoWatchPage.getLicence()).toBe('Attribution - Non Commercial')
- expect(await videoWatchPage.isDownloadEnabled()).toBeFalsy()
- expect(await videoWatchPage.areCommentsEnabled()).toBeFalsy()
+ expect(await videoWatchPage.getPrivacy()).toBe('Internal')
+ expect(await videoWatchPage.getLicence()).toBe('Attribution - Non Commercial')
+ expect(await videoWatchPage.isDownloadEnabled()).toBeFalsy()
+ expect(await videoWatchPage.areCommentsEnabled()).toBeFalsy()
+ })
+
+ after(async function () {
+ await loginPage.logout()
+ })
})
+ describe('P2P', function () {
+ let videoUrl: string
+
+ async function goOnVideoWatchPage () {
+ await go(videoUrl)
+ await videoWatchPage.waitWatchVideoName('video')
+ }
+
+ async function checkP2P (enabled: boolean) {
+ await goOnVideoWatchPage()
+ expect(await videoWatchPage.isPrivacyWarningDisplayed()).toEqual(enabled)
+
+ await videoWatchPage.goOnAssociatedEmbed()
+ expect(await videoWatchPage.isEmbedWarningDisplayed()).toEqual(enabled)
+ }
+
+ before(async () => {
+ await loginPage.loginAsRootUser()
+ await videoUploadPage.navigateTo()
+ await videoUploadPage.uploadVideo()
+ await videoUploadPage.setAsPublic()
+ await videoUploadPage.validSecondUploadStep('video')
+
+ await videoWatchPage.waitWatchVideoName('video')
+
+ videoUrl = await browser.getUrl()
+ })
+
+ beforeEach(async function () {
+ await goOnVideoWatchPage()
+ })
+
+ it('Should have P2P disabled for a logged in user', async function () {
+ await checkP2P(false)
+ })
+
+ it('Should have P2P disabled for anonymous users', async function () {
+ await loginPage.logout()
+
+ await checkP2P(false)
+ })
+ })
})
--- /dev/null
+import { AnonymousSettingsPage } from '../po/anonymous-settings.po'
+import { LoginPage } from '../po/login.po'
+import { MyAccountPage } from '../po/my-account.po'
+import { VideoUploadPage } from '../po/video-upload.po'
+import { VideoWatchPage } from '../po/video-watch.po'
+import { go, isMobileDevice, isSafari, waitServerUp } from '../utils'
+
+describe('User settings', () => {
+ let videoUploadPage: VideoUploadPage
+ let loginPage: LoginPage
+ let videoWatchPage: VideoWatchPage
+ let myAccountPage: MyAccountPage
+ let anonymousSettingsPage: AnonymousSettingsPage
+
+ before(async () => {
+ await waitServerUp()
+
+ loginPage = new LoginPage()
+ videoUploadPage = new VideoUploadPage()
+ videoWatchPage = new VideoWatchPage(isMobileDevice(), isSafari())
+ myAccountPage = new MyAccountPage()
+ anonymousSettingsPage = new AnonymousSettingsPage()
+
+ await browser.maximizeWindow()
+ })
+
+ describe('P2P', function () {
+ let videoUrl: string
+
+ async function goOnVideoWatchPage () {
+ await go(videoUrl)
+ await videoWatchPage.waitWatchVideoName('video')
+ }
+
+ async function checkP2P (enabled: boolean) {
+ await goOnVideoWatchPage()
+ expect(await videoWatchPage.isPrivacyWarningDisplayed()).toEqual(enabled)
+
+ await videoWatchPage.goOnAssociatedEmbed()
+ expect(await videoWatchPage.isEmbedWarningDisplayed()).toEqual(enabled)
+ }
+
+ before(async () => {
+ await loginPage.loginAsRootUser()
+ await videoUploadPage.navigateTo()
+ await videoUploadPage.uploadVideo()
+ await videoUploadPage.validSecondUploadStep('video')
+
+ await videoWatchPage.waitWatchVideoName('video')
+
+ videoUrl = await browser.getUrl()
+ })
+
+ beforeEach(async function () {
+ await goOnVideoWatchPage()
+ })
+
+ it('Should have P2P enabled for a logged in user', async function () {
+ await checkP2P(true)
+ })
+
+ it('Should disable P2P for a logged in user', async function () {
+ await myAccountPage.navigateToMySettings()
+ await myAccountPage.clickOnP2PCheckbox()
+
+ await checkP2P(false)
+ })
+
+ it('Should have P2P enabled for anonymous users', async function () {
+ await loginPage.logout()
+
+ await checkP2P(true)
+ })
+
+ it('Should disable P2P for an anonymous user', async function () {
+ await anonymousSettingsPage.openSettings()
+ await anonymousSettingsPage.clickOnP2PCheckbox()
+
+ await checkP2P(false)
+ })
+ })
+})
import { AdminConfigPage } from '../po/admin-config.po'
import { LoginPage } from '../po/login.po'
-import { MyAccountPage } from '../po/my-account'
+import { MyAccountPage } from '../po/my-account.po'
import { VideoListPage } from '../po/video-list.po'
import { VideoSearchPage } from '../po/video-search.po'
import { VideoUploadPage } from '../po/video-upload.po'
-function clickOnCheckbox (name: string) {
- return $(`my-peertube-checkbox[inputname=${name}] label`).click()
+function getCheckbox (name: string) {
+ return $(`my-peertube-checkbox[inputname=${name}] label`)
+}
+
+async function selectCustomSelect (id: string, valueLabel: string) {
+ await $(`[formcontrolname=${id}] .ng-arrow-wrapper`).click()
+
+ const option = await $$(`[formcontrolname=${id}] .ng-option`).filter(async o => {
+ const text = await o.getText()
+
+ return text.trimStart().startsWith(valueLabel)
+ }).then(options => options[0])
+
+ await option.waitForDisplayed()
+
+ return option.click()
}
export {
- clickOnCheckbox
+ getCheckbox,
+ selectCustomSelect
}
comments_enabled: false,
privacy: 4,
licence: 4
+ },
+ p2p: {
+ enabled: false
}
}
}
import { Component, Input, OnInit } from '@angular/core'
-import { ServerService } from '@app/core'
+import { ServerService, User, UserService } from '@app/core'
import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import { HTMLServerConfig, Video } from '@shared/models'
-import { getStoredP2PEnabled } from '../../../../../assets/player/peertube-player-local-storage'
-import { isWebRTCDisabled } from '../../../../../assets/player/utils'
+import { isP2PEnabled } from '../../../../../assets/player/utils'
@Component({
selector: 'my-privacy-concerns',
@Input() video: Video
- display = true
+ display = false
private serverConfig: HTMLServerConfig
constructor (
- private serverService: ServerService
+ private serverService: ServerService,
+ private userService: UserService
) { }
ngOnInit () {
this.serverConfig = this.serverService.getHTMLConfig()
- if (isWebRTCDisabled() || this.isTrackerDisabled() || this.isP2PDisabled() || this.alreadyAccepted()) {
- this.display = false
- }
+ this.userService.getAnonymousOrLoggedUser()
+ .subscribe(user => this.updateDisplay(user))
}
acceptedPrivacyConcern () {
peertubeLocalStorage.setItem(PrivacyConcernsComponent.LOCAL_STORAGE_PRIVACY_CONCERN_KEY, 'true')
- this.display = false
- }
- private isTrackerDisabled () {
- return this.video.isLocal && this.serverConfig.tracker.enabled === false
+ this.display = false
}
- private isP2PDisabled () {
- return getStoredP2PEnabled() === false
+ private updateDisplay (user: User) {
+ if (isP2PEnabled(this.video, this.serverConfig, user.p2pEnabled) && !this.alreadyAccepted()) {
+ this.display = true
+ }
}
private alreadyAccepted () {
import { Component, EventEmitter, Input, Output } from '@angular/core'
import { Router } from '@angular/router'
-import {
- AuthService,
- ComponentPagination,
- HooksService,
- LocalStorageService,
- Notifier,
- SessionStorageService,
- UserService
-} from '@app/core'
+import { AuthService, ComponentPagination, HooksService, Notifier, SessionStorageService, UserService } from '@app/core'
import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/shared/shared-video-playlist'
-import { peertubeLocalStorage, peertubeSessionStorage } from '@root-helpers/peertube-web-storage'
+import { peertubeSessionStorage } from '@root-helpers/peertube-web-storage'
+import { getBoolOrDefault } from '@root-helpers/local-storage-utils'
import { VideoPlaylistPrivacy } from '@shared/models'
@Component({
styleUrls: [ './video-watch-playlist.component.scss' ]
})
export class VideoWatchPlaylistComponent {
- static LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST = 'auto_play_video_playlist'
- static SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST = 'loop_playlist'
+ static SESSION_STORAGE_LOOP_PLAYLIST = 'loop_playlist'
@Input() playlist: VideoPlaylist
private auth: AuthService,
private notifier: Notifier,
private videoPlaylist: VideoPlaylistService,
- private localStorageService: LocalStorageService,
private sessionStorage: SessionStorageService,
private router: Router
) {
- // defaults to true
- this.autoPlayNextVideoPlaylist = this.auth.isLoggedIn()
- ? this.auth.getUser().autoPlayNextVideoPlaylist
- : this.localStorageService.getItem(VideoWatchPlaylistComponent.LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST) !== 'false'
+ this.userService.getAnonymousOrLoggedUser()
+ .subscribe(user => this.autoPlayNextVideoPlaylist = user.autoPlayNextVideoPlaylist)
this.setAutoPlayNextVideoPlaylistSwitchText()
- // defaults to false
- this.loopPlaylist = this.sessionStorage.getItem(VideoWatchPlaylistComponent.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST) === 'true'
+ this.loopPlaylist = getBoolOrDefault(this.sessionStorage.getItem(VideoWatchPlaylistComponent.SESSION_STORAGE_LOOP_PLAYLIST), false)
this.setLoopPlaylistSwitchText()
}
this.autoPlayNextVideoPlaylist = !this.autoPlayNextVideoPlaylist
this.setAutoPlayNextVideoPlaylistSwitchText()
- peertubeLocalStorage.setItem(
- VideoWatchPlaylistComponent.LOCAL_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST,
- this.autoPlayNextVideoPlaylist.toString()
- )
+ const details = { autoPlayNextVideoPlaylist: this.autoPlayNextVideoPlaylist }
if (this.auth.isLoggedIn()) {
- const details = {
- autoPlayNextVideoPlaylist: this.autoPlayNextVideoPlaylist
- }
-
this.userService.updateMyProfile(details)
.subscribe({
next: () => {
error: err => this.notifier.error(err.message)
})
+ } else {
+ this.userService.updateMyAnonymousProfile(details)
}
}
this.setLoopPlaylistSwitchText()
peertubeSessionStorage.setItem(
- VideoWatchPlaylistComponent.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO_PLAYLIST,
+ VideoWatchPlaylistComponent.SESSION_STORAGE_LOOP_PLAYLIST,
this.loopPlaylist.toString()
)
}
import { Observable } from 'rxjs'
import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core'
-import { AuthService, Notifier, SessionStorageService, User, UserService } from '@app/core'
+import { AuthService, Notifier, User, UserService } from '@app/core'
import { Video } from '@app/shared/shared-main'
import { MiniatureDisplayOptions } from '@app/shared/shared-video-miniature'
import { VideoPlaylist } from '@app/shared/shared-video-playlist'
-import { UserLocalStorageKeys } from '@root-helpers/users'
import { RecommendationInfo } from './recommendation-info.model'
import { RecommendedVideosStore } from './recommended-videos.store'
private userService: UserService,
private authService: AuthService,
private notifier: Notifier,
- private store: RecommendedVideosStore,
- private sessionStorageService: SessionStorageService
+ private store: RecommendedVideosStore
) {
this.videos$ = this.store.recommendations$
this.hasVideos$ = this.store.hasRecommendations$
this.videos$.subscribe(videos => this.gotRecommendations.emit(videos))
- if (this.authService.isLoggedIn()) {
- this.autoPlayNextVideo = this.authService.getUser().autoPlayNextVideo
- } else {
- this.autoPlayNextVideo = this.sessionStorageService.getItem(UserLocalStorageKeys.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO) === 'true'
-
- this.sessionStorageService.watch([ UserLocalStorageKeys.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO ]).subscribe(
- () => {
- this.autoPlayNextVideo = this.sessionStorageService.getItem(UserLocalStorageKeys.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO) === 'true'
- }
- )
- }
+ this.userService.getAnonymousOrLoggedUser()
+ .subscribe(user => this.autoPlayNextVideo = user.autoPlayNextVideo)
this.autoPlayNextVideoTooltip = $localize`When active, the next video is automatically played after the current one.`
}
}
switchAutoPlayNextVideo () {
- this.sessionStorageService.setItem(UserLocalStorageKeys.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO, this.autoPlayNextVideo.toString())
+ const details = { autoPlayNextVideo: this.autoPlayNextVideo }
if (this.authService.isLoggedIn()) {
- const details = {
- autoPlayNextVideo: this.autoPlayNextVideo
- }
-
this.userService.updateMyProfile(details)
.subscribe({
next: () => {
error: err => this.notifier.error(err.message)
})
+ } else {
+ this.userService.updateMyAnonymousProfile(details)
}
}
}
import { Hotkey, HotkeysService } from 'angular2-hotkeys'
import { forkJoin, Subscription } from 'rxjs'
+import { isP2PEnabled } from 'src/assets/player/utils'
import { PlatformLocation } from '@angular/common'
import { Component, ElementRef, Inject, LOCALE_ID, NgZone, OnDestroy, OnInit, ViewChild } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
RestExtractor,
ScreenService,
ServerService,
+ User,
UserService
} from '@app/core'
import { HooksService } from '@app/core/plugins/hooks.service'
'filter:api.video-watch.video.get.result'
)
- forkJoin([ videoObs, this.videoCaptionService.listCaptions(videoId) ])
- .subscribe({
- next: ([ video, captionsResult ]) => {
- const queryParams = this.route.snapshot.queryParams
+ forkJoin([
+ videoObs,
+ this.videoCaptionService.listCaptions(videoId),
+ this.userService.getAnonymousOrLoggedUser()
+ ]).subscribe({
+ next: ([ video, captionsResult, loggedInOrAnonymousUser ]) => {
+ const queryParams = this.route.snapshot.queryParams
- const urlOptions = {
- resume: queryParams.resume,
+ const urlOptions = {
+ resume: queryParams.resume,
- startTime: queryParams.start,
- stopTime: queryParams.stop,
+ startTime: queryParams.start,
+ stopTime: queryParams.stop,
- muted: queryParams.muted,
- loop: queryParams.loop,
- subtitle: queryParams.subtitle,
+ muted: queryParams.muted,
+ loop: queryParams.loop,
+ subtitle: queryParams.subtitle,
- playerMode: queryParams.mode,
- peertubeLink: false
- }
+ playerMode: queryParams.mode,
+ peertubeLink: false
+ }
- this.onVideoFetched(video, captionsResult.data, urlOptions)
- .catch(err => this.handleGlobalError(err))
- },
+ this.onVideoFetched({ video, videoCaptions: captionsResult.data, loggedInOrAnonymousUser, urlOptions })
+ .catch(err => this.handleGlobalError(err))
+ },
- error: err => this.handleRequestError(err)
- })
+ error: err => this.handleRequestError(err)
+ })
}
private loadPlaylist (playlistId: string) {
this.notifier.error(errorMessage)
}
- private async onVideoFetched (
- video: VideoDetails,
- videoCaptions: VideoCaption[],
+ private async onVideoFetched (options: {
+ video: VideoDetails
+ videoCaptions: VideoCaption[]
urlOptions: URLOptions
- ) {
+ loggedInOrAnonymousUser: User
+ }) {
+ const { video, videoCaptions, urlOptions, loggedInOrAnonymousUser } = options
+
this.subscribeToLiveEventsIfNeeded(this.video, video)
this.video = video
if (res === false) return this.location.back()
}
- this.buildPlayer(urlOptions)
+ this.buildPlayer(urlOptions, loggedInOrAnonymousUser)
.catch(err => console.error('Cannot build the player', err))
this.setOpenGraphTags()
this.hooks.runAction('action:video-watch.video.loaded', 'video-watch', hookOptions)
}
- private async buildPlayer (urlOptions: URLOptions) {
+ private async buildPlayer (urlOptions: URLOptions, loggedInOrAnonymousUser: User) {
// Flush old player if needed
this.flushPlayer()
video: this.video,
videoCaptions: this.videoCaptions,
urlOptions,
+ loggedInOrAnonymousUser,
user: this.user
}
const { playerMode, playerOptions } = await this.hooks.wrapFun(
video: VideoDetails
videoCaptions: VideoCaption[]
urlOptions: CustomizationOptions & { playerMode: PlayerMode }
+ loggedInOrAnonymousUser: User
user?: AuthUser
}) {
- const { video, videoCaptions, urlOptions, user } = params
+ const { video, videoCaptions, urlOptions, loggedInOrAnonymousUser, user } = params
const getStartTime = () => {
const byUrl = urlOptions.startTime !== undefined
const options: PeertubePlayerManagerOptions = {
common: {
autoplay: this.isAutoplay(),
+ p2pEnabled: isP2PEnabled(video, this.serverConfig, loggedInOrAnonymousUser.p2pEnabled),
+
nextVideo: () => this.playNextVideoInAngularZone(),
playerElement: this.playerElement,
ScrollService,
ServerService,
ThemeService,
- User
+ User,
+ UserLocalStorageService
} from '@app/core'
import { HooksService } from '@app/core/plugins/hooks.service'
import { PluginService } from '@app/core/plugins/plugin.service'
private ngbConfig: NgbConfig,
private loadingBar: LoadingBarService,
private scrollService: ScrollService,
+ private userLocalStorage: UserLocalStorageService,
public menu: MenuService
) {
this.ngbConfig.animation = false
ngOnInit () {
document.getElementById('incompatible-browser').className += ' browser-ok'
+ this.loadUser()
+
this.serverConfig = this.serverService.getHTMLConfig()
this.hooks.runAction('action:application.init', 'common')
}, undefined, $localize`Go to the videos upload page`)
])
}
+
+ private loadUser () {
+ const tokens = this.userLocalStorage.getTokens()
+ if (!tokens) return
+
+ const user = this.userLocalStorage.getLoggedInUser()
+ if (!user) return
+
+ // Initialize user
+ this.authService.buildAuthUser(user, tokens)
+ }
}
import { Observable, of } from 'rxjs'
import { map } from 'rxjs/operators'
import { User } from '@app/core/users/user.model'
-import {
- flushUserInfoFromLocalStorage,
- getUserInfoFromLocalStorage,
- saveUserInfoIntoLocalStorage,
- TokenOptions,
- Tokens
-} from '@root-helpers/users'
+import { UserTokens } from '@root-helpers/users'
import { hasUserRight } from '@shared/core-utils/users'
import {
MyUser as ServerMyUserModel,
} from '@shared/models'
export class AuthUser extends User implements ServerMyUserModel {
- tokens: Tokens
+ tokens: UserTokens
specialPlaylists: MyUserSpecialPlaylist[]
canSeeVideosLink = true
- static load () {
- const tokens = Tokens.load()
- if (!tokens) return null
-
- const userInfo = getUserInfoFromLocalStorage()
- if (!userInfo) return null
-
- return new AuthUser(userInfo, tokens)
- }
-
- static flush () {
- flushUserInfoFromLocalStorage()
-
- Tokens.flush()
- }
-
- constructor (userHash: Partial<ServerMyUserModel>, hashTokens: TokenOptions) {
+ constructor (userHash: Partial<ServerMyUserModel>, hashTokens: Partial<UserTokens>) {
super(userHash)
- this.tokens = new Tokens(hashTokens)
+ this.tokens = new UserTokens(hashTokens)
this.specialPlaylists = userHash.specialPlaylists
}
return user.role === UserRole.USER
}
- save () {
- saveUserInfoIntoLocalStorage({
- id: this.id,
- username: this.username,
- email: this.email,
- role: this.role,
- nsfwPolicy: this.nsfwPolicy,
- webTorrentEnabled: this.webTorrentEnabled,
- autoPlayVideo: this.autoPlayVideo
- })
-
- this.tokens.save()
- }
-
computeCanSeeVideosLink (quotaObservable: Observable<UserVideoQuota>): Observable<boolean> {
if (!this.isUploadDisabled()) {
this.canSeeVideosLink = true
import { Injectable } from '@angular/core'
import { Router } from '@angular/router'
import { Notifier } from '@app/core/notification/notifier.service'
-import { objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index'
+import { objectToUrlEncoded, peertubeLocalStorage, UserTokens } from '@root-helpers/index'
import { HttpStatusCode, MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models'
import { environment } from '../../../environments/environment'
import { RestExtractor } from '../rest/rest-extractor.service'
loginChangedSource: Observable<AuthStatus>
userInformationLoaded = new ReplaySubject<boolean>(1)
+ tokensRefreshed = new ReplaySubject<void>(1)
hotkeys: Hotkey[]
private clientId: string = peertubeLocalStorage.getItem(AuthService.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_ID)
this.loginChanged = new Subject<AuthStatus>()
this.loginChangedSource = this.loginChanged.asObservable()
- // Return null if there is nothing to load
- this.user = AuthUser.load()
-
// Set HotKeys
this.hotkeys = [
new Hotkey('m s', (event: KeyboardEvent): boolean => {
]
}
+ buildAuthUser (userInfo: Partial<User>, tokens: UserTokens) {
+ this.user = new AuthUser(userInfo, tokens)
+ }
+
loadClientCredentials () {
// Fetch the client_id/client_secret
this.http.get<OAuthClientLocal>(AuthService.BASE_CLIENT_URL)
this.user = null
- AuthUser.flush()
-
this.setStatus(AuthStatus.LoggedOut)
this.hotkeysService.remove(this.hotkeys)
.subscribe({
next: res => {
this.user.patch(res)
- this.user.save()
this.userInformationLoaded.next(true)
}
}
this.user = new AuthUser(obj, hashTokens)
- this.user.save()
this.setStatus(AuthStatus.LoggedIn)
this.userInformationLoaded.next(true)
private handleRefreshToken (obj: UserRefreshToken) {
this.user.refreshTokens(obj.access_token, obj.refresh_token)
- this.user.save()
+ this.tokensRefreshed.next()
}
private setStatus (status: AuthStatus) {
import { ScopedTokensService } from './scoped-tokens'
import { ServerService } from './server'
import { ThemeService } from './theme'
-import { UserService } from './users'
+import { UserLocalStorageService, UserService } from './users'
import { LocalStorageService, ScreenService, SessionStorageService } from './wrappers'
@NgModule({
RestService,
UserService,
+ UserLocalStorageService,
ScreenService,
LocalStorageService,
+export * from './user-local-storage.service'
export * from './user.model'
export * from './user.service'
--- /dev/null
+
+import { filter, throttleTime } from 'rxjs'
+import { Injectable } from '@angular/core'
+import { AuthService, AuthStatus } from '@app/core/auth'
+import { UserLocalStorageKeys, UserTokens } from '@root-helpers/users'
+import { getBoolOrDefault } from '@root-helpers/local-storage-utils'
+import { UserRole, UserUpdateMe } from '@shared/models'
+import { NSFWPolicyType } from '@shared/models/videos'
+import { ServerService } from '../server'
+import { LocalStorageService } from '../wrappers/storage.service'
+
+@Injectable()
+export class UserLocalStorageService {
+
+ constructor (
+ private authService: AuthService,
+ private server: ServerService,
+ private localStorageService: LocalStorageService
+ ) {
+ this.authService.userInformationLoaded.subscribe({
+ next: () => {
+ const user = this.authService.getUser()
+
+ this.setLoggedInUser(user)
+ this.setUserInfo(user)
+ this.setTokens(user.tokens)
+ }
+ })
+
+ this.authService.loginChangedSource
+ .pipe(filter(status => status === AuthStatus.LoggedOut))
+ .subscribe({
+ next: () => {
+ this.flushLoggedInUser()
+ this.flushUserInfo()
+ this.flushTokens()
+ }
+ })
+
+ this.authService.tokensRefreshed
+ .subscribe({
+ next: () => {
+ const user = this.authService.getUser()
+
+ this.setTokens(user.tokens)
+ }
+ })
+ }
+
+ // ---------------------------------------------------------------------------
+
+ getLoggedInUser () {
+ const usernameLocalStorage = this.localStorageService.getItem(UserLocalStorageKeys.USERNAME)
+
+ if (!usernameLocalStorage) return undefined
+
+ return {
+ id: parseInt(this.localStorageService.getItem(UserLocalStorageKeys.ID), 10),
+ username: this.localStorageService.getItem(UserLocalStorageKeys.USERNAME),
+ email: this.localStorageService.getItem(UserLocalStorageKeys.EMAIL),
+ role: parseInt(this.localStorageService.getItem(UserLocalStorageKeys.ROLE), 10) as UserRole,
+
+ ...this.getUserInfo()
+ }
+ }
+
+ setLoggedInUser (user: {
+ id: number
+ username: string
+ email: string
+ role: UserRole
+ }) {
+ this.localStorageService.setItem(UserLocalStorageKeys.ID, user.id.toString())
+ this.localStorageService.setItem(UserLocalStorageKeys.USERNAME, user.username)
+ this.localStorageService.setItem(UserLocalStorageKeys.EMAIL, user.email)
+ this.localStorageService.setItem(UserLocalStorageKeys.ROLE, user.role.toString())
+ }
+
+ flushLoggedInUser () {
+ this.localStorageService.removeItem(UserLocalStorageKeys.ID)
+ this.localStorageService.removeItem(UserLocalStorageKeys.USERNAME)
+ this.localStorageService.removeItem(UserLocalStorageKeys.EMAIL)
+ this.localStorageService.removeItem(UserLocalStorageKeys.ROLE)
+ }
+
+ // ---------------------------------------------------------------------------
+
+ getUserInfo () {
+ let videoLanguages: string[]
+
+ try {
+ const languagesString = this.localStorageService.getItem(UserLocalStorageKeys.VIDEO_LANGUAGES)
+ videoLanguages = languagesString && languagesString !== 'undefined'
+ ? JSON.parse(languagesString)
+ : null
+ } catch (err) {
+ videoLanguages = null
+ console.error('Cannot parse desired video languages from localStorage.', err)
+ }
+
+ const htmlConfig = this.server.getHTMLConfig()
+
+ const defaultNSFWPolicy = htmlConfig.instance.defaultNSFWPolicy
+ const defaultP2PEnabled = htmlConfig.defaults.p2p.enabled
+
+ return {
+ nsfwPolicy: this.localStorageService.getItem<NSFWPolicyType>(UserLocalStorageKeys.NSFW_POLICY) || defaultNSFWPolicy,
+ p2pEnabled: getBoolOrDefault(this.localStorageService.getItem(UserLocalStorageKeys.P2P_ENABLED), defaultP2PEnabled),
+ theme: this.localStorageService.getItem(UserLocalStorageKeys.THEME) || 'instance-default',
+ videoLanguages,
+
+ autoPlayVideo: getBoolOrDefault(this.localStorageService.getItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO), true),
+ autoPlayNextVideo: getBoolOrDefault(this.localStorageService.getItem(UserLocalStorageKeys.AUTO_PLAY_NEXT_VIDEO), false),
+ autoPlayNextVideoPlaylist: getBoolOrDefault(this.localStorageService.getItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST), true)
+ }
+ }
+
+ setUserInfo (profile: UserUpdateMe) {
+ const localStorageKeys: { [ id in keyof UserUpdateMe ]: string } = {
+ nsfwPolicy: UserLocalStorageKeys.NSFW_POLICY,
+ p2pEnabled: UserLocalStorageKeys.P2P_ENABLED,
+ autoPlayNextVideo: UserLocalStorageKeys.AUTO_PLAY_VIDEO,
+ autoPlayNextVideoPlaylist: UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST,
+ theme: UserLocalStorageKeys.THEME,
+ videoLanguages: UserLocalStorageKeys.VIDEO_LANGUAGES
+ }
+
+ const obj = Object.keys(localStorageKeys)
+ .filter(key => key in profile)
+ .map(key => ([ localStorageKeys[key], profile[key] ]))
+
+ for (const [ key, value ] of obj) {
+ try {
+ if (value === undefined) {
+ this.localStorageService.removeItem(key)
+ continue
+ }
+
+ const localStorageValue = typeof value === 'string'
+ ? value
+ : JSON.stringify(value)
+
+ this.localStorageService.setItem(key, localStorageValue)
+ } catch (err) {
+ console.error(`Cannot set ${key}->${value} in localStorage. Likely due to a value impossible to stringify.`, err)
+ }
+ }
+ }
+
+ flushUserInfo () {
+ this.localStorageService.removeItem(UserLocalStorageKeys.NSFW_POLICY)
+ this.localStorageService.removeItem(UserLocalStorageKeys.P2P_ENABLED)
+ this.localStorageService.removeItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO)
+ this.localStorageService.removeItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST)
+ this.localStorageService.removeItem(UserLocalStorageKeys.THEME)
+ this.localStorageService.removeItem(UserLocalStorageKeys.VIDEO_LANGUAGES)
+ }
+
+ listenUserInfoChange () {
+ return this.localStorageService.watch([
+ UserLocalStorageKeys.NSFW_POLICY,
+ UserLocalStorageKeys.P2P_ENABLED,
+ UserLocalStorageKeys.AUTO_PLAY_VIDEO,
+ UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST,
+ UserLocalStorageKeys.THEME,
+ UserLocalStorageKeys.VIDEO_LANGUAGES
+ ]).pipe(
+ throttleTime(200),
+ filter(() => this.authService.isLoggedIn() !== true)
+ )
+ }
+
+ // ---------------------------------------------------------------------------
+
+ getTokens () {
+ return UserTokens.getUserTokens(this.localStorageService)
+ }
+
+ setTokens (tokens: UserTokens) {
+ UserTokens.saveToLocalStorage(this.localStorageService, tokens)
+ }
+
+ flushTokens () {
+ UserTokens.flushLocalStorage(this.localStorageService)
+ }
+}
autoPlayVideo: boolean
autoPlayNextVideo: boolean
autoPlayNextVideoPlaylist: boolean
- webTorrentEnabled: boolean
+
+ p2pEnabled: boolean
+ // FIXME: deprecated in 4.1
+ webTorrentEnabled: never
+
videosHistoryEnabled: boolean
videoLanguages: string[]
this.videoCommentsCount = hash.videoCommentsCount
this.nsfwPolicy = hash.nsfwPolicy
- this.webTorrentEnabled = hash.webTorrentEnabled
+ this.p2pEnabled = hash.p2pEnabled
this.autoPlayVideo = hash.autoPlayVideo
this.autoPlayNextVideo = hash.autoPlayNextVideo
this.autoPlayNextVideoPlaylist = hash.autoPlayNextVideoPlaylist
import { SortMeta } from 'primeng/api'
import { from, Observable, of } from 'rxjs'
-import { catchError, concatMap, filter, first, map, shareReplay, tap, throttleTime, toArray } from 'rxjs/operators'
+import { catchError, concatMap, first, map, shareReplay, tap, toArray } from 'rxjs/operators'
import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { AuthService } from '@app/core/auth'
import { getBytes } from '@root-helpers/bytes'
-import { UserLocalStorageKeys } from '@root-helpers/users'
import {
ActorImage,
ResultList,
UserUpdateMe,
UserVideoQuota
} from '@shared/models'
-import { ServerService } from '../'
import { environment } from '../../../environments/environment'
import { RestExtractor, RestPagination, RestService } from '../rest'
-import { LocalStorageService, SessionStorageService } from '../wrappers/storage.service'
+import { UserLocalStorageService } from './'
import { User } from './user.model'
@Injectable()
constructor (
private authHttp: HttpClient,
- private server: ServerService,
private authService: AuthService,
private restExtractor: RestExtractor,
private restService: RestService,
- private localStorageService: LocalStorageService,
- private sessionStorageService: SessionStorageService
+ private userLocalStorageService: UserLocalStorageService
) { }
hasSignupInThisSession () {
)
}
+ // ---------------------------------------------------------------------------
+
+ updateMyAnonymousProfile (profile: UserUpdateMe) {
+ this.userLocalStorageService.setUserInfo(profile)
+ }
+
+ listenAnonymousUpdate () {
+ return this.userLocalStorageService.listenUserInfoChange()
+ .pipe(map(() => this.getAnonymousUser()))
+ }
+
+ getAnonymousUser () {
+ return new User(this.userLocalStorageService.getUserInfo())
+ }
+
+ // ---------------------------------------------------------------------------
+
updateMyProfile (profile: UserUpdateMe) {
const url = UserService.BASE_USERS_URL + 'me'
)
}
- updateMyAnonymousProfile (profile: UserUpdateMe) {
- const localStorageKeys: { [ id in keyof UserUpdateMe ]: string } = {
- nsfwPolicy: UserLocalStorageKeys.NSFW_POLICY,
- webTorrentEnabled: UserLocalStorageKeys.WEBTORRENT_ENABLED,
- autoPlayNextVideo: UserLocalStorageKeys.AUTO_PLAY_VIDEO,
- autoPlayNextVideoPlaylist: UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST,
- theme: UserLocalStorageKeys.THEME,
- videoLanguages: UserLocalStorageKeys.VIDEO_LANGUAGES
- }
-
- const obj = Object.keys(localStorageKeys)
- .filter(key => key in profile)
- .map(key => ([ localStorageKeys[key], profile[key] ]))
-
- for (const [ key, value ] of obj) {
- try {
- if (value === undefined) {
- this.localStorageService.removeItem(key)
- continue
- }
-
- const localStorageValue = typeof value === 'string'
- ? value
- : JSON.stringify(value)
-
- this.localStorageService.setItem(key, localStorageValue)
- } catch (err) {
- console.error(`Cannot set ${key}->${value} in localStorage. Likely due to a value impossible to stringify.`, err)
- }
- }
- }
-
- listenAnonymousUpdate () {
- return this.localStorageService.watch([
- UserLocalStorageKeys.NSFW_POLICY,
- UserLocalStorageKeys.WEBTORRENT_ENABLED,
- UserLocalStorageKeys.AUTO_PLAY_VIDEO,
- UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST,
- UserLocalStorageKeys.THEME,
- UserLocalStorageKeys.VIDEO_LANGUAGES
- ]).pipe(
- throttleTime(200),
- filter(() => this.authService.isLoggedIn() !== true),
- map(() => this.getAnonymousUser())
- )
- }
-
deleteMe () {
const url = UserService.BASE_USERS_URL + 'me'
.pipe(catchError(err => this.restExtractor.handleError(err)))
}
- getAnonymousUser () {
- let videoLanguages: string[]
-
- try {
- const languagesString = this.localStorageService.getItem(UserLocalStorageKeys.VIDEO_LANGUAGES)
- videoLanguages = languagesString && languagesString !== 'undefined'
- ? JSON.parse(languagesString)
- : null
- } catch (err) {
- videoLanguages = null
- console.error('Cannot parse desired video languages from localStorage.', err)
- }
-
- const defaultNSFWPolicy = this.server.getHTMLConfig().instance.defaultNSFWPolicy
-
- return new User({
- // local storage keys
- nsfwPolicy: this.localStorageService.getItem(UserLocalStorageKeys.NSFW_POLICY) || defaultNSFWPolicy,
- webTorrentEnabled: this.localStorageService.getItem(UserLocalStorageKeys.WEBTORRENT_ENABLED) !== 'false',
- theme: this.localStorageService.getItem(UserLocalStorageKeys.THEME) || 'instance-default',
- videoLanguages,
-
- autoPlayNextVideoPlaylist: this.localStorageService.getItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO_PLAYLIST) !== 'false',
- autoPlayVideo: this.localStorageService.getItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO) === 'true',
-
- // session storage keys
- autoPlayNextVideo: this.sessionStorageService.getItem(UserLocalStorageKeys.SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO) === 'true'
- })
- }
-
getUsers (parameters: {
pagination: RestPagination
sort: SortMeta
<my-global-icon iconName="p2p" aria-hidden="true"></my-global-icon>
<ng-container i18n>Help share videos</ng-container>
- <my-input-switch class="ml-auto" [checked]="user.webTorrentEnabled"></my-input-switch>
+ <my-input-switch class="ml-auto" [checked]="user.p2pEnabled"></my-input-switch>
</a>
<div class="dropdown-divider"></div>
toggleUseP2P () {
if (!this.user) return
- this.user.webTorrentEnabled = !this.user.webTorrentEnabled
+ this.user.p2pEnabled = !this.user.p2pEnabled
- this.userService.updateMyProfile({ webTorrentEnabled: this.user.webTorrentEnabled })
+ this.userService.updateMyProfile({ p2pEnabled: this.user.p2pEnabled })
.subscribe(() => this.authService.refreshUserInformation())
}
import { Injectable } from '@angular/core'
import { ComponentPaginationLight, RestExtractor, RestPagination, RestService } from '@app/core'
import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main'
-import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import {
ResultList,
Video as VideoServerModel,
private restService: RestService,
private videoService: VideoService,
private playlistService: VideoPlaylistService
- ) {
- // Add ability to override search endpoint if the user updated this local storage key
- const searchUrl = peertubeLocalStorage.getItem('search-url')
- if (searchUrl) SearchService.BASE_SEARCH_URL = searchUrl
- }
+ ) { }
searchVideos (parameters: {
search?: string
<div class="form-group">
<my-peertube-checkbox
- inputName="webTorrentEnabled" formControlName="webTorrentEnabled" [recommended]="true"
+ inputName="p2pEnabled" formControlName="p2pEnabled" [recommended]="true"
i18n-labelText labelText="Help share videos being played"
>
<ng-container ngProjectAs="description">
ngOnInit () {
this.buildForm({
nsfwPolicy: null,
- webTorrentEnabled: null,
+ p2pEnabled: null,
autoPlayVideo: null,
autoPlayNextVideo: null,
videoLanguages: null
this.form.patchValue({
nsfwPolicy: this.user.nsfwPolicy || this.defaultNSFWPolicy,
- webTorrentEnabled: this.user.webTorrentEnabled,
+ p2pEnabled: this.user.p2pEnabled,
autoPlayVideo: this.user.autoPlayVideo === true,
autoPlayNextVideo: this.user.autoPlayNextVideo,
videoLanguages: this.user.videoLanguages
updateDetails (onlyKeys?: string[]) {
const nsfwPolicy = this.form.value['nsfwPolicy']
- const webTorrentEnabled = this.form.value['webTorrentEnabled']
+ const p2pEnabled = this.form.value['p2pEnabled']
const autoPlayVideo = this.form.value['autoPlayVideo']
const autoPlayNextVideo = this.form.value['autoPlayNextVideo']
let details: UserUpdateMe = {
nsfwPolicy,
- webTorrentEnabled,
+ p2pEnabled,
autoPlayVideo,
autoPlayNextVideo,
videoLanguages
return undefined
}
-function getStoredP2PEnabled (): boolean {
- const value = getLocalStorage('webtorrent_enabled')
- if (value !== null && value !== undefined) return value === 'true'
-
- // By default webtorrent is enabled
- return true
-}
-
function getStoredMute () {
const value = getLocalStorage('mute')
if (value !== null && value !== undefined) return value === 'true'
export {
getStoredVolume,
- getStoredP2PEnabled,
getStoredMute,
getStoredTheater,
saveVolumeInStore,
import { RedundancyUrlManager } from './p2p-media-loader/redundancy-url-manager'
import { segmentUrlBuilderFactory } from './p2p-media-loader/segment-url-builder'
import { segmentValidatorFactory } from './p2p-media-loader/segment-validator'
-import { getAverageBandwidthInStore, getStoredP2PEnabled, saveAverageBandwidth } from './peertube-player-local-storage'
+import { getAverageBandwidthInStore, saveAverageBandwidth } from './peertube-player-local-storage'
import {
NextPreviousVideoButtonOptions,
P2PMediaLoaderPluginOptions,
onPlayerElementChange: (element: HTMLVideoElement) => void
autoplay: boolean
+ p2pEnabled: boolean
nextVideo?: () => void
hasNextVideo?: () => boolean
requiredSegmentsPriority: 1,
simultaneousHttpDownloads: 1,
segmentUrlBuilder: segmentUrlBuilderFactory(redundancyUrlManager, 1),
- useP2P: getStoredP2PEnabled(),
+ useP2P: commonOptions.p2pEnabled,
consumeOnly
},
segments: {
const webtorrent = {
autoplay,
+ playerRefusedP2P: commonOptions.p2pEnabled === false,
videoDuration: commonOptions.videoDuration,
playerElement: commonOptions.playerElement,
videoFiles: webtorrentOptions.videoFiles.length !== 0
videoFiles: VideoFile[]
startTime: number | string
+
+ playerRefusedP2P: boolean
}
type P2PMediaLoaderPluginOptions = {
-import { VideoFile } from '@shared/models'
+import { HTMLServerConfig, Video, VideoFile } from '@shared/models'
function toTitleCase (str: string) {
return str.charAt(0).toUpperCase() + str.slice(1)
return !!((window as any).RTCPeerConnection || (window as any).mozRTCPeerConnection || (window as any).webkitRTCPeerConnection) === false
}
+function isP2PEnabled (video: Video, config: HTMLServerConfig, userP2PEnabled: boolean) {
+ if (video.isLocal && config.tracker.enabled === false) return false
+ if (isWebRTCDisabled()) return false
+
+ return userP2PEnabled
+}
+
function isIOS () {
if (/iPad|iPhone|iPod/.test(navigator.platform)) {
return true
getRtcConfig,
toTitleCase,
isWebRTCDisabled,
+ isP2PEnabled,
buildVideoOrPlaylistEmbed,
videoFileMaxByResolution,
import * as WebTorrent from 'webtorrent'
import { timeToInt } from '@shared/core-utils'
import { VideoFile } from '@shared/models'
-import {
- getAverageBandwidthInStore,
- getStoredMute,
- getStoredP2PEnabled,
- getStoredVolume,
- saveAverageBandwidth
-} from '../peertube-player-local-storage'
+import { getAverageBandwidthInStore, getStoredMute, getStoredVolume, saveAverageBandwidth } from '../peertube-player-local-storage'
import { PeerTubeResolution, PlayerNetworkInfo, WebtorrentPluginOptions } from '../peertube-videojs-typings'
import { getRtcConfig, isIOS, videoFileMaxByResolution, videoFileMinByResolution } from '../utils'
import { PeertubeChunkStore } from './peertube-chunk-store'
this.startTime = timeToInt(options.startTime)
- // Disable auto play on iOS
+ // Custom autoplay handled by webtorrent because we lazy play the video
this.autoplay = options.autoplay
- this.playerRefusedP2P = !getStoredP2PEnabled()
+
+ this.playerRefusedP2P = options.playerRefusedP2P
this.videoFiles = options.videoFiles
this.videoDuration = options.videoDuration
export * from './users'
export * from './bytes'
export * from './images'
+export * from './local-storage-utils'
export * from './peertube-web-storage'
export * from './utils'
export * from './plugins-manager'
--- /dev/null
+function getBoolOrDefault (value: string, defaultValue: boolean) {
+ if (value === 'true') return true
+ if (value === 'false') return false
+
+ return defaultValue
+}
+
+export {
+ getBoolOrDefault
+}
export * from './user-local-storage-keys'
-export * from './user-local-storage-manager'
export * from './user-tokens'
export const UserLocalStorageKeys = {
ID: 'id',
+ USERNAME: 'username',
ROLE: 'role',
EMAIL: 'email',
+
VIDEOS_HISTORY_ENABLED: 'videos-history-enabled',
- USERNAME: 'username',
NSFW_POLICY: 'nsfw_policy',
- WEBTORRENT_ENABLED: 'peertube-videojs-' + 'webtorrent_enabled',
+ P2P_ENABLED: 'peertube-videojs-' + 'webtorrent_enabled',
+
AUTO_PLAY_VIDEO: 'auto_play_video',
- SESSION_STORAGE_AUTO_PLAY_NEXT_VIDEO: 'auto_play_next_video',
+ AUTO_PLAY_NEXT_VIDEO: 'auto_play_next_video',
AUTO_PLAY_VIDEO_PLAYLIST: 'auto_play_video_playlist',
+
THEME: 'theme',
LAST_ACTIVE_THEME: 'last_active_theme',
+
VIDEO_LANGUAGES: 'video_languages'
}
+
+export const UserTokenLocalStorageKeys = {
+ ACCESS_TOKEN: 'access_token',
+ REFRESH_TOKEN: 'refresh_token',
+ TOKEN_TYPE: 'token_type'
+}
+++ /dev/null
-import { NSFWPolicyType, UserRole } from '@shared/models'
-import { peertubeLocalStorage } from '../peertube-web-storage'
-import { UserLocalStorageKeys } from './user-local-storage-keys'
-
-function getUserInfoFromLocalStorage () {
- const usernameLocalStorage = peertubeLocalStorage.getItem(UserLocalStorageKeys.USERNAME)
-
- if (!usernameLocalStorage) return undefined
-
- return {
- id: parseInt(peertubeLocalStorage.getItem(UserLocalStorageKeys.ID), 10),
- username: peertubeLocalStorage.getItem(UserLocalStorageKeys.USERNAME),
- email: peertubeLocalStorage.getItem(UserLocalStorageKeys.EMAIL),
- role: parseInt(peertubeLocalStorage.getItem(UserLocalStorageKeys.ROLE), 10) as UserRole,
- nsfwPolicy: peertubeLocalStorage.getItem(UserLocalStorageKeys.NSFW_POLICY) as NSFWPolicyType,
- webTorrentEnabled: peertubeLocalStorage.getItem(UserLocalStorageKeys.WEBTORRENT_ENABLED) === 'true',
- autoPlayVideo: peertubeLocalStorage.getItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO) === 'true',
- videosHistoryEnabled: peertubeLocalStorage.getItem(UserLocalStorageKeys.VIDEOS_HISTORY_ENABLED) === 'true'
- }
-}
-
-function flushUserInfoFromLocalStorage () {
- peertubeLocalStorage.removeItem(UserLocalStorageKeys.ID)
- peertubeLocalStorage.removeItem(UserLocalStorageKeys.USERNAME)
- peertubeLocalStorage.removeItem(UserLocalStorageKeys.EMAIL)
- peertubeLocalStorage.removeItem(UserLocalStorageKeys.ROLE)
- peertubeLocalStorage.removeItem(UserLocalStorageKeys.NSFW_POLICY)
- peertubeLocalStorage.removeItem(UserLocalStorageKeys.WEBTORRENT_ENABLED)
- peertubeLocalStorage.removeItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO)
- peertubeLocalStorage.removeItem(UserLocalStorageKeys.VIDEOS_HISTORY_ENABLED)
-}
-
-function saveUserInfoIntoLocalStorage (info: {
- id: number
- username: string
- email: string
- role: UserRole
- nsfwPolicy: NSFWPolicyType
- webTorrentEnabled: boolean
- autoPlayVideo: boolean
-}) {
- peertubeLocalStorage.setItem(UserLocalStorageKeys.ID, info.id.toString())
- peertubeLocalStorage.setItem(UserLocalStorageKeys.USERNAME, info.username)
- peertubeLocalStorage.setItem(UserLocalStorageKeys.EMAIL, info.email)
- peertubeLocalStorage.setItem(UserLocalStorageKeys.ROLE, info.role.toString())
- peertubeLocalStorage.setItem(UserLocalStorageKeys.NSFW_POLICY, info.nsfwPolicy.toString())
- peertubeLocalStorage.setItem(UserLocalStorageKeys.WEBTORRENT_ENABLED, JSON.stringify(info.webTorrentEnabled))
- peertubeLocalStorage.setItem(UserLocalStorageKeys.AUTO_PLAY_VIDEO, JSON.stringify(info.autoPlayVideo))
-}
-
-export {
- getUserInfoFromLocalStorage,
- saveUserInfoIntoLocalStorage,
- flushUserInfoFromLocalStorage
-}
-import { peertubeLocalStorage } from '../peertube-web-storage'
-
-export type TokenOptions = {
- accessToken: string
- refreshToken: string
- tokenType: string
-}
-
-// Private class only used by User
-export class Tokens {
- private static KEYS = {
- ACCESS_TOKEN: 'access_token',
- REFRESH_TOKEN: 'refresh_token',
- TOKEN_TYPE: 'token_type'
- }
+import { UserTokenLocalStorageKeys } from './user-local-storage-keys'
+export class UserTokens {
accessToken: string
refreshToken: string
tokenType: string
- static load () {
- const accessTokenLocalStorage = peertubeLocalStorage.getItem(this.KEYS.ACCESS_TOKEN)
- const refreshTokenLocalStorage = peertubeLocalStorage.getItem(this.KEYS.REFRESH_TOKEN)
- const tokenTypeLocalStorage = peertubeLocalStorage.getItem(this.KEYS.TOKEN_TYPE)
-
- if (accessTokenLocalStorage && refreshTokenLocalStorage && tokenTypeLocalStorage) {
- return new Tokens({
- accessToken: accessTokenLocalStorage,
- refreshToken: refreshTokenLocalStorage,
- tokenType: tokenTypeLocalStorage
- })
- }
-
- return null
- }
-
- static flush () {
- peertubeLocalStorage.removeItem(this.KEYS.ACCESS_TOKEN)
- peertubeLocalStorage.removeItem(this.KEYS.REFRESH_TOKEN)
- peertubeLocalStorage.removeItem(this.KEYS.TOKEN_TYPE)
- }
-
- constructor (hash?: TokenOptions) {
+ constructor (hash?: Partial<UserTokens>) {
if (hash) {
this.accessToken = hash.accessToken
this.refreshToken = hash.refreshToken
}
}
- save () {
- peertubeLocalStorage.setItem(Tokens.KEYS.ACCESS_TOKEN, this.accessToken)
- peertubeLocalStorage.setItem(Tokens.KEYS.REFRESH_TOKEN, this.refreshToken)
- peertubeLocalStorage.setItem(Tokens.KEYS.TOKEN_TYPE, this.tokenType)
+ static getUserTokens (localStorage: Pick<Storage, 'getItem'>) {
+ const accessTokenLocalStorage = localStorage.getItem(UserTokenLocalStorageKeys.ACCESS_TOKEN)
+ const refreshTokenLocalStorage = localStorage.getItem(UserTokenLocalStorageKeys.REFRESH_TOKEN)
+ const tokenTypeLocalStorage = localStorage.getItem(UserTokenLocalStorageKeys.TOKEN_TYPE)
+
+ if (!accessTokenLocalStorage || !refreshTokenLocalStorage || !tokenTypeLocalStorage) return null
+
+ return new UserTokens({
+ accessToken: accessTokenLocalStorage,
+ refreshToken: refreshTokenLocalStorage,
+ tokenType: tokenTypeLocalStorage
+ })
+ }
+
+ static saveToLocalStorage (localStorage: Pick<Storage, 'setItem'>, tokens: UserTokens) {
+ localStorage.setItem(UserTokenLocalStorageKeys.ACCESS_TOKEN, tokens.accessToken)
+ localStorage.setItem(UserTokenLocalStorageKeys.REFRESH_TOKEN, tokens.refreshToken)
+ localStorage.setItem(UserTokenLocalStorageKeys.TOKEN_TYPE, tokens.tokenType)
+ }
+
+ static flushLocalStorage (localStorage: Pick<Storage, 'removeItem'>) {
+ localStorage.removeItem(UserTokenLocalStorageKeys.ACCESS_TOKEN)
+ localStorage.removeItem(UserTokenLocalStorageKeys.REFRESH_TOKEN)
+ localStorage.removeItem(UserTokenLocalStorageKeys.TOKEN_TYPE)
}
}
OAuth2ErrorCode,
ResultList,
UserRefreshToken,
+ Video,
VideoCaption,
VideoDetails,
VideoPlaylist,
import { P2PMediaLoaderOptions, PeertubePlayerManagerOptions, PlayerMode } from '../../assets/player/peertube-player-manager'
import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
import { TranslationsManager } from '../../assets/player/translations-manager'
+import { isP2PEnabled } from '../../assets/player/utils'
+import { getBoolOrDefault } from '../../root-helpers/local-storage-utils'
import { peertubeLocalStorage } from '../../root-helpers/peertube-web-storage'
import { PluginsManager } from '../../root-helpers/plugins-manager'
-import { Tokens } from '../../root-helpers/users'
+import { UserLocalStorageKeys, UserTokens } from '../../root-helpers/users'
import { objectToUrlEncoded } from '../../root-helpers/utils'
import { RegisterClientHelpers } from '../../types/register-client-option.model'
import { PeerTubeEmbedApi } from './embed-api'
mode: PlayerMode
scope = 'peertube'
- userTokens: Tokens
+ userTokens: UserTokens
headers = new Headers()
LOCAL_STORAGE_OAUTH_CLIENT_KEYS = {
CLIENT_ID: 'client_id',
return res.json()
}).then((obj: UserRefreshToken & { code?: OAuth2ErrorCode }) => {
if (!obj || obj.code === OAuth2ErrorCode.INVALID_GRANT) {
- Tokens.flush()
+ UserTokens.flushLocalStorage(peertubeLocalStorage)
this.removeTokensFromHeaders()
return resolve()
this.userTokens.accessToken = obj.access_token
this.userTokens.refreshToken = obj.refresh_token
- this.userTokens.save()
+ UserTokens.saveToLocalStorage(peertubeLocalStorage, this.userTokens)
this.setHeadersFromTokens()
return refreshingTokenPromise
.catch(() => {
- Tokens.flush()
+ UserTokens.flushLocalStorage(peertubeLocalStorage)
this.removeTokensFromHeaders()
}).then(() => fetch(url, {
}
async init () {
- this.userTokens = Tokens.load()
+ this.userTokens = UserTokens.getUserTokens(peertubeLocalStorage)
await this.initCore()
}
muted: this.muted,
loop: this.loop,
+ p2pEnabled: this.isP2PEnabled(videoInfo),
+
captions: videoCaptions.length !== 0,
subtitle: this.subtitle,
const title = this.title ? videoInfo.name : undefined
- const description = this.warningTitle && (!videoInfo.isLocal || this.config.tracker.enabled)
+ const description = this.warningTitle && this.isP2PEnabled(videoInfo)
? '<span class="text">' + peertubeTranslate('Watching this video may reveal your IP address to others.') + '</span>'
: undefined
translate: (value: string) => Promise.resolve(peertubeTranslate(value, translations))
}
}
+
+ private isP2PEnabled (video: Video) {
+ const userP2PEnabled = getBoolOrDefault(
+ peertubeLocalStorage.getItem(UserLocalStorageKeys.P2P_ENABLED),
+ this.config.defaults.p2p.enabled
+ )
+
+ return isP2PEnabled(video, this.config, userP2PEnabled)
+ }
}
PeerTubeEmbed.main()
# No licence by default
licence: null
+ p2p:
+ # Enable P2P by default
+ # Can be enabled/disabled by anonymous users and logged in users
+ enabled: true
+
# From the project root directory
storage:
tmp: 'storage/tmp/' # Use to download data (imports etc), store uploaded files before and during processing...
enabled: true
tracker:
- # If you disable the tracker, you disable the P2P aspect of PeerTube
+ # If you disable the tracker, you disable the P2P on your PeerTube instance
enabled: true
# Only handle requests on your videos
# If you set this to false it means you have a public tracker
# No licence by default
licence: null
+ p2p:
+ # Enable P2P by default
+ # Can be enabled/disabled by anonymous users and logged in users
+ enabled: true
+
# From the project root directory
storage:
tmp: '/var/www/peertube/storage/tmp/' # Use to download data (imports etc), store uploaded files before and during processing...
password: body.password,
email: body.email,
nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
+ p2pEnabled: CONFIG.DEFAULTS.P2P.ENABLED,
autoPlayVideo: true,
role: body.role,
videoQuota: body.videoQuota,
password: body.password,
email: body.email,
nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
+ p2pEnabled: CONFIG.DEFAULTS.P2P.ENABLED,
autoPlayVideo: true,
role: UserRole.USER,
videoQuota: CONFIG.USER.VIDEO_QUOTA,
const keysToUpdate: (keyof UserUpdateMe & keyof AttributesOnly<UserModel>)[] = [
'password',
'nsfwPolicy',
- 'webTorrentEnabled',
+ 'p2pEnabled',
'autoPlayVideo',
'autoPlayNextVideo',
'autoPlayNextVideoPlaylist',
if (body[key] !== undefined) user.set(key, body[key])
}
+ if (body.p2pEnabled !== undefined) {
+ user.set('p2pEnabled', body.p2pEnabled)
+ } else if (body.webTorrentEnabled !== undefined) { // FIXME: deprecated in 4.1
+ user.set('p2pEnabled', body.webTorrentEnabled)
+ }
+
if (body.email !== undefined) {
if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION) {
user.pendingEmail = body.email
return exists(value) && nsfwPolicies.includes(value)
}
-function isUserWebTorrentEnabledValid (value: any) {
+function isUserP2PEnabledValid (value: any) {
return isBooleanValid(value)
}
isUserAdminFlagsValid,
isUserEmailVerifiedValid,
isUserNSFWPolicyValid,
- isUserWebTorrentEnabledValid,
+ isUserP2PEnabledValid,
isUserAutoPlayVideoValid,
isUserAutoPlayNextVideoValid,
isUserAutoPlayNextVideoPlaylistValid,
COMMENTS_ENABLED: config.get<boolean>('defaults.publish.comments_enabled'),
PRIVACY: config.get<VideoPrivacy>('defaults.publish.privacy'),
LICENCE: config.get<number>('defaults.publish.licence')
+ },
+ P2P: {
+ ENABLED: config.get<boolean>('defaults.p2p.enabled')
}
},
// ---------------------------------------------------------------------------
-const LAST_MIGRATION_VERSION = 670
+const LAST_MIGRATION_VERSION = 675
// ---------------------------------------------------------------------------
role,
verified: true,
nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
+ p2pEnabled: CONFIG.DEFAULTS.P2P.ENABLED,
videoQuota: -1,
videoQuotaDaily: -1
}
--- /dev/null
+import * as Sequelize from 'sequelize'
+
+async function up (utils: {
+ transaction: Sequelize.Transaction
+ queryInterface: Sequelize.QueryInterface
+ sequelize: Sequelize.Sequelize
+ db: any
+}): Promise<void> {
+ await utils.queryInterface.renameColumn('user', 'webTorrentEnabled', 'p2pEnabled')
+
+ await utils.sequelize.query('ALTER TABLE "user" ALTER COLUMN "p2pEnabled" DROP DEFAULT')
+}
+
+function down (options) {
+ throw new Error('Not implemented.')
+}
+
+export {
+ up,
+ down
+}
password: null,
email: options.email,
nsfwPolicy: CONFIG.INSTANCE.DEFAULT_NSFW_POLICY,
+ p2pEnabled: CONFIG.DEFAULTS.P2P.ENABLED,
autoPlayVideo: true,
role: options.role,
videoQuota: CONFIG.USER.VIDEO_QUOTA,
commentsEnabled: CONFIG.DEFAULTS.PUBLISH.COMMENTS_ENABLED,
privacy: CONFIG.DEFAULTS.PUBLISH.PRIVACY,
licence: CONFIG.DEFAULTS.PUBLISH.LICENCE
+ },
+ p2p: {
+ enabled: CONFIG.DEFAULTS.P2P.ENABLED
}
},
isUserDisplayNameValid,
isUserNoModal,
isUserNSFWPolicyValid,
+ isUserP2PEnabledValid,
isUserPasswordValid,
isUserPasswordValidOrEmpty,
isUserRoleValid,
body('autoPlayVideo')
.optional()
.custom(isUserAutoPlayVideoValid).withMessage('Should have a valid automatically plays video attribute'),
+ body('p2pEnabled')
+ .optional()
+ .custom(isUserP2PEnabledValid).withMessage('Should have a valid p2p enabled boolean'),
body('videoLanguages')
.optional()
.custom(isUserVideoLanguages).withMessage('Should have a valid video languages attribute'),
isUserVideoQuotaDailyValid,
isUserVideoQuotaValid,
isUserVideosHistoryEnabledValid,
- isUserWebTorrentEnabledValid
+ isUserP2PEnabledValid
} from '../../helpers/custom-validators/users'
import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto'
import { DEFAULT_USER_THEME_NAME, NSFW_POLICY_TYPES } from '../../initializers/constants'
nsfwPolicy: NSFWPolicyType
@AllowNull(false)
- @Default(true)
- @Is('UserWebTorrentEnabled', value => throwIfNotValid(value, isUserWebTorrentEnabledValid, 'WebTorrent enabled'))
+ @Is('p2pEnabled', value => throwIfNotValid(value, isUserP2PEnabledValid, 'P2P enabled'))
@Column
- webTorrentEnabled: boolean
+ p2pEnabled: boolean
@AllowNull(false)
@Default(true)
emailVerified: this.emailVerified,
nsfwPolicy: this.nsfwPolicy,
- webTorrentEnabled: this.webTorrentEnabled,
+
+ // FIXME: deprecated in 4.1
+ webTorrentEnabled: this.p2pEnabled,
+ p2pEnabled: this.p2pEnabled,
+
videosHistoryEnabled: this.videosHistoryEnabled,
autoPlayVideo: this.autoPlayVideo,
autoPlayNextVideo: this.autoPlayNextVideo,
before(async function () {
this.timeout(30000)
- const overrideConfig = {
- defaults: {
- publish: {
- comments_enabled: false,
- download_enabled: false,
- privacy: VideoPrivacy.INTERNAL,
- licence: 4
- }
- }
- }
-
- server = await createSingleServer(1, overrideConfig)
+ server = await createSingleServer(1)
await setAccessTokensToServers([ server ])
await setDefaultVideoChannel([ server ])
})
describe('Default publish values', function () {
+
+ before(async function () {
+ const overrideConfig = {
+ defaults: {
+ publish: {
+ comments_enabled: false,
+ download_enabled: false,
+ privacy: VideoPrivacy.INTERNAL,
+ licence: 4
+ }
+ }
+ }
+
+ await server.kill()
+ await server.run(overrideConfig)
+ })
+
const attributes = {
name: 'video',
downloadEnabled: undefined,
})
})
+ describe('Default P2P values', function () {
+
+ before(async function () {
+ const overrideConfig = {
+ defaults: {
+ p2p: {
+ enabled: false
+ }
+ }
+ }
+
+ await server.kill()
+ await server.run(overrideConfig)
+ })
+
+ it('Should not have P2P enabled', async function () {
+ const config = await server.config.getConfig()
+
+ expect(config.defaults.p2p.enabled).to.be.false
+ })
+
+ it('Should create a user with this default setting', async function () {
+ await server.users.create({ username: 'user_p2p_1' })
+ const userToken = await server.login.getAccessToken('user_p2p_1')
+
+ const { p2pEnabled } = await server.users.getMyInfo({ token: userToken })
+ expect(p2pEnabled).to.be.false
+ })
+
+ it('Should register a user with this default setting', async function () {
+ await server.users.register({ username: 'user_p2p_2' })
+
+ const userToken = await server.login.getAccessToken('user_p2p_2')
+
+ const { p2pEnabled } = await server.users.getMyInfo({ token: userToken })
+ expect(p2pEnabled).to.be.false
+ })
+ })
+
after(async function () {
await cleanupTests([ server ])
})
})
it('Should upload a video on server 2 and 3 and propagate only the video of server 2', async function () {
- this.timeout(60000)
+ this.timeout(120000)
await servers[1].videos.upload({ attributes: { name: 'server2' } })
await servers[2].videos.upload({ attributes: { name: 'server3' } })
expect(user.autoPlayNextVideo).to.be.true
})
+ it('Should be able to change the p2p attribute', async function () {
+ {
+ await server.users.updateMe({
+ token: userToken,
+ webTorrentEnabled: false
+ })
+
+ const user = await server.users.getMyInfo({ token: userToken })
+ expect(user.p2pEnabled).to.be.false
+ }
+
+ {
+ await server.users.updateMe({
+ token: userToken,
+ p2pEnabled: true
+ })
+
+ const user = await server.users.getMyInfo({ token: userToken })
+ expect(user.p2pEnabled).to.be.true
+ }
+ })
+
it('Should be able to change the email attribute', async function () {
await server.users.updateMe({
token: userToken,
privacy: VideoPrivacy
licence: number
}
+
+ p2p: {
+ enabled: boolean
+ }
}
webadmin: {
description?: string
nsfwPolicy?: NSFWPolicyType
+ // FIXME: deprecated in favour of p2pEnabled in 4.1
webTorrentEnabled?: boolean
+ p2pEnabled?: boolean
+
autoPlayVideo?: boolean
autoPlayNextVideo?: boolean
autoPlayNextVideoPlaylist?: boolean
autoPlayVideo: boolean
autoPlayNextVideo: boolean
autoPlayNextVideoPlaylist: boolean
+
+ // @deprecated in favour of p2pEnabled
webTorrentEnabled: boolean
+ p2pEnabled: boolean
+
videosHistoryEnabled: boolean
videoLanguages: string[]
type: integer
description: The user daily video quota in bytes
example: -1
- webtorrentEnabled:
+ p2pEnabled:
type: boolean
description: Enable P2P in the player
UserWithStats:
- 'true'
- 'false'
- both
- webTorrentEnabled:
+ p2pEnabled:
type: boolean
description: whether to enable P2P in the player or not
autoPlayVideo: