Add history on server side
Add ability to disable, clear and list user videos history
This commit is contained in:
parent
583cd0d212
commit
8b9a525a18
|
@ -38,6 +38,7 @@ import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../h
|
|||
import { meRouter } from './me'
|
||||
import { deleteUserToken } from '../../../lib/oauth-model'
|
||||
import { myBlocklistRouter } from './my-blocklist'
|
||||
import { myVideosHistoryRouter } from './my-history'
|
||||
|
||||
const auditLogger = auditLoggerFactory('users')
|
||||
|
||||
|
@ -55,6 +56,7 @@ const askSendEmailLimiter = new RateLimit({
|
|||
|
||||
const usersRouter = express.Router()
|
||||
usersRouter.use('/', myBlocklistRouter)
|
||||
usersRouter.use('/', myVideosHistoryRouter)
|
||||
usersRouter.use('/', meRouter)
|
||||
|
||||
usersRouter.get('/autocomplete',
|
||||
|
|
|
@ -330,6 +330,7 @@ async function updateMe (req: express.Request, res: express.Response, next: expr
|
|||
if (body.nsfwPolicy !== undefined) user.nsfwPolicy = body.nsfwPolicy
|
||||
if (body.webTorrentEnabled !== undefined) user.webTorrentEnabled = body.webTorrentEnabled
|
||||
if (body.autoPlayVideo !== undefined) user.autoPlayVideo = body.autoPlayVideo
|
||||
if (body.videosHistoryEnabled !== undefined) user.videosHistoryEnabled = body.videosHistoryEnabled
|
||||
|
||||
await sequelizeTypescript.transaction(async t => {
|
||||
const userAccount = await AccountModel.load(user.Account.id)
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
import * as express from 'express'
|
||||
import {
|
||||
asyncMiddleware,
|
||||
asyncRetryTransactionMiddleware,
|
||||
authenticate,
|
||||
paginationValidator,
|
||||
setDefaultPagination,
|
||||
userHistoryRemoveValidator
|
||||
} from '../../../middlewares'
|
||||
import { UserModel } from '../../../models/account/user'
|
||||
import { getFormattedObjects } from '../../../helpers/utils'
|
||||
import { UserVideoHistoryModel } from '../../../models/account/user-video-history'
|
||||
import { sequelizeTypescript } from '../../../initializers'
|
||||
|
||||
const myVideosHistoryRouter = express.Router()
|
||||
|
||||
myVideosHistoryRouter.get('/me/history/videos',
|
||||
authenticate,
|
||||
paginationValidator,
|
||||
setDefaultPagination,
|
||||
asyncMiddleware(listMyVideosHistory)
|
||||
)
|
||||
|
||||
myVideosHistoryRouter.post('/me/history/videos/remove',
|
||||
authenticate,
|
||||
userHistoryRemoveValidator,
|
||||
asyncRetryTransactionMiddleware(removeUserHistory)
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
myVideosHistoryRouter
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
async function listMyVideosHistory (req: express.Request, res: express.Response) {
|
||||
const user: UserModel = res.locals.oauth.token.User
|
||||
|
||||
const resultList = await UserVideoHistoryModel.listForApi(user, req.query.start, req.query.count)
|
||||
|
||||
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||
}
|
||||
|
||||
async function removeUserHistory (req: express.Request, res: express.Response) {
|
||||
const user: UserModel = res.locals.oauth.token.User
|
||||
const beforeDate = req.body.beforeDate || null
|
||||
|
||||
await sequelizeTypescript.transaction(t => {
|
||||
return UserVideoHistoryModel.removeHistoryBefore(user, beforeDate, t)
|
||||
})
|
||||
|
||||
// Do not send the delete to other instances, we delete OUR copy of this video abuse
|
||||
|
||||
return res.type('json').status(204).end()
|
||||
}
|
|
@ -46,6 +46,10 @@ function isUserWebTorrentEnabledValid (value: any) {
|
|||
return isBooleanValid(value)
|
||||
}
|
||||
|
||||
function isUserVideosHistoryEnabledValid (value: any) {
|
||||
return isBooleanValid(value)
|
||||
}
|
||||
|
||||
function isUserAutoPlayVideoValid (value: any) {
|
||||
return isBooleanValid(value)
|
||||
}
|
||||
|
@ -73,6 +77,7 @@ function isAvatarFile (files: { [ fieldname: string ]: Express.Multer.File[] } |
|
|||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
isUserVideosHistoryEnabledValid,
|
||||
isUserBlockedValid,
|
||||
isUserPasswordValid,
|
||||
isUserBlockedReasonValid,
|
||||
|
|
|
@ -16,7 +16,7 @@ let config: IConfig = require('config')
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const LAST_MIGRATION_VERSION = 295
|
||||
const LAST_MIGRATION_VERSION = 300
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import * as Sequelize from 'sequelize'
|
||||
|
||||
async function up (utils: {
|
||||
transaction: Sequelize.Transaction,
|
||||
queryInterface: Sequelize.QueryInterface,
|
||||
sequelize: Sequelize.Sequelize,
|
||||
db: any
|
||||
}): Promise<void> {
|
||||
{
|
||||
const data = {
|
||||
type: Sequelize.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: true
|
||||
}
|
||||
|
||||
await utils.queryInterface.addColumn('user', 'videosHistoryEnabled', data)
|
||||
}
|
||||
}
|
||||
|
||||
function down (options) {
|
||||
throw new Error('Not implemented.')
|
||||
}
|
||||
|
||||
export {
|
||||
up,
|
||||
down
|
||||
}
|
|
@ -12,3 +12,4 @@ export * from './videos'
|
|||
export * from './webfinger'
|
||||
export * from './search'
|
||||
export * from './server'
|
||||
export * from './user-history'
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import * as express from 'express'
|
||||
import 'express-validator'
|
||||
import { body, param, query } from 'express-validator/check'
|
||||
import { logger } from '../../helpers/logger'
|
||||
import { areValidationErrors } from './utils'
|
||||
import { ActorFollowModel } from '../../models/activitypub/actor-follow'
|
||||
import { areValidActorHandles, isValidActorHandle } from '../../helpers/custom-validators/activitypub/actor'
|
||||
import { UserModel } from '../../models/account/user'
|
||||
import { CONFIG } from '../../initializers'
|
||||
import { isDateValid, toArray } from '../../helpers/custom-validators/misc'
|
||||
|
||||
const userHistoryRemoveValidator = [
|
||||
body('beforeDate')
|
||||
.optional()
|
||||
.custom(isDateValid).withMessage('Should have a valid before date'),
|
||||
|
||||
(req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||
logger.debug('Checking userHistoryRemoveValidator parameters', { parameters: req.body })
|
||||
|
||||
if (areValidationErrors(req, res)) return
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
userHistoryRemoveValidator
|
||||
}
|
|
@ -4,6 +4,7 @@ import { isIdOrUUIDValid } from '../../../helpers/custom-validators/misc'
|
|||
import { isVideoExist } from '../../../helpers/custom-validators/videos'
|
||||
import { areValidationErrors } from '../utils'
|
||||
import { logger } from '../../../helpers/logger'
|
||||
import { UserModel } from '../../../models/account/user'
|
||||
|
||||
const videoWatchingValidator = [
|
||||
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
|
||||
|
@ -17,6 +18,12 @@ const videoWatchingValidator = [
|
|||
if (areValidationErrors(req, res)) return
|
||||
if (!await isVideoExist(req.params.videoId, res, 'id')) return
|
||||
|
||||
const user = res.locals.oauth.token.User as UserModel
|
||||
if (user.videosHistoryEnabled === false) {
|
||||
logger.warn('Cannot set videos to watch by user %d: videos history is disabled.', user.id)
|
||||
return res.status(409).end()
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, IsInt, Min, Model, Table, UpdatedAt } from 'sequelize-typescript'
|
||||
import { AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, IsInt, Model, Table, UpdatedAt } from 'sequelize-typescript'
|
||||
import { VideoModel } from '../video/video'
|
||||
import { UserModel } from './user'
|
||||
import { Transaction, Op, DestroyOptions } from 'sequelize'
|
||||
|
||||
@Table({
|
||||
tableName: 'userVideoHistory',
|
||||
|
@ -52,4 +53,34 @@ export class UserVideoHistoryModel extends Model<UserVideoHistoryModel> {
|
|||
onDelete: 'CASCADE'
|
||||
})
|
||||
User: UserModel
|
||||
|
||||
static listForApi (user: UserModel, start: number, count: number) {
|
||||
return VideoModel.listForApi({
|
||||
start,
|
||||
count,
|
||||
sort: '-UserVideoHistories.updatedAt',
|
||||
nsfw: null, // All
|
||||
includeLocalVideos: true,
|
||||
withFiles: false,
|
||||
user,
|
||||
historyOfUser: user
|
||||
})
|
||||
}
|
||||
|
||||
static removeHistoryBefore (user: UserModel, beforeDate: string, t: Transaction) {
|
||||
const query: DestroyOptions = {
|
||||
where: {
|
||||
userId: user.id
|
||||
},
|
||||
transaction: t
|
||||
}
|
||||
|
||||
if (beforeDate) {
|
||||
query.where.updatedAt = {
|
||||
[Op.lt]: beforeDate
|
||||
}
|
||||
}
|
||||
|
||||
return UserVideoHistoryModel.destroy(query)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,8 @@ import {
|
|||
isUserUsernameValid,
|
||||
isUserVideoQuotaDailyValid,
|
||||
isUserVideoQuotaValid,
|
||||
isUserWebTorrentEnabledValid
|
||||
isUserWebTorrentEnabledValid,
|
||||
isUserVideosHistoryEnabledValid
|
||||
} from '../../helpers/custom-validators/users'
|
||||
import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto'
|
||||
import { OAuthTokenModel } from '../oauth/oauth-token'
|
||||
|
@ -114,6 +115,12 @@ export class UserModel extends Model<UserModel> {
|
|||
@Column
|
||||
webTorrentEnabled: boolean
|
||||
|
||||
@AllowNull(false)
|
||||
@Default(true)
|
||||
@Is('UserVideosHistoryEnabled', value => throwIfNotValid(value, isUserVideosHistoryEnabledValid, 'Videos history enabled'))
|
||||
@Column
|
||||
videosHistoryEnabled: boolean
|
||||
|
||||
@AllowNull(false)
|
||||
@Default(true)
|
||||
@Is('UserAutoPlayVideo', value => throwIfNotValid(value, isUserAutoPlayVideoValid, 'auto play video boolean'))
|
||||
|
|
|
@ -29,7 +29,7 @@ function getVideoSort (value: string, lastSort: string[] = [ 'id', 'ASC' ]) {
|
|||
]
|
||||
}
|
||||
|
||||
return [ [ field, direction ], lastSort ]
|
||||
return [ field.split('.').concat([ direction ]), lastSort ]
|
||||
}
|
||||
|
||||
function getSortOnModel (model: any, value: string, lastSort: string[] = [ 'id', 'ASC' ]) {
|
||||
|
|
|
@ -153,7 +153,8 @@ type AvailableForListIDsOptions = {
|
|||
accountId?: number
|
||||
videoChannelId?: number
|
||||
trendingDays?: number
|
||||
user?: UserModel
|
||||
user?: UserModel,
|
||||
historyOfUser?: UserModel
|
||||
}
|
||||
|
||||
@Scopes({
|
||||
|
@ -416,6 +417,16 @@ type AvailableForListIDsOptions = {
|
|||
query.subQuery = false
|
||||
}
|
||||
|
||||
if (options.historyOfUser) {
|
||||
query.include.push({
|
||||
model: UserVideoHistoryModel,
|
||||
required: true,
|
||||
where: {
|
||||
userId: options.historyOfUser.id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return query
|
||||
},
|
||||
[ ScopeNames.WITH_ACCOUNT_DETAILS ]: {
|
||||
|
@ -987,7 +998,8 @@ export class VideoModel extends Model<VideoModel> {
|
|||
videoChannelId?: number,
|
||||
followerActorId?: number
|
||||
trendingDays?: number,
|
||||
user?: UserModel
|
||||
user?: UserModel,
|
||||
historyOfUser?: UserModel
|
||||
}, countVideos = true) {
|
||||
if (options.filter && options.filter === 'all-local' && !options.user.hasRight(UserRight.SEE_ALL_VIDEOS)) {
|
||||
throw new Error('Try to filter all-local but no user has not the see all videos right')
|
||||
|
@ -1026,6 +1038,7 @@ export class VideoModel extends Model<VideoModel> {
|
|||
videoChannelId: options.videoChannelId,
|
||||
includeLocalVideos: options.includeLocalVideos,
|
||||
user: options.user,
|
||||
historyOfUser: options.historyOfUser,
|
||||
trendingDays
|
||||
}
|
||||
|
||||
|
@ -1341,7 +1354,7 @@ export class VideoModel extends Model<VideoModel> {
|
|||
}
|
||||
|
||||
const [ count, rowsId ] = await Promise.all([
|
||||
countVideos ? VideoModel.scope(countScope).count(countQuery) : Promise.resolve(undefined),
|
||||
countVideos ? VideoModel.scope(countScope).count(countQuery) : Promise.resolve<number>(undefined),
|
||||
VideoModel.scope(idsScope).findAll(query)
|
||||
])
|
||||
const ids = rowsId.map(r => r.id)
|
||||
|
|
|
@ -308,6 +308,14 @@ describe('Test users API validators', function () {
|
|||
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
|
||||
})
|
||||
|
||||
it('Should fail with an invalid videosHistoryEnabled attribute', async function () {
|
||||
const fields = {
|
||||
videosHistoryEnabled: -1
|
||||
}
|
||||
|
||||
await makePutBodyRequest({ url: server.url, path: path + 'me', token: userAccessToken, fields })
|
||||
})
|
||||
|
||||
it('Should fail with an non authenticated user', async function () {
|
||||
const fields = {
|
||||
currentPassword: 'my super password',
|
||||
|
|
|
@ -3,8 +3,11 @@
|
|||
import * as chai from 'chai'
|
||||
import 'mocha'
|
||||
import {
|
||||
checkBadCountPagination,
|
||||
checkBadStartPagination,
|
||||
flushTests,
|
||||
killallServers,
|
||||
makeGetRequest,
|
||||
makePostBodyRequest,
|
||||
makePutBodyRequest,
|
||||
runServer,
|
||||
|
@ -16,7 +19,9 @@ import {
|
|||
const expect = chai.expect
|
||||
|
||||
describe('Test videos history API validator', function () {
|
||||
let path: string
|
||||
let watchingPath: string
|
||||
let myHistoryPath = '/api/v1/users/me/history/videos'
|
||||
let myHistoryRemove = myHistoryPath + '/remove'
|
||||
let server: ServerInfo
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
@ -33,14 +38,14 @@ describe('Test videos history API validator', function () {
|
|||
const res = await uploadVideo(server.url, server.accessToken, {})
|
||||
const videoUUID = res.body.video.uuid
|
||||
|
||||
path = '/api/v1/videos/' + videoUUID + '/watching'
|
||||
watchingPath = '/api/v1/videos/' + videoUUID + '/watching'
|
||||
})
|
||||
|
||||
describe('When notifying a user is watching a video', function () {
|
||||
|
||||
it('Should fail with an unauthenticated user', async function () {
|
||||
const fields = { currentTime: 5 }
|
||||
await makePutBodyRequest({ url: server.url, path, fields, statusCodeExpected: 401 })
|
||||
await makePutBodyRequest({ url: server.url, path: watchingPath, fields, statusCodeExpected: 401 })
|
||||
})
|
||||
|
||||
it('Should fail with an incorrect video id', async function () {
|
||||
|
@ -58,13 +63,68 @@ describe('Test videos history API validator', function () {
|
|||
|
||||
it('Should fail with a bad current time', async function () {
|
||||
const fields = { currentTime: 'hello' }
|
||||
await makePutBodyRequest({ url: server.url, path, fields, token: server.accessToken, statusCodeExpected: 400 })
|
||||
await makePutBodyRequest({ url: server.url, path: watchingPath, fields, token: server.accessToken, statusCodeExpected: 400 })
|
||||
})
|
||||
|
||||
it('Should succeed with the correct parameters', async function () {
|
||||
const fields = { currentTime: 5 }
|
||||
|
||||
await makePutBodyRequest({ url: server.url, path, fields, token: server.accessToken, statusCodeExpected: 204 })
|
||||
await makePutBodyRequest({ url: server.url, path: watchingPath, fields, token: server.accessToken, statusCodeExpected: 204 })
|
||||
})
|
||||
})
|
||||
|
||||
describe('When listing user videos history', function () {
|
||||
it('Should fail with a bad start pagination', async function () {
|
||||
await checkBadStartPagination(server.url, myHistoryPath, server.accessToken)
|
||||
})
|
||||
|
||||
it('Should fail with a bad count pagination', async function () {
|
||||
await checkBadCountPagination(server.url, myHistoryPath, server.accessToken)
|
||||
})
|
||||
|
||||
it('Should fail with an unauthenticated user', async function () {
|
||||
await makeGetRequest({ url: server.url, path: myHistoryPath, statusCodeExpected: 401 })
|
||||
})
|
||||
|
||||
it('Should succeed with the correct params', async function () {
|
||||
await makeGetRequest({ url: server.url, token: server.accessToken, path: myHistoryPath, statusCodeExpected: 200 })
|
||||
})
|
||||
})
|
||||
|
||||
describe('When removing user videos history', function () {
|
||||
it('Should fail with an unauthenticated user', async function () {
|
||||
await makePostBodyRequest({ url: server.url, path: myHistoryPath + '/remove', statusCodeExpected: 401 })
|
||||
})
|
||||
|
||||
it('Should fail with a bad beforeDate parameter', async function () {
|
||||
const body = { beforeDate: '15' }
|
||||
await makePostBodyRequest({
|
||||
url: server.url,
|
||||
token: server.accessToken,
|
||||
path: myHistoryRemove,
|
||||
fields: body,
|
||||
statusCodeExpected: 400
|
||||
})
|
||||
})
|
||||
|
||||
it('Should succeed with a valid beforeDate param', async function () {
|
||||
const body = { beforeDate: new Date().toISOString() }
|
||||
await makePostBodyRequest({
|
||||
url: server.url,
|
||||
token: server.accessToken,
|
||||
path: myHistoryRemove,
|
||||
fields: body,
|
||||
statusCodeExpected: 204
|
||||
})
|
||||
})
|
||||
|
||||
it('Should succeed without body', async function () {
|
||||
await makePostBodyRequest({
|
||||
url: server.url,
|
||||
token: server.accessToken,
|
||||
path: myHistoryRemove,
|
||||
statusCodeExpected: 204
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -3,17 +3,21 @@
|
|||
import * as chai from 'chai'
|
||||
import 'mocha'
|
||||
import {
|
||||
createUser,
|
||||
flushTests,
|
||||
getVideosListWithToken,
|
||||
getVideoWithToken,
|
||||
killallServers, makePutBodyRequest,
|
||||
runServer, searchVideoWithToken,
|
||||
killallServers,
|
||||
runServer,
|
||||
searchVideoWithToken,
|
||||
ServerInfo,
|
||||
setAccessTokensToServers,
|
||||
uploadVideo
|
||||
updateMyUser,
|
||||
uploadVideo,
|
||||
userLogin
|
||||
} from '../../../../shared/utils'
|
||||
import { Video, VideoDetails } from '../../../../shared/models/videos'
|
||||
import { userWatchVideo } from '../../../../shared/utils/videos/video-history'
|
||||
import { listMyVideosHistory, removeMyVideosHistory, userWatchVideo } from '../../../../shared/utils/videos/video-history'
|
||||
|
||||
const expect = chai.expect
|
||||
|
||||
|
@ -22,6 +26,8 @@ describe('Test videos history', function () {
|
|||
let video1UUID: string
|
||||
let video2UUID: string
|
||||
let video3UUID: string
|
||||
let video3WatchedDate: Date
|
||||
let userAccessToken: string
|
||||
|
||||
before(async function () {
|
||||
this.timeout(30000)
|
||||
|
@ -46,6 +52,13 @@ describe('Test videos history', function () {
|
|||
const res = await uploadVideo(server.url, server.accessToken, { name: 'video 3' })
|
||||
video3UUID = res.body.video.uuid
|
||||
}
|
||||
|
||||
const user = {
|
||||
username: 'user_1',
|
||||
password: 'super password'
|
||||
}
|
||||
await createUser(server.url, server.accessToken, user.username, user.password)
|
||||
userAccessToken = await userLogin(server, user)
|
||||
})
|
||||
|
||||
it('Should get videos, without watching history', async function () {
|
||||
|
@ -62,8 +75,8 @@ describe('Test videos history', function () {
|
|||
})
|
||||
|
||||
it('Should watch the first and second video', async function () {
|
||||
await userWatchVideo(server.url, server.accessToken, video1UUID, 3)
|
||||
await userWatchVideo(server.url, server.accessToken, video2UUID, 8)
|
||||
await userWatchVideo(server.url, server.accessToken, video1UUID, 3)
|
||||
})
|
||||
|
||||
it('Should return the correct history when listing, searching and getting videos', async function () {
|
||||
|
@ -117,6 +130,68 @@ describe('Test videos history', function () {
|
|||
}
|
||||
})
|
||||
|
||||
it('Should have these videos when listing my history', async function () {
|
||||
video3WatchedDate = new Date()
|
||||
await userWatchVideo(server.url, server.accessToken, video3UUID, 2)
|
||||
|
||||
const res = await listMyVideosHistory(server.url, server.accessToken)
|
||||
|
||||
expect(res.body.total).to.equal(3)
|
||||
|
||||
const videos: Video[] = res.body.data
|
||||
expect(videos[0].name).to.equal('video 3')
|
||||
expect(videos[1].name).to.equal('video 1')
|
||||
expect(videos[2].name).to.equal('video 2')
|
||||
})
|
||||
|
||||
it('Should not have videos history on another user', async function () {
|
||||
const res = await listMyVideosHistory(server.url, userAccessToken)
|
||||
|
||||
expect(res.body.total).to.equal(0)
|
||||
expect(res.body.data).to.have.lengthOf(0)
|
||||
})
|
||||
|
||||
it('Should clear my history', async function () {
|
||||
await removeMyVideosHistory(server.url, server.accessToken, video3WatchedDate.toISOString())
|
||||
})
|
||||
|
||||
it('Should have my history cleared', async function () {
|
||||
const res = await listMyVideosHistory(server.url, server.accessToken)
|
||||
|
||||
expect(res.body.total).to.equal(1)
|
||||
|
||||
const videos: Video[] = res.body.data
|
||||
expect(videos[0].name).to.equal('video 3')
|
||||
})
|
||||
|
||||
it('Should disable videos history', async function () {
|
||||
await updateMyUser({
|
||||
url: server.url,
|
||||
accessToken: server.accessToken,
|
||||
videosHistoryEnabled: false
|
||||
})
|
||||
|
||||
await userWatchVideo(server.url, server.accessToken, video2UUID, 8, 409)
|
||||
})
|
||||
|
||||
it('Should re-enable videos history', async function () {
|
||||
await updateMyUser({
|
||||
url: server.url,
|
||||
accessToken: server.accessToken,
|
||||
videosHistoryEnabled: true
|
||||
})
|
||||
|
||||
await userWatchVideo(server.url, server.accessToken, video1UUID, 8)
|
||||
|
||||
const res = await listMyVideosHistory(server.url, server.accessToken)
|
||||
|
||||
expect(res.body.total).to.equal(2)
|
||||
|
||||
const videos: Video[] = res.body.data
|
||||
expect(videos[0].name).to.equal('video 1')
|
||||
expect(videos[1].name).to.equal('video 3')
|
||||
})
|
||||
|
||||
after(async function () {
|
||||
killallServers([ server ])
|
||||
|
||||
|
|
|
@ -3,9 +3,12 @@ import { NSFWPolicyType } from '../videos/nsfw-policy.type'
|
|||
export interface UserUpdateMe {
|
||||
displayName?: string
|
||||
description?: string
|
||||
nsfwPolicy?: NSFWPolicyType,
|
||||
webTorrentEnabled?: boolean,
|
||||
nsfwPolicy?: NSFWPolicyType
|
||||
|
||||
webTorrentEnabled?: boolean
|
||||
autoPlayVideo?: boolean
|
||||
videosHistoryEnabled?: boolean
|
||||
|
||||
email?: string
|
||||
currentPassword?: string
|
||||
password?: string
|
||||
|
|
|
@ -162,14 +162,15 @@ function unblockUser (url: string, userId: number | string, accessToken: string,
|
|||
|
||||
function updateMyUser (options: {
|
||||
url: string
|
||||
accessToken: string,
|
||||
currentPassword?: string,
|
||||
newPassword?: string,
|
||||
nsfwPolicy?: NSFWPolicyType,
|
||||
email?: string,
|
||||
accessToken: string
|
||||
currentPassword?: string
|
||||
newPassword?: string
|
||||
nsfwPolicy?: NSFWPolicyType
|
||||
email?: string
|
||||
autoPlayVideo?: boolean
|
||||
displayName?: string,
|
||||
displayName?: string
|
||||
description?: string
|
||||
videosHistoryEnabled?: boolean
|
||||
}) {
|
||||
const path = '/api/v1/users/me'
|
||||
|
||||
|
@ -181,6 +182,9 @@ function updateMyUser (options: {
|
|||
if (options.email !== undefined && options.email !== null) toSend['email'] = options.email
|
||||
if (options.description !== undefined && options.description !== null) toSend['description'] = options.description
|
||||
if (options.displayName !== undefined && options.displayName !== null) toSend['displayName'] = options.displayName
|
||||
if (options.videosHistoryEnabled !== undefined && options.videosHistoryEnabled !== null) {
|
||||
toSend['videosHistoryEnabled'] = options.videosHistoryEnabled
|
||||
}
|
||||
|
||||
return makePutBodyRequest({
|
||||
url: options.url,
|
||||
|
|
|
@ -1,14 +1,39 @@
|
|||
import { makePutBodyRequest } from '../requests/requests'
|
||||
import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
|
||||
|
||||
function userWatchVideo (url: string, token: string, videoId: number | string, currentTime: number) {
|
||||
function userWatchVideo (url: string, token: string, videoId: number | string, currentTime: number, statusCodeExpected = 204) {
|
||||
const path = '/api/v1/videos/' + videoId + '/watching'
|
||||
const fields = { currentTime }
|
||||
|
||||
return makePutBodyRequest({ url, path, token, fields, statusCodeExpected: 204 })
|
||||
return makePutBodyRequest({ url, path, token, fields, statusCodeExpected })
|
||||
}
|
||||
|
||||
function listMyVideosHistory (url: string, token: string) {
|
||||
const path = '/api/v1/users/me/history/videos'
|
||||
|
||||
return makeGetRequest({
|
||||
url,
|
||||
path,
|
||||
token,
|
||||
statusCodeExpected: 200
|
||||
})
|
||||
}
|
||||
|
||||
function removeMyVideosHistory (url: string, token: string, beforeDate?: string) {
|
||||
const path = '/api/v1/users/me/history/videos/remove'
|
||||
|
||||
return makePostBodyRequest({
|
||||
url,
|
||||
path,
|
||||
token,
|
||||
fields: beforeDate ? { beforeDate } : {},
|
||||
statusCodeExpected: 204
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export {
|
||||
userWatchVideo
|
||||
userWatchVideo,
|
||||
listMyVideosHistory,
|
||||
removeMyVideosHistory
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue