import { ActivatedRoute } from '@angular/router'
import { ContactAdminModalComponent } from '@app/+about/about-instance/contact-admin-modal.component'
import { Notifier } from '@app/core'
-import { copyToClipboard } from '../../../assets/player/utils'
+import { copyToClipboard } from '../../../root-helpers/utils'
import { InstanceService } from '@app/shared/shared-instance'
import { ServerConfig } from '@shared/models'
import { ResolverData } from './about-instance.resolver'
getVideoEmbed (entry: VideoBlacklist) {
return buildVideoOrPlaylistEmbed(
buildVideoLink({
- baseUrl: `${environment.embedUrl}/videos/embed/${entry.video.uuid}`,
+ baseUrl: `${environment.originServerUrl}/videos/embed/${entry.video.uuid}`,
title: false,
warningTitle: false
})
<div class="form-group col-12 col-lg-4 col-xl-3">
<h2 i18n class="applications-title">SUBSCRIPTION FEED</h2>
<div i18n class="applications-description">
- Used to retrieve the list of videos of the creators
- you subscribed to from outside PeerTube
+ Use third-party feed aggregators to retrieve the list of videos from
+ channels you subscribed to. Make sure to keep your token private.
</div>
</div>
import { Component, OnInit } from '@angular/core'
-import { AuthService, Notifier, ConfirmService } from '@app/core'
+import { AuthService, Notifier, ConfirmService, ScopedTokensService } from '@app/core'
import { VideoService } from '@app/shared/shared-main'
import { FeedFormat } from '@shared/models'
-import { Subject, merge } from 'rxjs'
-import { debounceTime } from 'rxjs/operators'
+import { ScopedToken } from '@shared/models/users/user-scoped-token'
+import { environment } from '../../../environments/environment'
@Component({
selector: 'my-account-applications',
feedUrl: string
feedToken: string
- private baseURL = window.location.protocol + '//' + window.location.host
- private tokenStream = new Subject()
+ private baseURL = environment.originServerUrl
constructor (
private authService: AuthService,
+ private scopedTokensService: ScopedTokensService,
private videoService: VideoService,
private notifier: Notifier,
private confirmService: ConfirmService
ngOnInit () {
this.feedUrl = this.baseURL
+ this.scopedTokensService.getScopedTokens()
+ .subscribe(
+ tokens => this.regenApplications(tokens),
- merge(
- this.tokenStream,
- this.authService.userInformationLoaded
- ).pipe(debounceTime(400))
- .subscribe(
- _ => {
- const user = this.authService.getUser()
- this.videoService.getVideoSubscriptionFeedUrls(user.account.id)
- .then(feeds => this.feedUrl = this.baseURL + feeds.find(f => f.format === FeedFormat.RSS).url)
- .then(_ => this.authService.getScopedTokens().then(tokens => this.feedToken = tokens.feedToken))
- },
-
- err => {
- this.notifier.error(err.message)
- }
- )
+ err => {
+ this.notifier.error(err.message)
+ }
+ )
}
async renewToken () {
- const res = await this.confirmService.confirm('Renewing the token will disallow previously configured clients from retrieving the feed until they use the new token. Proceed?', 'Renew token')
+ const res = await this.confirmService.confirm(
+ $localize`Renewing the token will disallow previously configured clients from retrieving the feed until they use the new token. Proceed?`,
+ $localize`Renew token`
+ )
if (res === false) return
- await this.authService.renewScopedTokens()
- this.notifier.success('Token renewed. Update your client configuration accordingly.')
- this.tokenStream.next()
+ this.scopedTokensService.renewScopedTokens().subscribe(
+ tokens => {
+ this.regenApplications(tokens)
+ this.notifier.success($localize`Token renewed. Update your client configuration accordingly.`)
+ },
+
+ err => {
+ this.notifier.error(err.message)
+ }
+ )
+
+ }
+
+ private regenApplications (tokens: ScopedToken) {
+ const user = this.authService.getUser()
+ const feeds = this.videoService.getVideoSubscriptionFeedUrls(user.account.id, tokens.feedToken)
+ this.feedUrl = this.baseURL + feeds.find(f => f.format === FeedFormat.RSS).url
+ this.feedToken = tokens.feedToken
}
}
label: $localize`Abuse reports`,
routerLink: '/my-account/abuses',
iconName: 'flag'
- },
- {
- label: $localize`Applications`,
- routerLink: '/my-account/applications',
- iconName: 'codesandbox'
}
]
}
routerLink: '/my-account/notifications'
},
+ {
+ label: $localize`Applications`,
+ routerLink: '/my-account/applications'
+ },
+
moderationEntries
]
}
import { MyAccountNotificationPreferencesComponent } from './my-account-settings/my-account-notification-preferences'
import { MyAccountProfileComponent } from './my-account-settings/my-account-profile/my-account-profile.component'
import { MyAccountSettingsComponent } from './my-account-settings/my-account-settings.component'
+import { MyAccountApplicationsComponent } from './my-account-applications/my-account-applications.component'
import { MyAccountComponent } from './my-account.component'
-import { VideoChangeOwnershipComponent } from './my-account-applications/my-account-applications.component'
@NgModule({
imports: [
MyAccountChangePasswordComponent,
MyAccountProfileComponent,
MyAccountChangeEmailComponent,
+ MyAccountApplicationsComponent,
MyAccountDangerZoneComponent,
MyAccountBlocklistComponent,
MyAccountAbusesListComponent,
MyAccountServerBlocklistComponent,
MyAccountNotificationsComponent,
- MyAccountNotificationPreferencesComponent,
MyAccountNotificationPreferencesComponent
],
import { Component, OnDestroy, OnInit } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'
-import { AuthService, LocalStorageService, Notifier, ScreenService, ServerService, UserService } from '@app/core'
+import { AuthService, LocalStorageService, Notifier, ScopedTokensService, ScreenService, ServerService, UserService } from '@app/core'
import { HooksService } from '@app/core/plugins/hooks.service'
import { immutableAssign } from '@app/helpers'
import { VideoService } from '@app/shared/shared-main'
import { VideoSortField, FeedFormat } from '@shared/models'
import { copyToClipboard } from '../../../root-helpers/utils'
import { environment } from '../../../environments/environment'
+import { forkJoin } from 'rxjs'
@Component({
selector: 'my-videos-user-subscriptions',
protected storageService: LocalStorageService,
private userSubscription: UserSubscriptionService,
private hooks: HooksService,
- private videoService: VideoService
+ private videoService: VideoService,
+ private scopedTokensService: ScopedTokensService
) {
super()
super.ngOnInit()
const user = this.authService.getUser()
- let feedUrl = environment.embedUrl
- this.videoService.getVideoSubscriptionFeedUrls(user.account.id)
- .then((feeds: any) => feedUrl = feedUrl + feeds.find((f: any) => f.format === FeedFormat.RSS).url)
+ let feedUrl = environment.originServerUrl
+
+ this.scopedTokensService.getScopedTokens().subscribe(
+ tokens => {
+ const feeds = this.videoService.getVideoSubscriptionFeedUrls(user.account.id, tokens.feedToken)
+ feedUrl = feedUrl + feeds.find((f: any) => f.format === FeedFormat.RSS).url
+ },
+
+ err => {
+ this.notifier.error(err.message)
+ }
+ )
+
this.actions.unshift({
label: $localize`Feed`,
iconName: 'syndication',
import { RestExtractor } from '../rest/rest-extractor.service'
import { AuthStatus } from './auth-status.model'
import { AuthUser } from './auth-user.model'
-import { ScopedTokenType, ScopedToken } from '@shared/models/users/user-scoped-token'
interface UserLoginWithUsername extends UserLogin {
access_token: string
private static BASE_CLIENT_URL = environment.apiUrl + '/api/v1/oauth-clients/local'
private static BASE_TOKEN_URL = environment.apiUrl + '/api/v1/users/token'
private static BASE_REVOKE_TOKEN_URL = environment.apiUrl + '/api/v1/users/revoke-token'
- private static BASE_SCOPED_TOKENS_URL = environment.apiUrl + '/api/v1/users/scoped-tokens'
private static BASE_USER_INFORMATION_URL = environment.apiUrl + '/api/v1/users/me'
private static LOCAL_STORAGE_OAUTH_CLIENT_KEYS = {
CLIENT_ID: 'client_id',
private loginChanged: Subject<AuthStatus>
private user: AuthUser = null
private refreshingTokenObservable: Observable<any>
- private scopedTokens: ScopedToken
constructor (
private http: HttpClient,
)
}
- getScopedTokens (): Promise<ScopedToken> {
- return new Promise((res, rej) => {
- if (this.scopedTokens) return res(this.scopedTokens)
-
- const authHeaderValue = this.getRequestHeaderValue()
- const headers = new HttpHeaders().set('Authorization', authHeaderValue)
-
- this.http.get<ScopedToken>(AuthService.BASE_SCOPED_TOKENS_URL, { headers })
- .subscribe(
- scopedTokens => {
- this.scopedTokens = scopedTokens
- res(this.scopedTokens)
- },
-
- err => {
- console.error(err)
- rej(err)
- }
- )
- })
- }
-
- renewScopedTokens (): Promise<ScopedToken> {
- return new Promise((res, rej) => {
- const authHeaderValue = this.getRequestHeaderValue()
- const headers = new HttpHeaders().set('Authorization', authHeaderValue)
-
- this.http.post<ScopedToken>(AuthService.BASE_SCOPED_TOKENS_URL, {}, { headers })
- .subscribe(
- scopedTokens => {
- this.scopedTokens = scopedTokens
- res(this.scopedTokens)
- },
-
- err => {
- console.error(err)
- rej(err)
- }
- )
- })
- }
-
private mergeUserInformation (obj: UserLoginWithUsername): Observable<UserLoginWithUserInformation> {
// User is not loaded yet, set manually auth header
const headers = new HttpHeaders().set('Authorization', `${obj.token_type} ${obj.access_token}`)
import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client'
import { LoadingBarRouterModule } from '@ngx-loading-bar/router'
import { AuthService } from './auth'
+import { ScopedTokensService } from './scoped-tokens'
import { ConfirmService } from './confirm'
import { CheatSheetComponent } from './hotkeys'
import { MenuService } from './menu'
providers: [
AuthService,
+ ScopedTokensService,
ConfirmService,
ServerService,
ThemeService,
export * from './auth'
+export * from './scoped-tokens'
export * from './confirm'
export * from './hotkeys'
export * from './menu'
--- /dev/null
+export * from './scoped-tokens.service'
--- /dev/null
+import { Injectable } from '@angular/core'
+import { HttpClient } from '@angular/common/http'
+import { environment } from '../../../environments/environment'
+import { AuthService } from '../auth'
+import { ScopedToken } from '@shared/models/users/user-scoped-token'
+import { catchError } from 'rxjs/operators'
+import { RestExtractor } from '../rest'
+
+@Injectable()
+export class ScopedTokensService {
+ private static BASE_SCOPED_TOKENS_URL = environment.apiUrl + '/api/v1/users/scoped-tokens'
+
+ constructor (
+ private authHttp: HttpClient,
+ private restExtractor: RestExtractor
+ ) {}
+
+ getScopedTokens () {
+ return this.authHttp
+ .get<ScopedToken>(ScopedTokensService.BASE_SCOPED_TOKENS_URL)
+ .pipe(
+ catchError(res => this.restExtractor.handleError(res))
+ )
+ }
+
+ renewScopedTokens () {
+ return this.authHttp
+ .post<ScopedToken>(ScopedTokensService.BASE_SCOPED_TOKENS_URL, {})
+ .pipe(
+ catchError(res => this.restExtractor.handleError(res))
+ )
+ }
+}
}
function getAbsoluteEmbedUrl () {
- let absoluteEmbedUrl = environment.embedUrl
+ let absoluteEmbedUrl = environment.originServerUrl
if (!absoluteEmbedUrl) {
// The Embed is on the same domain
absoluteEmbedUrl = window.location.origin
getVideoEmbed (abuse: AdminAbuse) {
return buildVideoOrPlaylistEmbed(
buildVideoLink({
- baseUrl: `${environment.embedUrl}/videos/embed/${abuse.video.uuid}`,
+ baseUrl: `${environment.originServerUrl}/videos/embed/${abuse.video.uuid}`,
title: false,
warningTitle: false,
startTime: abuse.video.startAt,
import { Component, Input } from '@angular/core'
import { Notifier } from '@app/core'
-import { FormGroup } from '@angular/forms'
@Component({
selector: 'my-input-readonly-copy',
VideoFilter,
VideoPrivacy,
VideoSortField,
- VideoUpdate,
- VideoCreate
+ VideoUpdate
} from '@shared/models'
import { environment } from '../../../../environments/environment'
import { Account } from '../account/account.model'
export class VideoService implements VideosProvider {
static BASE_VIDEO_URL = environment.apiUrl + '/api/v1/videos/'
static BASE_FEEDS_URL = environment.apiUrl + '/feeds/videos.'
+ static BASE_SUBSCRIPTION_FEEDS_URL = environment.apiUrl + '/feeds/subscriptions.'
constructor (
private authHttp: HttpClient,
private restExtractor: RestExtractor,
private restService: RestService,
- private serverService: ServerService,
- private authService: AuthService
+ private serverService: ServerService
) {}
getVideoViewUrl (uuid: string) {
)
}
- buildBaseFeedUrls (params: HttpParams) {
+ buildBaseFeedUrls (params: HttpParams, base = VideoService.BASE_FEEDS_URL) {
const feeds = [
{
format: FeedFormat.RSS,
label: 'media rss 2.0',
- url: VideoService.BASE_FEEDS_URL + FeedFormat.RSS.toLowerCase()
+ url: base + FeedFormat.RSS.toLowerCase()
},
{
format: FeedFormat.ATOM,
label: 'atom 1.0',
- url: VideoService.BASE_FEEDS_URL + FeedFormat.ATOM.toLowerCase()
+ url: base + FeedFormat.ATOM.toLowerCase()
},
{
format: FeedFormat.JSON,
label: 'json 1.0',
- url: VideoService.BASE_FEEDS_URL + FeedFormat.JSON.toLowerCase()
+ url: base + FeedFormat.JSON.toLowerCase()
}
]
return this.buildBaseFeedUrls(params)
}
- async getVideoSubscriptionFeedUrls (accountId: number) {
+ getVideoSubscriptionFeedUrls (accountId: number, feedToken: string) {
let params = this.restService.addRestGetParams(new HttpParams())
params = params.set('accountId', accountId.toString())
-
- const { feedToken } = await this.authService.getScopedTokens()
params = params.set('token', feedToken)
- return this.buildBaseFeedUrls(params)
+ return this.buildBaseFeedUrls(params, VideoService.BASE_SUBSCRIPTION_FEEDS_URL)
}
getVideoFileMetadata (metadataUrl: string) {
production: false,
hmr: false,
apiUrl: 'http://localhost:9001',
- embedUrl: 'http://localhost:9001'
+ originServerUrl: 'http://localhost:9001'
}
production: false,
hmr: true,
apiUrl: '',
- embedUrl: 'http://localhost:9000'
+ originServerUrl: 'http://localhost:9000'
}
production: true,
hmr: false,
apiUrl: '',
- embedUrl: ''
+ originServerUrl: ''
}
production: true,
hmr: false,
apiUrl: '',
- embedUrl: ''
+ originServerUrl: ''
}
line-height: $button-height;
border-radius: 3px;
text-align: center;
- padding: 0 13px 0 13px;
+ padding: 0 17px 0 13px;
cursor: pointer;
}
import { VideoModel } from '../models/video/video'
import { VideoCommentModel } from '../models/video/video-comment'
import { VideoFilter } from '../../shared/models/videos/video-query.type'
-import { logger } from '../helpers/logger'
const feedsRouter = express.Router()
})(ROUTE_CACHE_LIFETIME.FEEDS)),
commonVideosFiltersValidator,
asyncMiddleware(videoFeedsValidator),
- asyncMiddleware(videoSubscriptonFeedsValidator),
asyncMiddleware(generateVideoFeed)
)
+feedsRouter.get('/feeds/subscriptions.:format',
+ videosSortValidator,
+ setDefaultVideosSort,
+ feedsFormatValidator,
+ setFeedFormatContentType,
+ asyncMiddleware(cacheRoute({
+ headerBlacklist: [
+ 'Content-Type'
+ ]
+ })(ROUTE_CACHE_LIFETIME.FEEDS)),
+ commonVideosFiltersValidator,
+ asyncMiddleware(videoSubscriptonFeedsValidator),
+ asyncMiddleware(generateVideoFeedForSubscriptions)
+)
+
// ---------------------------------------------------------------------------
export {
async function generateVideoCommentsFeed (req: express.Request, res: express.Response) {
const start = 0
-
const video = res.locals.videoAll
const account = res.locals.account
const videoChannel = res.locals.videoChannel
async function generateVideoFeed (req: express.Request, res: express.Response) {
const start = 0
-
const account = res.locals.account
const videoChannel = res.locals.videoChannel
- const token = req.query.token
const nsfw = buildNSFWFilter(res, req.query.nsfw)
let name: string
queryString: new URL(WEBSERVER.URL + req.url).search
})
- /**
- * We have two ways to query video results:
- * - one with account and token -> get subscription videos
- * - one with either account, channel, or nothing: just videos with these filters
- */
- const options = token && token !== '' && res.locals.user
- ? {
- followerActorId: res.locals.user.Account.Actor.id,
- user: res.locals.user,
- includeLocalVideos: false
- }
- : {
- accountId: account ? account.id : null,
- videoChannelId: videoChannel ? videoChannel.id : null
- }
+ const options = {
+ accountId: account ? account.id : null,
+ videoChannelId: videoChannel ? videoChannel.id : null
+ }
const resultList = await VideoModel.listForApi({
start,
...options
})
+ addVideosToFeed(feed, resultList.data)
+
+ // Now the feed generation is done, let's send it!
+ return sendFeed(feed, req, res)
+}
+
+async function generateVideoFeedForSubscriptions (req: express.Request, res: express.Response) {
+ const start = 0
+ const account = res.locals.account
+ const nsfw = buildNSFWFilter(res, req.query.nsfw)
+ const name = account.getDisplayName()
+ const description = account.description
+
+ const feed = initFeed({
+ name,
+ description,
+ resourceType: 'videos',
+ queryString: new URL(WEBSERVER.URL + req.url).search
+ })
+
+ const options = {
+ followerActorId: res.locals.user.Account.Actor.id,
+ user: res.locals.user
+ }
+
+ const resultList = await VideoModel.listForApi({
+ start,
+ count: FEEDS.COUNT,
+ sort: req.query.sort,
+ includeLocalVideos: true,
+ nsfw,
+ filter: req.query.filter as VideoFilter,
+ withFiles: true,
+ ...options
+ })
+
+ addVideosToFeed(feed, resultList.data)
+
+ // Now the feed generation is done, let's send it!
+ return sendFeed(feed, req, res)
+}
+
+function initFeed (parameters: {
+ name: string
+ description: string
+ resourceType?: 'videos' | 'video-comments'
+ queryString?: string
+}) {
+ const webserverUrl = WEBSERVER.URL
+ const { name, description, resourceType, queryString } = parameters
+
+ return new Feed({
+ title: name,
+ description,
+ // updated: TODO: somehowGetLatestUpdate, // optional, default = today
+ id: webserverUrl,
+ link: webserverUrl,
+ image: webserverUrl + '/client/assets/images/icons/icon-96x96.png',
+ favicon: webserverUrl + '/client/assets/images/favicon.png',
+ copyright: `All rights reserved, unless otherwise specified in the terms specified at ${webserverUrl}/about` +
+ ` and potential licenses granted by each content's rightholder.`,
+ generator: `Toraifōsu`, // ^.~
+ feedLinks: {
+ json: `${webserverUrl}/feeds/${resourceType}.json${queryString}`,
+ atom: `${webserverUrl}/feeds/${resourceType}.atom${queryString}`,
+ rss: `${webserverUrl}/feeds/${resourceType}.xml${queryString}`
+ },
+ author: {
+ name: 'Instance admin of ' + CONFIG.INSTANCE.NAME,
+ email: CONFIG.ADMIN.EMAIL,
+ link: `${webserverUrl}/about`
+ }
+ })
+}
+
+function addVideosToFeed (feed, videos: VideoModel[]) {
/**
* Adding video items to the feed object, one at a time
*/
- resultList.data.forEach(video => {
+ for (const video of videos) {
const formattedVideoFiles = video.getFormattedVideoFilesJSON()
const torrents = formattedVideoFiles.map(videoFile => ({
}
]
})
- })
-
- // Now the feed generation is done, let's send it!
- return sendFeed(feed, req, res)
-}
-
-function initFeed (parameters: {
- name: string
- description: string
- resourceType?: 'videos' | 'video-comments'
- queryString?: string
-}) {
- const webserverUrl = WEBSERVER.URL
- const { name, description, resourceType, queryString } = parameters
-
- return new Feed({
- title: name,
- description,
- // updated: TODO: somehowGetLatestUpdate, // optional, default = today
- id: webserverUrl,
- link: webserverUrl,
- image: webserverUrl + '/client/assets/images/icons/icon-96x96.png',
- favicon: webserverUrl + '/client/assets/images/favicon.png',
- copyright: `All rights reserved, unless otherwise specified in the terms specified at ${webserverUrl}/about` +
- ` and potential licenses granted by each content's rightholder.`,
- generator: `Toraifōsu`, // ^.~
- feedLinks: {
- json: `${webserverUrl}/feeds/${resourceType}.json${queryString}`,
- atom: `${webserverUrl}/feeds/${resourceType}.atom${queryString}`,
- rss: `${webserverUrl}/feeds/${resourceType}.xml${queryString}`
- },
- author: {
- name: 'Instance admin of ' + CONFIG.INSTANCE.NAME,
- email: CONFIG.ADMIN.EMAIL,
- link: `${webserverUrl}/about`
- }
- })
+ }
}
function sendFeed (feed, req: express.Request, res: express.Response) {
if (!account) {
if (sendNotFound === true) {
res.status(404)
- .send({ error: 'Account not found' })
- .end()
+ .json({ error: 'Account not found' })
}
return false
}
async function doesUserFeedTokenCorrespond (id: number | string, token: string, res: Response) {
- const user = await UserModel.loadById(parseInt(id + '', 10))
+ const user = await UserModel.loadByIdWithChannels(parseInt(id + '', 10))
if (token !== user.feedToken) {
res.status(401)
- .send({ error: 'User and token mismatch' })
- .end()
+ .json({ error: 'User and token mismatch' })
return false
}
]
const videoSubscriptonFeedsValidator = [
- query('accountId').optional().custom(isIdValid),
- query('token').optional(),
+ query('accountId').custom(isIdValid),
+ query('token'),
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
logger.debug('Checking feeds parameters', { parameters: req.query })
// a token alone is erroneous
if (req.query.token && !req.query.accountId) return
+ if (req.query.accountId && !await doesAccountIdExist(req.query.accountId, res)) return
if (req.query.token && !await doesUserFeedTokenCorrespond(res.locals.account.userId, req.query.token, res)) return
return next()
const res = await listUserSubscriptionVideos(servers[0].url, feeduserAccessToken)
expect(res.body.total).to.equal(0)
- const json = await getJSONfeed(servers[0].url, 'videos', { accountId: feeduserAccountId, token: feeduserFeedToken })
+ const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: feeduserAccountId, token: feeduserFeedToken })
const jsonObj = JSON.parse(json.text)
expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos
}
const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken)
expect(res.body.total).to.equal(0)
- const json = await getJSONfeed(servers[0].url, 'videos', { accountId: userAccountId, token: userFeedToken })
+ const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken })
const jsonObj = JSON.parse(json.text)
expect(jsonObj.items.length).to.be.equal(0) // no subscription, it should not list the instance's videos but list 0 videos
}
expect(res.body.total).to.equal(1)
expect(res.body.data[0].name).to.equal('user video')
- const json = await getJSONfeed(servers[0].url, 'videos', { accountId: userAccountId, token: userFeedToken, version: 1 })
+ const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 1 })
const jsonObj = JSON.parse(json.text)
expect(jsonObj.items.length).to.be.equal(1) // subscribed to self, it should not list the instance's videos but list john's
}
const res = await listUserSubscriptionVideos(servers[0].url, userAccessToken)
expect(res.body.total).to.equal(2, "there should be 2 videos part of the subscription")
- const json = await getJSONfeed(servers[0].url, 'videos', { accountId: userAccountId, token: userFeedToken, version: 2 })
+ const json = await getJSONfeed(servers[0].url, 'subscriptions', { accountId: userAccountId, token: userFeedToken, version: 2 })
const jsonObj = JSON.parse(json.text)
expect(jsonObj.items.length).to.be.equal(2) // subscribed to root, it should not list the instance's videos but list root/john's
}
import * as request from 'supertest'
-type FeedType = 'videos' | 'video-comments'
+type FeedType = 'videos' | 'video-comments' | 'subscriptions'
function getXMLfeed (url: string, feed: FeedType, format?: string) {
const path = '/feeds/' + feed + '.xml'