import { SortMeta } from 'primeng/api'
import { Component, OnInit } from '@angular/core'
import { ConfirmService, Notifier, RestPagination, RestTable, ServerService } from '@app/core'
-import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage'
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import { RedundancyService } from '@app/shared/shared-main'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { VideoRedundanciesTarget, VideoRedundancy } from '@shared/models'
@Component({
selector: 'my-video-block-list',
templateUrl: './video-block-list.component.html',
- styleUrls: [ '../../../shared/shared-moderation/moderation.scss', '../../../shared/shared-abuse-list/abuse-list-table.component.scss', './video-block-list.component.scss' ]
+ styleUrls: [ '../../../shared/shared-moderation/moderation.scss', './video-block-list.component.scss' ]
})
export class VideoBlockListComponent extends RestTable implements OnInit, AfterViewInit {
blocklist: (VideoBlacklist & { reasonHtml?: string, embedHtml?: string })[] = []
import { SortMeta } from 'primeng/api'
import { Component, OnInit } from '@angular/core'
import { Notifier, RestPagination, RestTable } from '@app/core'
-import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage'
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { Job, JobState, JobType } from '@shared/models'
import { JobStateClient } from '../../../../types/job-state-client.type'
import { Component, Input } from '@angular/core'
import { Router } from '@angular/router'
import { AuthService, ComponentPagination, LocalStorageService, Notifier, SessionStorageService, UserService } from '@app/core'
-import { peertubeLocalStorage, peertubeSessionStorage } from '@app/helpers/peertube-web-storage'
+import { peertubeLocalStorage, peertubeSessionStorage } from '@root-helpers/peertube-web-storage'
import { VideoPlaylist, VideoPlaylistElement, VideoPlaylistService } from '@app/shared/shared-video-playlist'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { VideoDetails, VideoPlaylistPrivacy } from '@shared/models'
import { AuthService, AuthUser, ConfirmService, MarkdownService, Notifier, RestExtractor, ServerService, UserService } from '@app/core'
import { HooksService } from '@app/core/plugins/hooks.service'
import { RedirectService } from '@app/core/routing/redirect.service'
-import { isXPercentInViewport, peertubeLocalStorage, scrollToTop } from '@app/helpers'
+import { isXPercentInViewport, scrollToTop } from '@app/helpers'
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import { Video, VideoCaptionService, VideoDetails, VideoService } from '@app/shared/shared-main'
import { SubscribeButtonComponent } from '@app/shared/shared-user-subscription'
import { VideoPlaylist, VideoPlaylistService } from '@app/shared/shared-video-playlist'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { BroadcastMessageLevel, getShortLocale, is18nPath, ServerConfig, UserRole } from '@shared/models'
import { MenuService } from './core/menu/menu.service'
-import { peertubeLocalStorage, POP_STATE_MODAL_DISMISS } from './helpers'
+import { POP_STATE_MODAL_DISMISS } from './helpers'
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import { InstanceService } from './shared/shared-instance'
@Component({
import { Observable, of } from 'rxjs'
import { map } from 'rxjs/operators'
import { User } from '@app/core/users/user.model'
-import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage'
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import {
hasUserRight,
MyUser as ServerMyUserModel,
UserRole,
UserVideoQuota
} from '@shared/models'
-
-export type TokenOptions = {
- accessToken: string
- refreshToken: string
- tokenType: string
-}
-
-// Private class only used by User
-class Tokens {
- private static KEYS = {
- ACCESS_TOKEN: 'access_token',
- REFRESH_TOKEN: 'refresh_token',
- TOKEN_TYPE: 'token_type'
- }
-
- 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) {
- if (hash) {
- this.accessToken = hash.accessToken
- this.refreshToken = hash.refreshToken
-
- if (hash.tokenType === 'bearer') {
- this.tokenType = 'Bearer'
- } else {
- this.tokenType = hash.tokenType
- }
- }
- }
-
- 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)
- }
-}
+import { TokenOptions, Tokens } from '../../../root-helpers/pure-auth-user.model'
export class AuthUser extends User implements ServerMyUserModel {
tokens: Tokens
import { Injectable } from '@angular/core'
import { Router } from '@angular/router'
import { Notifier } from '@app/core/notification/notifier.service'
-import { objectToUrlEncoded, peertubeLocalStorage } from '@app/helpers'
+import { objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index'
import { I18n } from '@ngx-translate/i18n-polyfill'
import { MyUser as UserServerModel, OAuthClientLocal, User, UserLogin, UserRefreshToken } from '@shared/models'
import { environment } from '../../../environments/environment'
-import { peertubeLocalStorage } from '@app/helpers/peertube-web-storage'
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import { LazyLoadEvent, SortMeta } from 'primeng/api'
import { RestPagination } from './rest-pagination'
import { Subject } from 'rxjs'
import { first, map, share, shareReplay, switchMap, tap } from 'rxjs/operators'
import { HttpClient } from '@angular/common/http'
import { Inject, Injectable, LOCALE_ID } from '@angular/core'
-import { getDevLocale, isOnDevLocale, peertubeLocalStorage, sortBy } from '@app/helpers'
+import { getDevLocale, isOnDevLocale, sortBy } from '@app/helpers'
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import {
getCompleteLocale,
isDefaultLocale,
UserRole,
VideoChannel
} from '@shared/models'
+import { UserKeys } from '@root-helpers/user-keys'
export class User implements UserServerModel {
- static KEYS = {
- ID: 'id',
- ROLE: 'role',
- EMAIL: 'email',
- VIDEOS_HISTORY_ENABLED: 'videos-history-enabled',
- USERNAME: 'username',
- NSFW_POLICY: 'nsfw_policy',
- WEBTORRENT_ENABLED: 'peertube-videojs-' + 'webtorrent_enabled',
- AUTO_PLAY_VIDEO: 'auto_play_video',
- SESSION_STORAGE_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'
- }
+ static KEYS = UserKeys
id: number
username: string
import { Observable, Subject } from 'rxjs'
import { filter } from 'rxjs/operators'
import { Injectable } from '@angular/core'
-import { peertubeLocalStorage, peertubeSessionStorage } from '@app/helpers'
+import { peertubeLocalStorage, peertubeSessionStorage } from '@root-helpers/peertube-web-storage'
abstract class StorageService {
protected instance: Storage
export * from './locales'
export * from './constants'
export * from './i18n-utils'
-export * from './peertube-web-storage'
export * from './utils'
export * from './zone'
return Object.assign({}, target, source)
}
-function objectToUrlEncoded (obj: any) {
- const str: string[] = []
- for (const key of Object.keys(obj)) {
- str.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]))
- }
-
- return str.join('&')
-}
-
// Thanks: https://gist.github.com/ghinda/8442a57f22099bdb2e34
function objectToFormData (obj: any, form?: FormData, namespace?: string) {
const fd = form || new FormData()
sortBy,
durationToString,
lineFeedToHtml,
- objectToUrlEncoded,
getParameterByName,
populateAsyncUserVideoChannels,
getAbsoluteAPIUrl,
-@import 'variables';
-@import 'mixins';
-@import 'miniature';
-
-.table-video-link {
- @include disable-outline;
-
- position: relative;
- top: 3px;
-}
-
-.table-comment-link,
-.table-account-link {
- @include disable-outline;
-
- color: var(--mainForegroundColor);
-
- ::ng-deep p:last-child {
- margin: 0;
- }
-}
-
-.table-account-link {
- display: flex;
- flex-direction: column;
-}
-
.comment-flagged-account,
.account-flagged-handle {
font-size: 11px;
color: var(--greyForegroundColor);
}
-.table-video {
- display: inline-flex;
-
- .table-video-image {
- @include miniature-thumbnail;
-
- $image-height: 45px;
-
- height: $image-height;
- width: #{(16/9) * $image-height};
- margin-right: 0.5rem;
- border-radius: 2px;
- border: none;
- background: transparent;
- display: inline-flex;
- justify-content: center;
- align-items: center;
- position: relative;
-
- img {
- height: 100%;
- width: 100%;
- border-radius: 2px;
- }
-
- span {
- color: pvar(--inputPlaceholderColor);
- }
-
- .table-video-image-label {
- @include static-thumbnail-overlay;
- position: absolute;
- border-radius: 3px;
- font-size: 10px;
- padding: 0 3px;
- line-height: 1.3;
- bottom: 2px;
- right: 2px;
- }
- }
-
- .table-video-text {
- display: inline-flex;
- flex-direction: column;
- justify-content: center;
- font-size: 90%;
- color: pvar(--mainForegroundColor);
- line-height: 1rem;
-
- div .glyphicon {
- font-size: 80%;
- color: gray;
- margin-left: 0.1rem;
- }
-
- div + div {
- color: var(--greyForegroundColor);
- font-size: 11px;
- }
- }
-}
-
.abuse-states .glyphicon-comment {
margin-left: 0.5rem;
}
display: block !important;
}
}
+
+.table-video-link {
+ @include disable-outline;
+
+ position: relative;
+ top: 3px;
+}
+
+.table-comment-link,
+.table-account-link {
+ @include disable-outline;
+
+ color: var(--mainForegroundColor);
+
+ ::ng-deep p:last-child {
+ margin: 0;
+ }
+}
+
+.table-account-link {
+ display: flex;
+ flex-direction: column;
+}
+
+.table-video {
+ display: inline-flex;
+
+ .table-video-image {
+ @include miniature-thumbnail;
+
+ $image-height: 45px;
+
+ height: $image-height;
+ width: #{(16/9) * $image-height};
+ margin-right: 0.5rem;
+ border-radius: 2px;
+ border: none;
+ background: transparent;
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ position: relative;
+
+ img {
+ height: 100%;
+ width: 100%;
+ border-radius: 2px;
+ }
+
+ span {
+ color: pvar(--inputPlaceholderColor);
+ }
+
+ .table-video-image-label {
+ @include static-thumbnail-overlay;
+ position: absolute;
+ border-radius: 3px;
+ font-size: 10px;
+ padding: 0 3px;
+ line-height: 1.3;
+ bottom: 2px;
+ right: 2px;
+ }
+ }
+
+ .table-video-text {
+ display: inline-flex;
+ flex-direction: column;
+ justify-content: center;
+ font-size: 90%;
+ color: pvar(--mainForegroundColor);
+ line-height: 1rem;
+
+ div .glyphicon {
+ font-size: 80%;
+ color: gray;
+ margin-left: 0.1rem;
+ }
+
+ div + div {
+ color: var(--greyForegroundColor);
+ font-size: 11px;
+ }
+ }
+}
import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { ComponentPaginationLight, RestExtractor, RestPagination, RestService } from '@app/core'
-import { peertubeLocalStorage } from '@app/helpers'
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
import { Video, VideoChannel, VideoChannelService, VideoService } from '@app/shared/shared-main'
import { ResultList, SearchTargetType, Video as VideoServerModel, VideoChannel as VideoChannelServerModel } from '@shared/models'
import { environment } from '../../../environments/environment'
--- /dev/null
+export * from './peertube-web-storage'
+export * from './utils'
+export * from './user-keys'
+export * from './pure-auth-user.model'
--- /dev/null
+// pure version of auth-user, that doesn't import app packages
+import { peertubeLocalStorage } from '@root-helpers/peertube-web-storage'
+import {
+ MyUser as ServerMyUserModel,
+ MyUserSpecialPlaylist,
+ NSFWPolicyType,
+ UserRole
+} from '@shared/models'
+import { UserKeys } from '@root-helpers/user-keys'
+
+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'
+ }
+
+ 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) {
+ if (hash) {
+ this.accessToken = hash.accessToken
+ this.refreshToken = hash.refreshToken
+
+ if (hash.tokenType === 'bearer') {
+ this.tokenType = 'Bearer'
+ } else {
+ this.tokenType = hash.tokenType
+ }
+ }
+ }
+
+ 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)
+ }
+}
+
+export class PureAuthUser {
+ tokens: Tokens
+ specialPlaylists: MyUserSpecialPlaylist[]
+
+ canSeeVideosLink = true
+
+ static load () {
+ const usernameLocalStorage = peertubeLocalStorage.getItem(UserKeys.USERNAME)
+ if (usernameLocalStorage) {
+ return new PureAuthUser(
+ {
+ id: parseInt(peertubeLocalStorage.getItem(UserKeys.ID), 10),
+ username: peertubeLocalStorage.getItem(UserKeys.USERNAME),
+ email: peertubeLocalStorage.getItem(UserKeys.EMAIL),
+ role: parseInt(peertubeLocalStorage.getItem(UserKeys.ROLE), 10) as UserRole,
+ nsfwPolicy: peertubeLocalStorage.getItem(UserKeys.NSFW_POLICY) as NSFWPolicyType,
+ webTorrentEnabled: peertubeLocalStorage.getItem(UserKeys.WEBTORRENT_ENABLED) === 'true',
+ autoPlayVideo: peertubeLocalStorage.getItem(UserKeys.AUTO_PLAY_VIDEO) === 'true',
+ videosHistoryEnabled: peertubeLocalStorage.getItem(UserKeys.VIDEOS_HISTORY_ENABLED) === 'true'
+ },
+ Tokens.load()
+ )
+ }
+
+ return null
+ }
+
+ constructor (userHash: Partial<ServerMyUserModel>, hashTokens: TokenOptions) {
+ this.tokens = new Tokens(hashTokens)
+ this.specialPlaylists = userHash.specialPlaylists
+ }
+
+ getAccessToken () {
+ return this.tokens.accessToken
+ }
+
+ getRefreshToken () {
+ return this.tokens.refreshToken
+ }
+
+ getTokenType () {
+ return this.tokens.tokenType
+ }
+
+ refreshTokens (accessToken: string, refreshToken: string) {
+ this.tokens.accessToken = accessToken
+ this.tokens.refreshToken = refreshToken
+ }
+
+ save () {
+ this.tokens.save()
+ }
+}
--- /dev/null
+export const UserKeys = {
+ ID: 'id',
+ ROLE: 'role',
+ EMAIL: 'email',
+ VIDEOS_HISTORY_ENABLED: 'videos-history-enabled',
+ USERNAME: 'username',
+ NSFW_POLICY: 'nsfw_policy',
+ WEBTORRENT_ENABLED: 'peertube-videojs-' + 'webtorrent_enabled',
+ AUTO_PLAY_VIDEO: 'auto_play_video',
+ SESSION_STORAGE_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'
+}
--- /dev/null
+function objectToUrlEncoded (obj: any) {
+ const str: string[] = []
+ for (const key of Object.keys(obj)) {
+ str.push(encodeURIComponent(key) + '=' + encodeURIComponent(obj[key]))
+ }
+
+ return str.join('&')
+}
+
+export {
+ objectToUrlEncoded
+}
peertubeTranslate,
ResultList,
ServerConfig,
- VideoDetails
+ VideoDetails,
+ UserRefreshToken
} from '../../../../shared'
import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model'
import {
import { TranslationsManager } from '../../assets/player/translations-manager'
import videojs from 'video.js'
import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
-import { AuthUser } from '@app/core/auth/auth-user.model'
+import { PureAuthUser, objectToUrlEncoded, peertubeLocalStorage } from '@root-helpers/index'
type Translations = { [ id: string ]: string }
mode: PlayerMode
scope = 'peertube'
- user: AuthUser
+ user: PureAuthUser
headers = new Headers()
+ LOCAL_STORAGE_OAUTH_CLIENT_KEYS = {
+ CLIENT_ID: 'client_id',
+ CLIENT_SECRET: 'client_secret'
+ }
static async main () {
const videoContainerId = 'video-container'
return window.location.origin + '/api/v1/videos/' + id
}
+ refreshFetch (url: string, options?: Object) {
+ return fetch(url, options)
+ .then((res: Response) => {
+ if (res.status !== 401) return res
+
+ // 401 unauthorized is not catch-ed, but then-ed
+ const error = res
+
+ const refreshingTokenPromise = new Promise((resolve, reject) => {
+ const clientId: string = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_ID)
+ const clientSecret: string = peertubeLocalStorage.getItem(this.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_SECRET)
+ const headers = new Headers()
+ headers.set('Content-Type', 'application/x-www-form-urlencoded')
+ const data = {
+ refresh_token: this.user.getRefreshToken(),
+ client_id: clientId,
+ client_secret: clientSecret,
+ response_type: 'code',
+ grant_type: 'refresh_token'
+ }
+
+ fetch('/api/v1/users/token', {
+ headers,
+ method: 'POST',
+ body: objectToUrlEncoded(data)
+ })
+ .then(res => res.json())
+ .then((obj: UserRefreshToken) => {
+ this.user.refreshTokens(obj.access_token, obj.refresh_token)
+ this.user.save()
+ this.headers.set('Authorization', `${this.user.getTokenType()} ${this.user.getAccessToken()}`)
+ resolve()
+ })
+ .catch((refreshTokenError: any) => {
+ reject(refreshTokenError)
+ })
+ })
+
+ return refreshingTokenPromise
+ .catch(() => {
+ // If refreshing fails, continue with original error
+ throw error
+ })
+ .then(() => fetch(url, {
+ ...options,
+ headers: this.headers
+ }))
+ })
+ }
+
loadVideoInfo (videoId: string): Promise<Response> {
- return fetch(this.getVideoUrl(videoId), { headers: this.headers })
+ return this.refreshFetch(this.getVideoUrl(videoId), { headers: this.headers })
}
loadVideoCaptions (videoId: string): Promise<Response> {
- return fetch(this.getVideoUrl(videoId) + '/captions', { headers: this.headers })
+ return fetch(this.getVideoUrl(videoId) + '/captions')
}
loadConfig (): Promise<Response> {
async init () {
try {
- this.user = AuthUser.load()
+ this.user = PureAuthUser.load()
await this.initCore()
} catch (e) {
console.error(e)
"video.js": [ "node_modules/video.js/core" ],
"@app/*": [ "src/app/*" ],
"@shared/*": [ "../shared/*" ],
+ "@root-helpers/*": [ "src/root-helpers/*" ],
"fs": [ "src/shims/noop.ts" ],
"http": [ "src/shims/http.ts" ],
"https": [ "src/shims/https.ts" ],
alias: {
'video.js$': path.resolve('node_modules/video.js/core.js'),
- '@app': path.resolve('src/app'),
+ '@root-helpers': path.resolve('src/root-helpers'),
'@shared': path.resolve('../shared')
}
},