ServerInfo,
setAccessTokensToServers,
uploadVideo
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
const expect = chai.expect
uploadVideo,
userLogin,
waitJobs
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import * as chai from 'chai'
import { Video } from '../../../../shared/models/videos'
import 'mocha'
import { expect } from 'chai'
-import { buildRequestStub } from '../../../../shared/utils/miscs/stubs'
+import { buildRequestStub } from '../../../../shared/extra-utils/miscs/stubs'
import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../../../helpers/peertube-crypto'
import { cloneDeep } from 'lodash'
import { buildSignedActivity } from '../../../helpers/activitypub'
uploadVideoAndGetId,
wait,
waitJobs
-} from '../../../../shared/utils'
-import { getAccount } from '../../../../shared/utils/users/accounts'
+} from '../../../../shared/extra-utils'
+import { getAccount } from '../../../../shared/extra-utils/users/accounts'
import { VideoPlaylistPrivacy } from '../../../../shared/models/videos'
describe('Test AP refresher', function () {
killallServers,
ServerInfo,
setActorField
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import { HTTP_SIGNATURE } from '../../../initializers/constants'
import { buildDigest, buildGlobalHeaders } from '../../../lib/job-queue/handlers/utils/activitypub-http-utils'
import * as chai from 'chai'
import { activityPubContextify, buildSignedActivity } from '../../../helpers/activitypub'
-import { makeFollowRequest, makePOSTAPRequest } from '../../../../shared/utils/requests/activitypub'
+import { makeFollowRequest, makePOSTAPRequest } from '../../../../shared/extra-utils/requests/activitypub'
const expect = chai.expect
import 'mocha'
-import { flushTests, killallServers, runServer, ServerInfo } from '../../../../shared/utils'
+import { flushTests, killallServers, runServer, ServerInfo } from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination
-} from '../../../../shared/utils/requests/check-api-params'
-import { getAccount } from '../../../../shared/utils/users/accounts'
+} from '../../../../shared/extra-utils/requests/check-api-params'
+import { getAccount } from '../../../../shared/extra-utils/users/accounts'
describe('Test accounts API validators', function () {
const path = '/api/v1/accounts/'
makePostBodyRequest,
ServerInfo,
setAccessTokensToServers, userLogin
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination
-} from '../../../../shared/utils/requests/check-api-params'
+} from '../../../../shared/extra-utils/requests/check-api-params'
describe('Test blocklist API validators', function () {
let servers: ServerInfo[]
import {
createUser, flushTests, killallServers, makeDeleteRequest, makeGetRequest, makePutBodyRequest, runServer, ServerInfo,
setAccessTokensToServers, userLogin, immutableAssign
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
describe('Test config API validators', function () {
const path = '/api/v1/config/custom'
runServer,
ServerInfo,
setAccessTokensToServers
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination
-} from '../../../../shared/utils/requests/check-api-params'
-import { getAccount } from '../../../../shared/utils/users/accounts'
-import { sendContactForm } from '../../../../shared/utils/server/contact-form'
-import { MockSmtpServer } from '../../../../shared/utils/miscs/email'
+} from '../../../../shared/extra-utils/requests/check-api-params'
+import { getAccount } from '../../../../shared/extra-utils/users/accounts'
+import { sendContactForm } from '../../../../shared/extra-utils/server/contact-form'
+import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
describe('Test contact form API validators', function () {
let server: ServerInfo
ServerInfo,
setAccessTokensToServers,
userLogin
-} from '../../../../shared/utils'
-import { makeGetRequest } from '../../../../shared/utils/requests/requests'
+} from '../../../../shared/extra-utils'
+import { makeGetRequest } from '../../../../shared/extra-utils/requests/requests'
describe('Test debug API validators', function () {
const path = '/api/v1/server/debug'
import {
createUser, flushTests, killallServers, makeDeleteRequest, makePostBodyRequest, runServer, ServerInfo, setAccessTokensToServers,
userLogin
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination
-} from '../../../../shared/utils/requests/check-api-params'
+} from '../../../../shared/extra-utils/requests/check-api-params'
describe('Test server follows API validators', function () {
let server: ServerInfo
ServerInfo,
setAccessTokensToServers,
userLogin
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination
-} from '../../../../shared/utils/requests/check-api-params'
-import { makeGetRequest } from '../../../../shared/utils/requests/requests'
+} from '../../../../shared/extra-utils/requests/check-api-params'
+import { makeGetRequest } from '../../../../shared/extra-utils/requests/requests'
describe('Test jobs API validators', function () {
const path = '/api/v1/jobs/failed'
ServerInfo,
setAccessTokensToServers,
userLogin
-} from '../../../../shared/utils'
-import { makeGetRequest } from '../../../../shared/utils/requests/requests'
+} from '../../../../shared/extra-utils'
+import { makeGetRequest } from '../../../../shared/extra-utils/requests/requests'
describe('Test logs API validators', function () {
const path = '/api/v1/server/logs'
ServerInfo,
setAccessTokensToServers,
userLogin
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
describe('Test server redundancy API validators', function () {
let servers: ServerInfo[]
import 'mocha'
-import { flushTests, immutableAssign, killallServers, makeGetRequest, runServer, ServerInfo } from '../../../../shared/utils'
+import { flushTests, immutableAssign, killallServers, makeGetRequest, runServer, ServerInfo } from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination
-} from '../../../../shared/utils/requests/check-api-params'
+} from '../../../../shared/extra-utils/requests/check-api-params'
describe('Test videos API validator', function () {
let server: ServerInfo
ServerInfo,
setAccessTokensToServers,
uploadVideo
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
describe('Test services API validators', function () {
let server: ServerInfo
ServerInfo,
setAccessTokensToServers,
wait
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination
-} from '../../../../shared/utils/requests/check-api-params'
+} from '../../../../shared/extra-utils/requests/check-api-params'
import { UserNotificationSetting, UserNotificationSettingValue } from '../../../../shared/models/users'
describe('Test user notifications API validators', function () {
ServerInfo,
setAccessTokensToServers,
userLogin
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination
-} from '../../../../shared/utils/requests/check-api-params'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils/requests/check-api-params'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
describe('Test user subscriptions API validators', function () {
const path = '/api/v1/users/me/subscriptions'
createUser, flushTests, getMyUserInformation, getMyUserVideoRating, getUsersList, immutableAssign, killallServers, makeGetRequest,
makePostBodyRequest, makeUploadRequest, makePutBodyRequest, registerUser, removeUser, runServer, ServerInfo, setAccessTokensToServers,
updateUser, uploadVideo, userLogin, deleteMe, unblockUser, blockUser
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination
-} from '../../../../shared/utils/requests/check-api-params'
-import { getMagnetURI, getMyVideoImports, getYoutubeVideoUrl, importVideo } from '../../../../shared/utils/videos/video-imports'
+} from '../../../../shared/extra-utils/requests/check-api-params'
+import { getMagnetURI, getMyVideoImports, getYoutubeVideoUrl, importVideo } from '../../../../shared/extra-utils/videos/video-imports'
import { VideoPrivacy } from '../../../../shared/models/videos'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
import { expect } from 'chai'
import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
updateVideoAbuse,
uploadVideo,
userLogin
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination
-} from '../../../../shared/utils/requests/check-api-params'
+} from '../../../../shared/extra-utils/requests/check-api-params'
import { VideoAbuseState } from '../../../../shared/models/videos'
describe('Test video abuses API validators', function () {
setAccessTokensToServers,
uploadVideo,
userLogin, waitJobs
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination
-} from '../../../../shared/utils/requests/check-api-params'
+} from '../../../../shared/extra-utils/requests/check-api-params'
import { VideoDetails, VideoBlacklistType } from '../../../../shared/models/videos'
import { expect } from 'chai'
setAccessTokensToServers,
uploadVideo,
userLogin
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import { join } from 'path'
-import { createVideoCaption } from '../../../../shared/utils/videos/video-captions'
+import { createVideoCaption } from '../../../../shared/extra-utils/videos/video-captions'
describe('Test video captions API validator', function () {
const path = '/api/v1/videos/'
ServerInfo,
setAccessTokensToServers,
userLogin
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination
-} from '../../../../shared/utils/requests/check-api-params'
+} from '../../../../shared/extra-utils/requests/check-api-params'
import { User } from '../../../../shared/models/users'
import { join } from 'path'
createUser,
flushTests, killallServers, makeDeleteRequest, makeGetRequest, makePostBodyRequest, runServer, ServerInfo, setAccessTokensToServers,
uploadVideo, userLogin
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination
-} from '../../../../shared/utils/requests/check-api-params'
-import { addVideoCommentThread } from '../../../../shared/utils/videos/video-comments'
+} from '../../../../shared/extra-utils/requests/check-api-params'
+import { addVideoCommentThread } from '../../../../shared/extra-utils/videos/video-comments'
const expect = chai.expect
setAccessTokensToServers,
updateCustomSubConfig,
userLogin
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination
-} from '../../../../shared/utils/requests/check-api-params'
-import { getMagnetURI, getYoutubeVideoUrl } from '../../../../shared/utils/videos/video-imports'
+} from '../../../../shared/extra-utils/requests/check-api-params'
+import { getMagnetURI, getYoutubeVideoUrl } from '../../../../shared/extra-utils/videos/video-imports'
describe('Test video imports API validator', function () {
const path = '/api/v1/videos/imports'
updateVideoPlaylist,
updateVideoPlaylistElement,
uploadVideoAndGetId
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination
-} from '../../../../shared/utils/requests/check-api-params'
+} from '../../../../shared/extra-utils/requests/check-api-params'
import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
import { VideoPlaylistType } from '../../../../shared/models/videos/playlist/video-playlist-type.model'
ServerInfo,
setAccessTokensToServers, setDefaultVideoChannel,
userLogin
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import { UserRole } from '../../../../shared/models/users'
import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
ServerInfo,
setAccessTokensToServers,
uploadVideo
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
const expect = chai.expect
createUser, flushTests, getMyUserInformation, getVideo, getVideosList, immutableAssign, killallServers, makeDeleteRequest,
makeGetRequest, makeUploadRequest, makePutBodyRequest, removeVideo, uploadVideo,
runServer, ServerInfo, setAccessTokensToServers, userLogin, updateCustomSubConfig
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import {
checkBadCountPagination,
checkBadSortPagination,
checkBadStartPagination
-} from '../../../../shared/utils/requests/check-api-params'
-import { getAccountsList } from '../../../../shared/utils/users/accounts'
+} from '../../../../shared/extra-utils/requests/check-api-params'
+import { getAccountsList } from '../../../../shared/extra-utils/users/accounts'
const expect = chai.expect
wait,
getCustomConfig,
updateCustomConfig, getVideoThreadComments, getVideoCommentThreads, follow
-} from '../../../../shared/utils'
-import { killallServers, ServerInfo, uploadVideo } from '../../../../shared/utils/index'
-import { setAccessTokensToServers } from '../../../../shared/utils/users/login'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
-import { getUserNotificationSocket } from '../../../../shared/utils/socket/socket-io'
+} from '../../../../shared/extra-utils'
+import { killallServers, ServerInfo, uploadVideo } from '../../../../shared/extra-utils/index'
+import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
+import { getUserNotificationSocket } from '../../../../shared/extra-utils/socket/socket-io'
import {
checkCommentMention,
CheckerBaseParams,
markAsReadNotifications,
updateMyNotificationSettings,
markAsReadAllNotifications, checkNewInstanceFollower
-} from '../../../../shared/utils/users/user-notifications'
+} from '../../../../shared/extra-utils/users/user-notifications'
import {
User,
UserNotification,
UserNotificationSettingValue,
UserNotificationType
} from '../../../../shared/models/users'
-import { MockSmtpServer } from '../../../../shared/utils/miscs/email'
-import { addUserSubscription, removeUserSubscription } from '../../../../shared/utils/users/user-subscriptions'
+import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
+import { addUserSubscription, removeUserSubscription } from '../../../../shared/extra-utils/users/user-subscriptions'
import { VideoPrivacy } from '../../../../shared/models/videos'
-import { getBadVideoUrl, getYoutubeVideoUrl, importVideo } from '../../../../shared/utils/videos/video-imports'
-import { addVideoCommentReply, addVideoCommentThread } from '../../../../shared/utils/videos/video-comments'
+import { getBadVideoUrl, getYoutubeVideoUrl, importVideo } from '../../../../shared/extra-utils/videos/video-imports'
+import { addVideoCommentReply, addVideoCommentThread } from '../../../../shared/extra-utils/videos/video-comments'
import * as uuidv4 from 'uuid/v4'
-import { addAccountToAccountBlocklist, removeAccountFromAccountBlocklist } from '../../../../shared/utils/users/blocklist'
+import { addAccountToAccountBlocklist, removeAccountFromAccountBlocklist } from '../../../../shared/extra-utils/users/blocklist'
import { CustomConfig } from '../../../../shared/models/server'
import { VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
viewVideo,
wait,
waitUntilLog
-} from '../../../../shared/utils'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
import * as magnetUtil from 'magnet-uri'
-import { updateRedundancy } from '../../../../shared/utils/server/redundancy'
+import { updateRedundancy } from '../../../../shared/extra-utils/server/redundancy'
import { ActorFollow } from '../../../../shared/models/actors'
import { readdir } from 'fs-extra'
import { join } from 'path'
import { VideoRedundancyStrategy } from '../../../../shared/models/redundancy'
-import { getStats } from '../../../../shared/utils/server/stats'
+import { getStats } from '../../../../shared/extra-utils/server/stats'
import { ServerStats } from '../../../../shared/models/server/server-stats.model'
const expect = chai.expect
uploadVideo,
userLogin,
wait
-} from '../../../../shared/utils'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
import { VideoChannel } from '../../../../shared/models/videos'
-import { searchVideoChannel } from '../../../../shared/utils/search/video-channels'
+import { searchVideoChannel } from '../../../../shared/extra-utils/search/video-channels'
const expect = chai.expect
uploadVideo,
wait,
searchVideo
-} from '../../../../shared/utils'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
import { Video, VideoPrivacy } from '../../../../shared/models/videos'
const expect = chai.expect
uploadVideo,
wait,
immutableAssign
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
const expect = chai.expect
runServer,
setAccessTokensToServers,
updateCustomConfig
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import { ServerConfig } from '../../../../shared/models'
const expect = chai.expect
import * as chai from 'chai'
import 'mocha'
-import { flushTests, killallServers, runServer, ServerInfo, setAccessTokensToServers, wait } from '../../../../shared/utils'
-import { MockSmtpServer } from '../../../../shared/utils/miscs/email'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
-import { sendContactForm } from '../../../../shared/utils/server/contact-form'
+import { flushTests, killallServers, runServer, ServerInfo, setAccessTokensToServers, wait } from '../../../../shared/extra-utils'
+import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
+import { sendContactForm } from '../../../../shared/extra-utils/server/contact-form'
const expect = chai.expect
killallServers,
ServerInfo,
setAccessTokensToServers
-} from '../../../../shared/utils'
-import { MockSmtpServer } from '../../../../shared/utils/miscs/email'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils'
+import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
const expect = chai.expect
ServerInfo,
setAccessTokensToServers,
uploadVideo
-} from '../../../../shared/utils'
-import { unfollow } from '../../../../shared/utils/server/follows'
-import { userLogin } from '../../../../shared/utils/users/login'
-import { createUser } from '../../../../shared/utils/users/users'
+} from '../../../../shared/extra-utils'
+import { unfollow } from '../../../../shared/extra-utils/server/follows'
+import { userLogin } from '../../../../shared/extra-utils/users/login'
+import { createUser } from '../../../../shared/extra-utils/users/users'
const expect = chai.expect
ServerInfo,
setAccessTokensToServers,
updateCustomSubConfig
-} from '../../../../shared/utils/index'
+} from '../../../../shared/extra-utils/index'
import {
follow,
getFollowersListPaginationAndSort,
getFollowingListPaginationAndSort,
removeFollower,
rejectFollower
-} from '../../../../shared/utils/server/follows'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils/server/follows'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
import { ActorFollow } from '../../../../shared/models/actors'
const expect = chai.expect
import 'mocha'
import { Video, VideoPrivacy } from '../../../../shared/models/videos'
import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
-import { completeVideoCheck } from '../../../../shared/utils'
+import { completeVideoCheck } from '../../../../shared/extra-utils'
import {
flushAndRunMultipleServers,
getVideosList,
ServerInfo,
setAccessTokensToServers,
uploadVideo
-} from '../../../../shared/utils/index'
-import { dateIsValid } from '../../../../shared/utils/miscs/miscs'
+} from '../../../../shared/extra-utils/index'
+import { dateIsValid } from '../../../../shared/extra-utils/miscs/miscs'
import {
follow,
getFollowersListPaginationAndSort,
getFollowingListPaginationAndSort,
unfollow
-} from '../../../../shared/utils/server/follows'
-import { expectAccountFollows } from '../../../../shared/utils/users/accounts'
-import { userLogin } from '../../../../shared/utils/users/login'
-import { createUser } from '../../../../shared/utils/users/users'
+} from '../../../../shared/extra-utils/server/follows'
+import { expectAccountFollows } from '../../../../shared/extra-utils/users/accounts'
+import { userLogin } from '../../../../shared/extra-utils/users/login'
+import { createUser } from '../../../../shared/extra-utils/users/users'
import {
addVideoCommentReply,
addVideoCommentThread,
getVideoCommentThreads,
getVideoThreadComments
-} from '../../../../shared/utils/videos/video-comments'
-import { rateVideo } from '../../../../shared/utils/videos/videos'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
-import { createVideoCaption, listVideoCaptions, testCaptionFile } from '../../../../shared/utils/videos/video-captions'
+} from '../../../../shared/extra-utils/videos/video-comments'
+import { rateVideo } from '../../../../shared/extra-utils/videos/videos'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
+import { createVideoCaption, listVideoCaptions, testCaptionFile } from '../../../../shared/extra-utils/videos/video-captions'
import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model'
const expect = chai.expect
updateVideo,
uploadVideo,
wait
-} from '../../../../shared/utils'
-import { follow, getFollowersListPaginationAndSort } from '../../../../shared/utils/server/follows'
-import { getJobsListPaginationAndSort, waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils'
+import { follow, getFollowersListPaginationAndSort } from '../../../../shared/extra-utils/server/follows'
+import { getJobsListPaginationAndSort, waitJobs } from '../../../../shared/extra-utils/server/jobs'
import {
addVideoCommentReply,
addVideoCommentThread,
getVideoCommentThreads,
getVideoThreadComments
-} from '../../../../shared/utils/videos/video-comments'
+} from '../../../../shared/extra-utils/videos/video-comments'
const expect = chai.expect
import * as chai from 'chai'
import 'mocha'
-import { killallServers, ServerInfo, setAccessTokensToServers } from '../../../../shared/utils/index'
-import { doubleFollow } from '../../../../shared/utils/server/follows'
-import { getJobsList, getJobsListPaginationAndSort, waitJobs } from '../../../../shared/utils/server/jobs'
-import { flushAndRunMultipleServers } from '../../../../shared/utils/server/servers'
-import { uploadVideo } from '../../../../shared/utils/videos/videos'
-import { dateIsValid } from '../../../../shared/utils/miscs/miscs'
+import { killallServers, ServerInfo, setAccessTokensToServers } from '../../../../shared/extra-utils/index'
+import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
+import { getJobsList, getJobsListPaginationAndSort, waitJobs } from '../../../../shared/extra-utils/server/jobs'
+import { flushAndRunMultipleServers } from '../../../../shared/extra-utils/server/servers'
+import { uploadVideo } from '../../../../shared/extra-utils/videos/videos'
+import { dateIsValid } from '../../../../shared/extra-utils/miscs/miscs'
const expect = chai.expect
import * as chai from 'chai'
import 'mocha'
-import { flushTests, killallServers, runServer, ServerInfo, setAccessTokensToServers } from '../../../../shared/utils/index'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
-import { uploadVideo } from '../../../../shared/utils/videos/videos'
-import { getLogs } from '../../../../shared/utils/logs/logs'
+import { flushTests, killallServers, runServer, ServerInfo, setAccessTokensToServers } from '../../../../shared/extra-utils/index'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
+import { uploadVideo } from '../../../../shared/extra-utils/videos/videos'
+import { getLogs } from '../../../../shared/extra-utils/logs/logs'
const expect = chai.expect
flushTests,
killallServers,
ServerInfo
-} from '../../../../shared/utils'
-import { runServer } from '../../../../shared/utils/server/servers'
+} from '../../../../shared/extra-utils'
+import { runServer } from '../../../../shared/extra-utils/server/servers'
describe('Start and stop server without web client routes', function () {
let server: ServerInfo
userLogin,
viewVideo,
wait
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
const expect = chai.expect
import {
flushTests,
runServer,
registerUser, getCustomConfig, setAccessTokensToServers, updateCustomConfig
-} from '../../../../shared/utils/index'
+} from '../../../../shared/extra-utils/index'
describe('Test application behind a reverse proxy', function () {
let server = null
uploadVideo,
viewVideo,
wait
-} from '../../../../shared/utils'
-import { flushTests, setAccessTokensToServers } from '../../../../shared/utils/index'
-import { getStats } from '../../../../shared/utils/server/stats'
-import { addVideoCommentThread } from '../../../../shared/utils/videos/video-comments'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils'
+import { flushTests, setAccessTokensToServers } from '../../../../shared/extra-utils/index'
+import { getStats } from '../../../../shared/extra-utils/server/stats'
+import { addVideoCommentThread } from '../../../../shared/extra-utils/videos/video-comments'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
const expect = chai.expect
import * as magnetUtil from 'magnet-uri'
import 'mocha'
-import { getVideo, killallServers, reRunServer, runServer, ServerInfo, uploadVideo } from '../../../../shared/utils'
-import { flushTests, setAccessTokensToServers } from '../../../../shared/utils/index'
+import { getVideo, killallServers, reRunServer, runServer, ServerInfo, uploadVideo } from '../../../../shared/extra-utils'
+import { flushTests, setAccessTokensToServers } from '../../../../shared/extra-utils/index'
import { VideoDetails } from '../../../../shared/models/videos'
import * as WebTorrent from 'webtorrent'
ServerInfo,
uploadVideo,
userLogin
-} from '../../../../shared/utils/index'
-import { setAccessTokensToServers } from '../../../../shared/utils/users/login'
-import { getVideosListWithToken, getVideosList } from '../../../../shared/utils/videos/videos'
+} from '../../../../shared/extra-utils/index'
+import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
+import { getVideosListWithToken, getVideosList } from '../../../../shared/extra-utils/videos/videos'
import {
addVideoCommentReply,
addVideoCommentThread,
getVideoCommentThreads,
getVideoThreadComments
-} from '../../../../shared/utils/videos/video-comments'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils/videos/video-comments'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
import {
addAccountToAccountBlocklist,
removeAccountFromServerBlocklist,
removeServerFromAccountBlocklist,
removeServerFromServerBlocklist
-} from '../../../../shared/utils/users/blocklist'
+} from '../../../../shared/extra-utils/users/blocklist'
const expect = chai.expect
unfollow,
updateVideo,
userLogin
-} from '../../../../shared/utils'
-import { killallServers, ServerInfo, uploadVideo } from '../../../../shared/utils/index'
-import { setAccessTokensToServers } from '../../../../shared/utils/users/login'
+} from '../../../../shared/extra-utils'
+import { killallServers, ServerInfo, uploadVideo } from '../../../../shared/extra-utils/index'
+import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
import { Video, VideoChannel } from '../../../../shared/models/videos'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
import {
addUserSubscription,
listUserSubscriptions,
listUserSubscriptionVideos,
removeUserSubscription,
getUserSubscription, areSubscriptionsExist
-} from '../../../../shared/utils/users/user-subscriptions'
+} from '../../../../shared/extra-utils/users/user-subscriptions'
const expect = chai.expect
removeUser,
updateMyUser,
userLogin
-} from '../../../../shared/utils'
-import { getMyUserInformation, killallServers, ServerInfo, testImage, updateMyAvatar, uploadVideo } from '../../../../shared/utils/index'
-import { checkActorFilesWereRemoved, getAccount, getAccountsList } from '../../../../shared/utils/users/accounts'
-import { setAccessTokensToServers } from '../../../../shared/utils/users/login'
+} from '../../../../shared/extra-utils'
+import {
+ getMyUserInformation,
+ killallServers,
+ ServerInfo,
+ testImage,
+ updateMyAvatar,
+ uploadVideo
+} from '../../../../shared/extra-utils/index'
+import { checkActorFilesWereRemoved, getAccount, getAccountsList } from '../../../../shared/extra-utils/users/accounts'
+import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
import { User } from '../../../../shared/models/users'
import { VideoChannel } from '../../../../shared/models/videos'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
const expect = chai.expect
import {
registerUser, flushTests, getUserInformation, getMyUserInformation, killallServers,
userLogin, login, runServer, ServerInfo, verifyEmail, updateCustomSubConfig, wait
-} from '../../../../shared/utils'
-import { setAccessTokensToServers } from '../../../../shared/utils/users/login'
-import { MockSmtpServer } from '../../../../shared/utils/miscs/email'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils'
+import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
+import { MockSmtpServer } from '../../../../shared/extra-utils/miscs/email'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
const expect = chai.expect
updateUser,
uploadVideo,
userLogin
-} from '../../../../shared/utils'
-import { follow } from '../../../../shared/utils/server/follows'
-import { setAccessTokensToServers } from '../../../../shared/utils/users/login'
-import { getMyVideos } from '../../../../shared/utils/videos/videos'
+} from '../../../../shared/extra-utils'
+import { follow } from '../../../../shared/extra-utils/server/follows'
+import { setAccessTokensToServers } from '../../../../shared/extra-utils/users/login'
+import { getMyVideos } from '../../../../shared/extra-utils/videos/videos'
import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
const expect = chai.expect
viewVideo,
wait,
webtorrentAdd
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import {
addVideoCommentReply,
addVideoCommentThread,
deleteVideoComment,
getVideoCommentThreads,
getVideoThreadComments
-} from '../../../../shared/utils/videos/video-comments'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils/videos/video-comments'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
const expect = chai.expect
ServerInfo,
setAccessTokensToServers,
uploadVideo
-} from '../../../../shared/utils/index'
-import { runServer } from '../../../../shared/utils/server/servers'
+} from '../../../../shared/extra-utils/index'
+import { runServer } from '../../../../shared/extra-utils/server/servers'
const expect = chai.expect
uploadVideo,
viewVideo,
wait
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
const expect = chai.expect
setAccessTokensToServers,
updateVideoAbuse,
uploadVideo
-} from '../../../../shared/utils/index'
-import { doubleFollow } from '../../../../shared/utils/server/follows'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils/index'
+import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
const expect = chai.expect
updateVideoBlacklist,
uploadVideo,
userLogin
-} from '../../../../shared/utils/index'
-import { doubleFollow } from '../../../../shared/utils/server/follows'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils/index'
+import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
import { VideoBlacklist, VideoBlacklistType } from '../../../../shared/models/videos'
import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model'
import { UserRole } from '../../../../shared/models/users'
removeVideo,
uploadVideo,
wait
-} from '../../../../shared/utils'
-import { flushTests, killallServers, ServerInfo, setAccessTokensToServers } from '../../../../shared/utils/index'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
-import { createVideoCaption, deleteVideoCaption, listVideoCaptions, testCaptionFile } from '../../../../shared/utils/videos/video-captions'
+} from '../../../../shared/extra-utils'
+import { flushTests, killallServers, ServerInfo, setAccessTokensToServers } from '../../../../shared/extra-utils/index'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
+import {
+ createVideoCaption,
+ deleteVideoCaption,
+ listVideoCaptions,
+ testCaptionFile
+} from '../../../../shared/extra-utils/videos/video-captions'
import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model'
const expect = chai.expect
uploadVideo,
userLogin,
getVideo
-} from '../../../../shared/utils'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
import { User } from '../../../../shared/models/users'
import { VideoDetails } from '../../../../shared/models/videos'
updateVideoChannelAvatar,
uploadVideo,
userLogin
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import {
addVideoChannel,
deleteVideoChannel,
ServerInfo,
setAccessTokensToServers,
updateVideoChannel
-} from '../../../../shared/utils/index'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils/index'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
const expect = chai.expect
import * as chai from 'chai'
import 'mocha'
import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
-import { testImage } from '../../../../shared/utils'
+import { testImage } from '../../../../shared/extra-utils'
import {
dateIsValid,
flushTests,
setAccessTokensToServers,
updateMyAvatar,
uploadVideo
-} from '../../../../shared/utils/index'
+} from '../../../../shared/extra-utils/index'
import {
addVideoCommentReply,
addVideoCommentThread,
deleteVideoComment,
getVideoCommentThreads,
getVideoThreadComments
-} from '../../../../shared/utils/videos/video-comments'
+} from '../../../../shared/extra-utils/videos/video-comments'
const expect = chai.expect
setAccessTokensToServers,
updateVideo,
uploadVideo
-} from '../../../../shared/utils/index'
-import { doubleFollow } from '../../../../shared/utils/server/follows'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils/index'
+import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
const expect = chai.expect
updateVideo,
uploadVideo,
waitJobs
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import { VideoDetails } from '../../../../shared/models/videos'
import { VideoStreamingPlaylistType } from '../../../../shared/models/videos/video-streaming-playlist.type'
import { join } from 'path'
killallServers,
ServerInfo,
setAccessTokensToServers
-} from '../../../../shared/utils'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
-import { getMagnetURI, getYoutubeVideoUrl, importVideo, getMyVideoImports } from '../../../../shared/utils/videos/video-imports'
+} from '../../../../shared/extra-utils'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
+import { getMagnetURI, getYoutubeVideoUrl, importVideo, getMyVideoImports } from '../../../../shared/extra-utils/videos/video-imports'
const expect = chai.expect
ServerInfo,
setAccessTokensToServers,
uploadVideo
-} from '../../../../shared/utils/index'
-import { userLogin } from '../../../../shared/utils/users/login'
-import { createUser } from '../../../../shared/utils/users/users'
-import { getMyVideos } from '../../../../shared/utils/videos/videos'
+} from '../../../../shared/extra-utils/index'
+import { userLogin } from '../../../../shared/extra-utils/users/login'
+import { createUser } from '../../../../shared/extra-utils/users/users'
+import { getMyVideos } from '../../../../shared/extra-utils/videos/videos'
import {
getAccountVideos,
getConfig,
searchVideoWithToken,
updateCustomConfig,
updateMyUser
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import { ServerConfig } from '../../../../shared/models'
import { CustomConfig } from '../../../../shared/models/server/custom-config.model'
import { User } from '../../../../shared/models/users'
uploadVideoAndGetId,
userLogin,
waitJobs
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model'
import { VideoPlaylist } from '../../../../shared/models/videos/playlist/video-playlist.model'
import { Video } from '../../../../shared/models/videos'
ServerInfo,
setAccessTokensToServers,
uploadVideo
-} from '../../../../shared/utils/index'
-import { doubleFollow } from '../../../../shared/utils/server/follows'
-import { userLogin } from '../../../../shared/utils/users/login'
-import { createUser } from '../../../../shared/utils/users/users'
-import { getMyVideos, getVideo, getVideoWithToken, updateVideo } from '../../../../shared/utils/videos/videos'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils/index'
+import { doubleFollow } from '../../../../shared/extra-utils/server/follows'
+import { userLogin } from '../../../../shared/extra-utils/users/login'
+import { createUser } from '../../../../shared/extra-utils/users/users'
+import { getMyVideos, getVideo, getVideoWithToken, updateVideo } from '../../../../shared/extra-utils/videos/videos'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
const expect = chai.expect
updateVideo,
uploadVideo,
wait
-} from '../../../../shared/utils'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+} from '../../../../shared/extra-utils'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
const expect = chai.expect
setAccessTokensToServers,
uploadVideo,
webtorrentAdd
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import { extname, join } from 'path'
-import { waitJobs } from '../../../../shared/utils/server/jobs'
+import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
import { VIDEO_TRANSCODING_FPS } from '../../../../server/initializers/constants'
const expect = chai.expect
setAccessTokensToServers,
uploadVideo,
userLogin
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import { Video, VideoPrivacy } from '../../../../shared/models/videos'
import { UserRole } from '../../../../shared/models/users'
uploadVideo,
userLogin,
wait
-} from '../../../../shared/utils'
+} from '../../../../shared/extra-utils'
import { Video, VideoDetails } from '../../../../shared/models/videos'
-import { listMyVideosHistory, removeMyVideosHistory, userWatchVideo } from '../../../../shared/utils/videos/video-history'
+import { listMyVideosHistory, removeMyVideosHistory, userWatchVideo } from '../../../../shared/extra-utils/videos/video-history'
const expect = chai.expect
import * as chai from 'chai'
import 'mocha'
-import { flushTests, killallServers, runServer, ServerInfo, setAccessTokensToServers, uploadVideo } from '../../../../shared/utils'
-import { getVideosOverview } from '../../../../shared/utils/overviews/overviews'
+import { flushTests, killallServers, runServer, ServerInfo, setAccessTokensToServers, uploadVideo } from '../../../../shared/extra-utils'
+import { getVideosOverview } from '../../../../shared/extra-utils/overviews/overviews'
import { VideosOverview } from '../../../../shared/models/overviews'
const expect = chai.expect
ServerInfo,
setAccessTokensToServers,
uploadVideo, uploadVideoAndGetId, viewVideo, wait, countVideoViewsOf, doubleFollow, waitJobs
-} from '../../../../shared/utils'
-import { getVideosOverview } from '../../../../shared/utils/overviews/overviews'
+} from '../../../../shared/extra-utils'
+import { getVideosOverview } from '../../../../shared/extra-utils/overviews/overviews'
import { VideosOverview } from '../../../../shared/models/overviews'
-import { listMyVideosHistory } from '../../../../shared/utils/videos/video-history'
+import { listMyVideosHistory } from '../../../../shared/extra-utils/videos/video-history'
const expect = chai.expect
ServerInfo,
setAccessTokensToServers,
uploadVideo
-} from '../../../shared/utils'
-import { waitJobs } from '../../../shared/utils/server/jobs'
+} from '../../../shared/extra-utils'
+import { waitJobs } from '../../../shared/extra-utils/server/jobs'
const expect = chai.expect
ServerInfo,
setAccessTokensToServers,
uploadVideo, wait
-} from '../../../shared/utils'
-import { waitJobs } from '../../../shared/utils/server/jobs'
+} from '../../../shared/extra-utils'
+import { waitJobs } from '../../../shared/extra-utils/server/jobs'
const expect = chai.expect
ServerInfo,
setAccessTokensToServers,
uploadVideo, viewVideo, wait
-} from '../../../shared/utils'
-import { waitJobs } from '../../../shared/utils/server/jobs'
+} from '../../../shared/extra-utils'
+import { waitJobs } from '../../../shared/extra-utils/server/jobs'
import { getVideoFileBitrate, getVideoFileFPS, getVideoFileResolution } from '../../helpers/ffmpeg-utils'
import { VIDEO_TRANSCODING_FPS } from '../../initializers/constants'
import { join } from 'path'
runServer,
ServerInfo,
setAccessTokensToServers
-} from '../../../shared/utils'
+} from '../../../shared/extra-utils'
describe('Test CLI wrapper', function () {
let server: ServerInfo
runServer,
ServerInfo,
setAccessTokensToServers
-} from '../../../shared/utils'
+} from '../../../shared/extra-utils'
describe('Test reset password scripts', function () {
let server: ServerInfo
import 'mocha'
import * as chai from 'chai'
import { VideoDetails } from '../../../shared/models/videos'
-import { waitJobs } from '../../../shared/utils/server/jobs'
-import { addVideoCommentThread } from '../../../shared/utils/videos/video-comments'
+import { waitJobs } from '../../../shared/extra-utils/server/jobs'
+import { addVideoCommentThread } from '../../../shared/extra-utils/videos/video-comments'
import {
addVideoChannel,
createUser,
ServerInfo,
setAccessTokensToServers,
uploadVideo
-} from '../../../shared/utils'
-import { getAccountsList } from '../../../shared/utils/users/accounts'
+} from '../../../shared/extra-utils'
+import { getAccountsList } from '../../../shared/extra-utils/users/accounts'
const expect = chai.expect
updateCustomConfig,
updateCustomSubConfig,
uploadVideo
-} from '../../shared/utils'
+} from '../../shared/extra-utils'
const expect = chai.expect
ServerInfo,
setAccessTokensToServers,
uploadVideo, userLogin
-} from '../../../shared/utils'
+} from '../../../shared/extra-utils'
import * as libxmljs from 'libxmljs'
-import { addVideoCommentThread } from '../../../shared/utils/videos/video-comments'
-import { waitJobs } from '../../../shared/utils/server/jobs'
+import { addVideoCommentThread } from '../../../shared/extra-utils/videos/video-comments'
+import { waitJobs } from '../../../shared/extra-utils/server/jobs'
import { User } from '../../../shared/models/users'
chai.use(require('chai-xml'))
import 'mocha'
import { doRequest, doRequestAndSaveToFile } from '../../helpers/requests'
-import { get4KFileUrl, root, wait } from '../../../shared/utils'
+import { get4KFileUrl, root, wait } from '../../../shared/extra-utils'
import { join } from 'path'
import { pathExists, remove } from 'fs-extra'
import { expect } from 'chai'
ServerInfo,
setAccessTokensToServers,
uploadVideo
-} from '../../shared/utils'
+} from '../../shared/extra-utils'
import { VideoPrivacy } from '../../shared/models/videos'
const expect = chai.expect
ServerInfo,
setAccessTokensToServers,
uploadVideo
-} from '../../../shared/utils'
+} from '../../../shared/extra-utils'
import * as Bluebird from 'bluebird'
start()
updateVideo,
uploadVideo, viewVideo,
wait
-} from '../../../shared/utils'
-import { getJobsListPaginationAndSort } from '../../../shared/utils/server/jobs'
+} from '../../../shared/extra-utils'
+import { getJobsListPaginationAndSort } from '../../../shared/extra-utils/server/jobs'
interface ServerInfo extends DefaultServerInfo {
requestsNumber: number
Server,
Client,
User
-} from '../../shared/utils'
+} from '../../shared/extra-utils'
program
.option('-u, --url <url>', 'Server url')
import { VideoPrivacy } from '../../shared/models/videos'
import { doRequestAndSaveToFile } from '../helpers/requests'
import { CONSTRAINTS_FIELDS } from '../initializers/constants'
-import { getClient, getVideoCategories, login, searchVideoWithSort, uploadVideo } from '../../shared/utils/index'
+import { getClient, getVideoCategories, login, searchVideoWithSort, uploadVideo } from '../../shared/extra-utils/index'
import { truncate } from 'lodash'
import * as prompt from 'prompt'
import { remove } from 'fs-extra'
import * as program from 'commander'
import { access, constants } from 'fs-extra'
import { isAbsolute } from 'path'
-import { getClient, login } from '../../shared/utils'
-import { uploadVideo } from '../../shared/utils/'
+import { getClient, login } from '../../shared/extra-utils'
+import { uploadVideo } from '../../shared/extra-utils/'
import { VideoPrivacy } from '../../shared/models/videos'
import { netrc, getSettings } from './cli'
--- /dev/null
+import { exec } from 'child_process'
+
+import { ServerInfo } from '../server/servers'
+
+function getEnvCli (server?: ServerInfo) {
+ return `NODE_ENV=test NODE_APP_INSTANCE=${server.serverNumber}`
+}
+
+async function execCLI (command: string) {
+ return new Promise<string>((res, rej) => {
+ exec(command, (err, stdout, stderr) => {
+ if (err) return rej(err)
+
+ return res(stdout)
+ })
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ execCLI,
+ getEnvCli
+}
--- /dev/null
+import * as request from 'supertest'
+
+type FeedType = 'videos' | 'video-comments'
+
+function getXMLfeed (url: string, feed: FeedType, format?: string) {
+ const path = '/feeds/' + feed + '.xml'
+
+ return request(url)
+ .get(path)
+ .query((format) ? { format: format } : {})
+ .set('Accept', 'application/xml')
+ .expect(200)
+ .expect('Content-Type', /xml/)
+}
+
+function getJSONfeed (url: string, feed: FeedType, query: any = {}) {
+ const path = '/feeds/' + feed + '.json'
+
+ return request(url)
+ .get(path)
+ .query(query)
+ .set('Accept', 'application/json')
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ getXMLfeed,
+ getJSONfeed
+}
--- /dev/null
+export * from './server/activitypub'
+export * from './cli/cli'
+export * from './server/clients'
+export * from './server/config'
+export * from './server/jobs'
+export * from './users/login'
+export * from './miscs/miscs'
+export * from './miscs/stubs'
+export * from './miscs/sql'
+export * from './server/follows'
+export * from './requests/requests'
+export * from './requests/check-api-params'
+export * from './server/servers'
+export * from './videos/services'
+export * from './videos/video-playlists'
+export * from './users/users'
+export * from './users/accounts'
+export * from './videos/video-abuses'
+export * from './videos/video-blacklist'
+export * from './videos/video-channels'
+export * from './videos/video-comments'
+export * from './videos/video-streaming-playlists'
+export * from './videos/videos'
+export * from './videos/video-change-ownership'
+export * from './feeds/feeds'
+export * from './search/videos'
--- /dev/null
+import { makeGetRequest } from '../requests/requests'
+import { LogLevel } from '../../models/server/log-level.type'
+
+function getLogs (url: string, accessToken: string, startDate: Date, endDate?: Date, level?: LogLevel) {
+ const path = '/api/v1/server/logs'
+
+ return makeGetRequest({
+ url,
+ path,
+ token: accessToken,
+ query: { startDate, endDate, level },
+ statusCodeExpected: 200
+ })
+}
+
+export {
+ getLogs
+}
--- /dev/null
+const MailDev = require('maildev')
+
+// must run maildev as forked ChildProcess
+// failed instantiation stops main process with exit code 0
+process.on('message', (msg) => {
+ if (msg.start) {
+ const maildev = new MailDev({
+ ip: '127.0.0.1',
+ smtp: 1025,
+ disableWeb: true,
+ silent: true
+ })
+
+ maildev.on('new', email => {
+ process.send({ email })
+ })
+
+ maildev.listen(err => {
+ if (err) {
+ // cannot send as Error object
+ return process.send({ err: err.message })
+ }
+
+ return process.send({ err: null })
+ })
+ }
+})
--- /dev/null
+import { fork, ChildProcess } from 'child_process'
+
+class MockSmtpServer {
+
+ private static instance: MockSmtpServer
+ private started = false
+ private emailChildProcess: ChildProcess
+ private emails: object[]
+
+ private constructor () {
+ this.emailChildProcess = fork(`${__dirname}/email-child-process`, [])
+
+ this.emailChildProcess.on('message', (msg) => {
+ if (msg.email) {
+ return this.emails.push(msg.email)
+ }
+ })
+
+ process.on('exit', () => this.kill())
+ }
+
+ collectEmails (emailsCollection: object[]) {
+ return new Promise((res, rej) => {
+ if (this.started) {
+ this.emails = emailsCollection
+ return res()
+ }
+
+ // ensure maildev isn't started until
+ // unexpected exit can be reported to test runner
+ this.emailChildProcess.send({ start: true })
+ this.emailChildProcess.on('exit', () => {
+ return rej(new Error('maildev exited unexpectedly, confirm port not in use'))
+ })
+ this.emailChildProcess.on('message', (msg) => {
+ if (msg.err) {
+ return rej(new Error(msg.err))
+ }
+ this.started = true
+ this.emails = emailsCollection
+ return res()
+ })
+ })
+ }
+
+ kill () {
+ if (!this.emailChildProcess) return
+
+ process.kill(this.emailChildProcess.pid)
+
+ this.emailChildProcess = null
+ MockSmtpServer.instance = null
+ }
+
+ static get Instance () {
+ return this.instance || (this.instance = new this())
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ MockSmtpServer
+}
--- /dev/null
+/* tslint:disable:no-unused-expression */
+
+import * as chai from 'chai'
+import { isAbsolute, join } from 'path'
+import * as request from 'supertest'
+import * as WebTorrent from 'webtorrent'
+import { pathExists, readFile } from 'fs-extra'
+import * as ffmpeg from 'fluent-ffmpeg'
+
+const expect = chai.expect
+let webtorrent = new WebTorrent()
+
+function immutableAssign <T, U> (target: T, source: U) {
+ return Object.assign<{}, T, U>({}, target, source)
+}
+
+ // Default interval -> 5 minutes
+function dateIsValid (dateString: string, interval = 300000) {
+ const dateToCheck = new Date(dateString)
+ const now = new Date()
+
+ return Math.abs(now.getTime() - dateToCheck.getTime()) <= interval
+}
+
+function wait (milliseconds: number) {
+ return new Promise(resolve => setTimeout(resolve, milliseconds))
+}
+
+function webtorrentAdd (torrent: string, refreshWebTorrent = false) {
+ if (refreshWebTorrent === true) webtorrent = new WebTorrent()
+
+ return new Promise<WebTorrent.Torrent>(res => webtorrent.add(torrent, res))
+}
+
+function root () {
+ // We are in /miscs
+ return join(__dirname, '..', '..', '..')
+}
+
+async function testImage (url: string, imageName: string, imagePath: string, extension = '.jpg') {
+ const res = await request(url)
+ .get(imagePath)
+ .expect(200)
+
+ const body = res.body
+
+ const data = await readFile(join(root(), 'server', 'tests', 'fixtures', imageName + extension))
+ const minLength = body.length - ((20 * body.length) / 100)
+ const maxLength = body.length + ((20 * body.length) / 100)
+
+ expect(data.length).to.be.above(minLength)
+ expect(data.length).to.be.below(maxLength)
+}
+
+function buildAbsoluteFixturePath (path: string, customTravisPath = false) {
+ if (isAbsolute(path)) {
+ return path
+ }
+
+ if (customTravisPath && process.env.TRAVIS) return join(process.env.HOME, 'fixtures', path)
+
+ return join(root(), 'server', 'tests', 'fixtures', path)
+}
+
+async function generateHighBitrateVideo () {
+ const tempFixturePath = buildAbsoluteFixturePath('video_high_bitrate_1080p.mp4', true)
+
+ const exists = await pathExists(tempFixturePath)
+ if (!exists) {
+
+ // Generate a random, high bitrate video on the fly, so we don't have to include
+ // a large file in the repo. The video needs to have a certain minimum length so
+ // that FFmpeg properly applies bitrate limits.
+ // https://stackoverflow.com/a/15795112
+ return new Promise<string>(async (res, rej) => {
+ ffmpeg()
+ .outputOptions([ '-f rawvideo', '-video_size 1920x1080', '-i /dev/urandom' ])
+ .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
+ .outputOptions([ '-maxrate 10M', '-bufsize 10M' ])
+ .output(tempFixturePath)
+ .on('error', rej)
+ .on('end', () => res(tempFixturePath))
+ .run()
+ })
+ }
+
+ return tempFixturePath
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ dateIsValid,
+ wait,
+ webtorrentAdd,
+ immutableAssign,
+ testImage,
+ buildAbsoluteFixturePath,
+ root,
+ generateHighBitrateVideo
+}
--- /dev/null
+import * as Sequelize from 'sequelize'
+
+let sequelizes: { [ id: number ]: Sequelize.Sequelize } = {}
+
+function getSequelize (serverNumber: number) {
+ if (sequelizes[serverNumber]) return sequelizes[serverNumber]
+
+ const dbname = 'peertube_test' + serverNumber
+ const username = 'peertube'
+ const password = 'peertube'
+ const host = 'localhost'
+ const port = 5432
+
+ const seq = new Sequelize(dbname, username, password, {
+ dialect: 'postgres',
+ host,
+ port,
+ operatorsAliases: false,
+ logging: false
+ })
+
+ sequelizes[serverNumber] = seq
+
+ return seq
+}
+
+function setActorField (serverNumber: number, to: string, field: string, value: string) {
+ const seq = getSequelize(serverNumber)
+
+ const options = { type: Sequelize.QueryTypes.UPDATE }
+
+ return seq.query(`UPDATE actor SET "${field}" = '${value}' WHERE url = '${to}'`, options)
+}
+
+function setVideoField (serverNumber: number, uuid: string, field: string, value: string) {
+ const seq = getSequelize(serverNumber)
+
+ const options = { type: Sequelize.QueryTypes.UPDATE }
+
+ return seq.query(`UPDATE video SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options)
+}
+
+function setPlaylistField (serverNumber: number, uuid: string, field: string, value: string) {
+ const seq = getSequelize(serverNumber)
+
+ const options = { type: Sequelize.QueryTypes.UPDATE }
+
+ return seq.query(`UPDATE "videoPlaylist" SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options)
+}
+
+async function countVideoViewsOf (serverNumber: number, uuid: string) {
+ const seq = getSequelize(serverNumber)
+
+ // tslint:disable
+ const query = `SELECT SUM("videoView"."views") AS "total" FROM "videoView" INNER JOIN "video" ON "video"."id" = "videoView"."videoId" WHERE "video"."uuid" = '${uuid}'`
+
+ const options = { type: Sequelize.QueryTypes.SELECT }
+ const [ { total } ] = await seq.query(query, options)
+
+ if (!total) return 0
+
+ return parseInt(total, 10)
+}
+
+async function closeAllSequelize (servers: any[]) {
+ for (let i = 1; i <= servers.length; i++) {
+ if (sequelizes[ i ]) {
+ await sequelizes[ i ].close()
+ delete sequelizes[ i ]
+ }
+ }
+}
+
+export {
+ setVideoField,
+ setPlaylistField,
+ setActorField,
+ countVideoViewsOf,
+ closeAllSequelize
+}
--- /dev/null
+function buildRequestStub (): any {
+ return { }
+}
+
+function buildResponseStub (): any {
+ return {
+ locals: {}
+ }
+}
+
+export {
+ buildResponseStub,
+ buildRequestStub
+}
--- /dev/null
+import { makeGetRequest } from '../requests/requests'
+
+function getVideosOverview (url: string, useCache = false) {
+ const path = '/api/v1/overviews/videos'
+
+ const query = {
+ t: useCache ? undefined : new Date().getTime()
+ }
+
+ return makeGetRequest({
+ url,
+ path,
+ query,
+ statusCodeExpected: 200
+ })
+}
+
+export { getVideosOverview }
--- /dev/null
+import { doRequest } from '../../../server/helpers/requests'
+import { HTTP_SIGNATURE } from '../../../server/initializers/constants'
+import { buildGlobalHeaders } from '../../../server/lib/job-queue/handlers/utils/activitypub-http-utils'
+import { activityPubContextify } from '../../../server/helpers/activitypub'
+
+function makePOSTAPRequest (url: string, body: any, httpSignature: any, headers: any) {
+ const options = {
+ method: 'POST',
+ uri: url,
+ json: body,
+ httpSignature,
+ headers
+ }
+
+ return doRequest(options)
+}
+
+async function makeFollowRequest (to: { url: string }, by: { url: string, privateKey }) {
+ const follow = {
+ type: 'Follow',
+ id: by.url + '/toto',
+ actor: by.url,
+ object: to.url
+ }
+
+ const body = activityPubContextify(follow)
+
+ const httpSignature = {
+ algorithm: HTTP_SIGNATURE.ALGORITHM,
+ authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME,
+ keyId: by.url,
+ key: by.privateKey,
+ headers: HTTP_SIGNATURE.HEADERS_TO_SIGN
+ }
+ const headers = buildGlobalHeaders(body)
+
+ return makePOSTAPRequest(to.url, body, httpSignature, headers)
+}
+
+export {
+ makePOSTAPRequest,
+ makeFollowRequest
+}
--- /dev/null
+import { makeGetRequest } from './requests'
+import { immutableAssign } from '../miscs/miscs'
+
+function checkBadStartPagination (url: string, path: string, token?: string, query = {}) {
+ return makeGetRequest({
+ url,
+ path,
+ token,
+ query: immutableAssign(query, { start: 'hello' }),
+ statusCodeExpected: 400
+ })
+}
+
+function checkBadCountPagination (url: string, path: string, token?: string, query = {}) {
+ return makeGetRequest({
+ url,
+ path,
+ token,
+ query: immutableAssign(query, { count: 'hello' }),
+ statusCodeExpected: 400
+ })
+}
+
+function checkBadSortPagination (url: string, path: string, token?: string, query = {}) {
+ return makeGetRequest({
+ url,
+ path,
+ token,
+ query: immutableAssign(query, { sort: 'hello' }),
+ statusCodeExpected: 400
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ checkBadStartPagination,
+ checkBadCountPagination,
+ checkBadSortPagination
+}
--- /dev/null
+import * as request from 'supertest'
+import { buildAbsoluteFixturePath, root } from '../miscs/miscs'
+import { isAbsolute, join } from 'path'
+import { parse } from 'url'
+
+function get4KFileUrl () {
+ return 'https://download.cpy.re/peertube/4k_file.txt'
+}
+
+function makeRawRequest (url: string, statusCodeExpected?: number, range?: string) {
+ const { host, protocol, pathname } = parse(url)
+
+ return makeGetRequest({ url: `${protocol}//${host}`, path: pathname, statusCodeExpected, range })
+}
+
+function makeGetRequest (options: {
+ url: string,
+ path?: string,
+ query?: any,
+ token?: string,
+ statusCodeExpected?: number,
+ contentType?: string,
+ range?: string
+}) {
+ if (!options.statusCodeExpected) options.statusCodeExpected = 400
+ if (options.contentType === undefined) options.contentType = 'application/json'
+
+ const req = request(options.url).get(options.path)
+
+ if (options.contentType) req.set('Accept', options.contentType)
+ if (options.token) req.set('Authorization', 'Bearer ' + options.token)
+ if (options.query) req.query(options.query)
+ if (options.range) req.set('Range', options.range)
+
+ return req.expect(options.statusCodeExpected)
+}
+
+function makeDeleteRequest (options: {
+ url: string,
+ path: string,
+ token?: string,
+ statusCodeExpected?: number
+}) {
+ if (!options.statusCodeExpected) options.statusCodeExpected = 400
+
+ const req = request(options.url)
+ .delete(options.path)
+ .set('Accept', 'application/json')
+
+ if (options.token) req.set('Authorization', 'Bearer ' + options.token)
+
+ return req.expect(options.statusCodeExpected)
+}
+
+function makeUploadRequest (options: {
+ url: string,
+ method?: 'POST' | 'PUT',
+ path: string,
+ token?: string,
+ fields: { [ fieldName: string ]: any },
+ attaches: { [ attachName: string ]: any | any[] },
+ statusCodeExpected?: number
+}) {
+ if (!options.statusCodeExpected) options.statusCodeExpected = 400
+
+ let req: request.Test
+ if (options.method === 'PUT') {
+ req = request(options.url).put(options.path)
+ } else {
+ req = request(options.url).post(options.path)
+ }
+
+ req.set('Accept', 'application/json')
+
+ if (options.token) req.set('Authorization', 'Bearer ' + options.token)
+
+ Object.keys(options.fields).forEach(field => {
+ const value = options.fields[field]
+
+ if (value === undefined) return
+
+ if (Array.isArray(value)) {
+ for (let i = 0; i < value.length; i++) {
+ req.field(field + '[' + i + ']', value[i])
+ }
+ } else {
+ req.field(field, value)
+ }
+ })
+
+ Object.keys(options.attaches).forEach(attach => {
+ const value = options.attaches[attach]
+ if (Array.isArray(value)) {
+ req.attach(attach, buildAbsoluteFixturePath(value[0]), value[1])
+ } else {
+ req.attach(attach, buildAbsoluteFixturePath(value))
+ }
+ })
+
+ return req.expect(options.statusCodeExpected)
+}
+
+function makePostBodyRequest (options: {
+ url: string,
+ path: string,
+ token?: string,
+ fields?: { [ fieldName: string ]: any },
+ statusCodeExpected?: number
+}) {
+ if (!options.fields) options.fields = {}
+ if (!options.statusCodeExpected) options.statusCodeExpected = 400
+
+ const req = request(options.url)
+ .post(options.path)
+ .set('Accept', 'application/json')
+
+ if (options.token) req.set('Authorization', 'Bearer ' + options.token)
+
+ return req.send(options.fields)
+ .expect(options.statusCodeExpected)
+}
+
+function makePutBodyRequest (options: {
+ url: string,
+ path: string,
+ token?: string,
+ fields: { [ fieldName: string ]: any },
+ statusCodeExpected?: number
+}) {
+ if (!options.statusCodeExpected) options.statusCodeExpected = 400
+
+ const req = request(options.url)
+ .put(options.path)
+ .set('Accept', 'application/json')
+
+ if (options.token) req.set('Authorization', 'Bearer ' + options.token)
+
+ return req.send(options.fields)
+ .expect(options.statusCodeExpected)
+}
+
+function makeHTMLRequest (url: string, path: string) {
+ return request(url)
+ .get(path)
+ .set('Accept', 'text/html')
+ .expect(200)
+}
+
+function updateAvatarRequest (options: {
+ url: string,
+ path: string,
+ accessToken: string,
+ fixture: string
+}) {
+ let filePath = ''
+ if (isAbsolute(options.fixture)) {
+ filePath = options.fixture
+ } else {
+ filePath = join(root(), 'server', 'tests', 'fixtures', options.fixture)
+ }
+
+ return makeUploadRequest({
+ url: options.url,
+ path: options.path,
+ token: options.accessToken,
+ fields: {},
+ attaches: { avatarfile: filePath },
+ statusCodeExpected: 200
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ get4KFileUrl,
+ makeHTMLRequest,
+ makeGetRequest,
+ makeUploadRequest,
+ makePostBodyRequest,
+ makePutBodyRequest,
+ makeDeleteRequest,
+ makeRawRequest,
+ updateAvatarRequest
+}
--- /dev/null
+import { makeGetRequest } from '../requests/requests'
+
+function searchVideoChannel (url: string, search: string, token?: string, statusCodeExpected = 200) {
+ const path = '/api/v1/search/video-channels'
+
+ return makeGetRequest({
+ url,
+ path,
+ query: {
+ sort: '-createdAt',
+ search
+ },
+ token,
+ statusCodeExpected
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ searchVideoChannel
+}
--- /dev/null
+/* tslint:disable:no-unused-expression */
+
+import * as request from 'supertest'
+import { VideosSearchQuery } from '../../models/search'
+import { immutableAssign } from '../miscs/miscs'
+
+function searchVideo (url: string, search: string) {
+ const path = '/api/v1/search/videos'
+ const req = request(url)
+ .get(path)
+ .query({ sort: '-publishedAt', search })
+ .set('Accept', 'application/json')
+
+ return req.expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function searchVideoWithToken (url: string, search: string, token: string, query: { nsfw?: boolean } = {}) {
+ const path = '/api/v1/search/videos'
+ const req = request(url)
+ .get(path)
+ .set('Authorization', 'Bearer ' + token)
+ .query(immutableAssign(query, { sort: '-publishedAt', search }))
+ .set('Accept', 'application/json')
+
+ return req.expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function searchVideoWithPagination (url: string, search: string, start: number, count: number, sort?: string) {
+ const path = '/api/v1/search/videos'
+
+ const req = request(url)
+ .get(path)
+ .query({ start })
+ .query({ search })
+ .query({ count })
+
+ if (sort) req.query({ sort })
+
+ return req.set('Accept', 'application/json')
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function searchVideoWithSort (url: string, search: string, sort: string) {
+ const path = '/api/v1/search/videos'
+
+ return request(url)
+ .get(path)
+ .query({ search })
+ .query({ sort })
+ .set('Accept', 'application/json')
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function advancedVideosSearch (url: string, options: VideosSearchQuery) {
+ const path = '/api/v1/search/videos'
+
+ return request(url)
+ .get(path)
+ .query(options)
+ .set('Accept', 'application/json')
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ searchVideo,
+ advancedVideosSearch,
+ searchVideoWithToken,
+ searchVideoWithPagination,
+ searchVideoWithSort
+}
--- /dev/null
+import * as request from 'supertest'
+
+function makeActivityPubGetRequest (url: string, path: string, expectedStatus = 200) {
+ return request(url)
+ .get(path)
+ .set('Accept', 'application/activity+json,text/html;q=0.9,\\*/\\*;q=0.8')
+ .expect(expectedStatus)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ makeActivityPubGetRequest
+}
--- /dev/null
+import * as request from 'supertest'
+import * as urlUtil from 'url'
+
+function getClient (url: string) {
+ const path = '/api/v1/oauth-clients/local'
+
+ return request(url)
+ .get(path)
+ .set('Host', urlUtil.parse(url).host)
+ .set('Accept', 'application/json')
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ getClient
+}
--- /dev/null
+import { makeDeleteRequest, makeGetRequest, makePutBodyRequest } from '../requests/requests'
+import { CustomConfig } from '../../models/server/custom-config.model'
+
+function getConfig (url: string) {
+ const path = '/api/v1/config'
+
+ return makeGetRequest({
+ url,
+ path,
+ statusCodeExpected: 200
+ })
+}
+
+function getAbout (url: string) {
+ const path = '/api/v1/config/about'
+
+ return makeGetRequest({
+ url,
+ path,
+ statusCodeExpected: 200
+ })
+}
+
+function getCustomConfig (url: string, token: string, statusCodeExpected = 200) {
+ const path = '/api/v1/config/custom'
+
+ return makeGetRequest({
+ url,
+ token,
+ path,
+ statusCodeExpected
+ })
+}
+
+function updateCustomConfig (url: string, token: string, newCustomConfig: CustomConfig, statusCodeExpected = 200) {
+ const path = '/api/v1/config/custom'
+
+ return makePutBodyRequest({
+ url,
+ token,
+ path,
+ fields: newCustomConfig,
+ statusCodeExpected
+ })
+}
+
+function updateCustomSubConfig (url: string, token: string, newConfig: any) {
+ const updateParams: CustomConfig = {
+ instance: {
+ name: 'PeerTube updated',
+ shortDescription: 'my short description',
+ description: 'my super description',
+ terms: 'my super terms',
+ defaultClientRoute: '/videos/recently-added',
+ isNSFW: true,
+ defaultNSFWPolicy: 'blur',
+ customizations: {
+ javascript: 'alert("coucou")',
+ css: 'body { background-color: red; }'
+ }
+ },
+ services: {
+ twitter: {
+ username: '@MySuperUsername',
+ whitelisted: true
+ }
+ },
+ cache: {
+ previews: {
+ size: 2
+ },
+ captions: {
+ size: 3
+ }
+ },
+ signup: {
+ enabled: false,
+ limit: 5,
+ requiresEmailVerification: false
+ },
+ admin: {
+ email: 'superadmin1@example.com'
+ },
+ contactForm: {
+ enabled: true
+ },
+ user: {
+ videoQuota: 5242881,
+ videoQuotaDaily: 318742
+ },
+ transcoding: {
+ enabled: true,
+ allowAdditionalExtensions: true,
+ threads: 1,
+ resolutions: {
+ '240p': false,
+ '360p': true,
+ '480p': true,
+ '720p': false,
+ '1080p': false
+ },
+ hls: {
+ enabled: false
+ }
+ },
+ import: {
+ videos: {
+ http: {
+ enabled: false
+ },
+ torrent: {
+ enabled: false
+ }
+ }
+ },
+ autoBlacklist: {
+ videos: {
+ ofUsers: {
+ enabled: false
+ }
+ }
+ },
+ followers: {
+ instance: {
+ enabled: true,
+ manualApproval: false
+ }
+ }
+ }
+
+ Object.assign(updateParams, newConfig)
+
+ return updateCustomConfig(url, token, updateParams)
+}
+
+function deleteCustomConfig (url: string, token: string, statusCodeExpected = 200) {
+ const path = '/api/v1/config/custom'
+
+ return makeDeleteRequest({
+ url,
+ token,
+ path,
+ statusCodeExpected
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ getConfig,
+ getCustomConfig,
+ updateCustomConfig,
+ getAbout,
+ deleteCustomConfig,
+ updateCustomSubConfig
+}
--- /dev/null
+import * as request from 'supertest'
+import { ContactForm } from '../../models/server'
+
+function sendContactForm (options: {
+ url: string,
+ fromEmail: string,
+ fromName: string,
+ body: string,
+ expectedStatus?: number
+}) {
+ const path = '/api/v1/server/contact'
+
+ const body: ContactForm = {
+ fromEmail: options.fromEmail,
+ fromName: options.fromName,
+ body: options.body
+ }
+ return request(options.url)
+ .post(path)
+ .send(body)
+ .expect(options.expectedStatus || 204)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ sendContactForm
+}
--- /dev/null
+import * as request from 'supertest'
+import { ServerInfo } from './servers'
+import { waitJobs } from './jobs'
+import { makeGetRequest, makePostBodyRequest } from '..'
+
+function getFollowersListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string) {
+ const path = '/api/v1/server/followers'
+
+ return request(url)
+ .get(path)
+ .query({ start })
+ .query({ count })
+ .query({ sort })
+ .query({ search })
+ .set('Accept', 'application/json')
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function acceptFollower (url: string, token: string, follower: string, statusCodeExpected = 204) {
+ const path = '/api/v1/server/followers/' + follower + '/accept'
+
+ return makePostBodyRequest({
+ url,
+ token,
+ path,
+ statusCodeExpected
+ })
+}
+
+function rejectFollower (url: string, token: string, follower: string, statusCodeExpected = 204) {
+ const path = '/api/v1/server/followers/' + follower + '/reject'
+
+ return makePostBodyRequest({
+ url,
+ token,
+ path,
+ statusCodeExpected
+ })
+}
+
+function getFollowingListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string) {
+ const path = '/api/v1/server/following'
+
+ return request(url)
+ .get(path)
+ .query({ start })
+ .query({ count })
+ .query({ sort })
+ .query({ search })
+ .set('Accept', 'application/json')
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function follow (follower: string, following: string[], accessToken: string, expectedStatus = 204) {
+ const path = '/api/v1/server/following'
+
+ const followingHosts = following.map(f => f.replace(/^http:\/\//, ''))
+ return request(follower)
+ .post(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .send({ 'hosts': followingHosts })
+ .expect(expectedStatus)
+}
+
+async function unfollow (url: string, accessToken: string, target: ServerInfo, expectedStatus = 204) {
+ const path = '/api/v1/server/following/' + target.host
+
+ return request(url)
+ .delete(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .expect(expectedStatus)
+}
+
+function removeFollower (url: string, accessToken: string, follower: ServerInfo, expectedStatus = 204) {
+ const path = '/api/v1/server/followers/peertube@' + follower.host
+
+ return request(url)
+ .delete(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .expect(expectedStatus)
+}
+
+async function doubleFollow (server1: ServerInfo, server2: ServerInfo) {
+ await Promise.all([
+ follow(server1.url, [ server2.url ], server1.accessToken),
+ follow(server2.url, [ server1.url ], server2.accessToken)
+ ])
+
+ // Wait request propagation
+ await waitJobs([ server1, server2 ])
+
+ return true
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ getFollowersListPaginationAndSort,
+ getFollowingListPaginationAndSort,
+ unfollow,
+ removeFollower,
+ follow,
+ doubleFollow,
+ acceptFollower,
+ rejectFollower
+}
--- /dev/null
+import * as request from 'supertest'
+import { Job, JobState } from '../../models'
+import { wait } from '../miscs/miscs'
+import { ServerInfo } from './servers'
+
+function getJobsList (url: string, accessToken: string, state: JobState) {
+ const path = '/api/v1/jobs/' + state
+
+ return request(url)
+ .get(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function getJobsListPaginationAndSort (url: string, accessToken: string, state: JobState, start: number, count: number, sort: string) {
+ const path = '/api/v1/jobs/' + state
+
+ return request(url)
+ .get(path)
+ .query({ start })
+ .query({ count })
+ .query({ sort })
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+async function waitJobs (serversArg: ServerInfo[] | ServerInfo) {
+ const pendingJobWait = process.env.NODE_PENDING_JOB_WAIT ? parseInt(process.env.NODE_PENDING_JOB_WAIT, 10) : 2000
+ let servers: ServerInfo[]
+
+ if (Array.isArray(serversArg) === false) servers = [ serversArg as ServerInfo ]
+ else servers = serversArg as ServerInfo[]
+
+ const states: JobState[] = [ 'waiting', 'active', 'delayed' ]
+ let pendingRequests = false
+
+ function tasksBuilder () {
+ const tasks: Promise<any>[] = []
+ pendingRequests = false
+
+ // Check if each server has pending request
+ for (const server of servers) {
+ for (const state of states) {
+ const p = getJobsListPaginationAndSort(server.url, server.accessToken, state, 0, 10, '-createdAt')
+ .then(res => res.body.data)
+ .then((jobs: Job[]) => jobs.filter(j => j.type !== 'videos-views'))
+ .then(jobs => {
+ if (jobs.length !== 0) pendingRequests = true
+ })
+ tasks.push(p)
+ }
+ }
+
+ return tasks
+ }
+
+ do {
+ await Promise.all(tasksBuilder())
+
+ // Retry, in case of new jobs were created
+ if (pendingRequests === false) {
+ await wait(pendingJobWait)
+ await Promise.all(tasksBuilder())
+ }
+
+ if (pendingRequests) {
+ await wait(1000)
+ }
+ } while (pendingRequests)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ getJobsList,
+ waitJobs,
+ getJobsListPaginationAndSort
+}
--- /dev/null
+import { makePutBodyRequest } from '../requests/requests'
+
+async function updateRedundancy (url: string, accessToken: string, host: string, redundancyAllowed: boolean, expectedStatus = 204) {
+ const path = '/api/v1/server/redundancy/' + host
+
+ return makePutBodyRequest({
+ url,
+ path,
+ token: accessToken,
+ fields: { redundancyAllowed },
+ statusCodeExpected: expectedStatus
+ })
+}
+
+export {
+ updateRedundancy
+}
--- /dev/null
+/* tslint:disable:no-unused-expression */
+
+import { ChildProcess, exec, fork } from 'child_process'
+import { join } from 'path'
+import { root, wait } from '../miscs/miscs'
+import { readdir, readFile } from 'fs-extra'
+import { existsSync } from 'fs'
+import { expect } from 'chai'
+import { VideoChannel } from '../../models/videos'
+
+interface ServerInfo {
+ app: ChildProcess,
+ url: string
+ host: string
+ serverNumber: number
+
+ client: {
+ id: string,
+ secret: string
+ }
+
+ user: {
+ username: string,
+ password: string,
+ email?: string
+ }
+
+ accessToken?: string
+ videoChannel?: VideoChannel
+
+ video?: {
+ id: number
+ uuid: string
+ name: string
+ account: {
+ name: string
+ }
+ }
+
+ remoteVideo?: {
+ id: number
+ uuid: string
+ }
+
+ videos?: { id: number, uuid: string }[]
+}
+
+function flushAndRunMultipleServers (totalServers: number, configOverride?: Object) {
+ let apps = []
+ let i = 0
+
+ return new Promise<ServerInfo[]>(res => {
+ function anotherServerDone (serverNumber, app) {
+ apps[serverNumber - 1] = app
+ i++
+ if (i === totalServers) {
+ return res(apps)
+ }
+ }
+
+ flushTests()
+ .then(() => {
+ for (let j = 1; j <= totalServers; j++) {
+ runServer(j, configOverride).then(app => anotherServerDone(j, app))
+ }
+ })
+ })
+}
+
+function flushTests () {
+ return new Promise<void>((res, rej) => {
+ return exec('npm run clean:server:test', err => {
+ if (err) return rej(err)
+
+ return res()
+ })
+ })
+}
+
+function runServer (serverNumber: number, configOverride?: Object, args = []) {
+ const server: ServerInfo = {
+ app: null,
+ serverNumber: serverNumber,
+ url: `http://localhost:${9000 + serverNumber}`,
+ host: `localhost:${9000 + serverNumber}`,
+ client: {
+ id: null,
+ secret: null
+ },
+ user: {
+ username: null,
+ password: null
+ }
+ }
+
+ // These actions are async so we need to be sure that they have both been done
+ const serverRunString = {
+ 'Server listening': false
+ }
+ const key = 'Database peertube_test' + serverNumber + ' is ready'
+ serverRunString[key] = false
+
+ const regexps = {
+ client_id: 'Client id: (.+)',
+ client_secret: 'Client secret: (.+)',
+ user_username: 'Username: (.+)',
+ user_password: 'User password: (.+)'
+ }
+
+ // Share the environment
+ const env = Object.create(process.env)
+ env['NODE_ENV'] = 'test'
+ env['NODE_APP_INSTANCE'] = serverNumber.toString()
+
+ if (configOverride !== undefined) {
+ env['NODE_CONFIG'] = JSON.stringify(configOverride)
+ }
+
+ const options = {
+ silent: true,
+ env: env,
+ detached: true
+ }
+
+ return new Promise<ServerInfo>(res => {
+ server.app = fork(join(root(), 'dist', 'server.js'), args, options)
+ server.app.stdout.on('data', function onStdout (data) {
+ let dontContinue = false
+
+ // Capture things if we want to
+ for (const key of Object.keys(regexps)) {
+ const regexp = regexps[key]
+ const matches = data.toString().match(regexp)
+ if (matches !== null) {
+ if (key === 'client_id') server.client.id = matches[1]
+ else if (key === 'client_secret') server.client.secret = matches[1]
+ else if (key === 'user_username') server.user.username = matches[1]
+ else if (key === 'user_password') server.user.password = matches[1]
+ }
+ }
+
+ // Check if all required sentences are here
+ for (const key of Object.keys(serverRunString)) {
+ if (data.toString().indexOf(key) !== -1) serverRunString[key] = true
+ if (serverRunString[key] === false) dontContinue = true
+ }
+
+ // If no, there is maybe one thing not already initialized (client/user credentials generation...)
+ if (dontContinue === true) return
+
+ server.app.stdout.removeListener('data', onStdout)
+
+ process.on('exit', () => {
+ try {
+ process.kill(server.app.pid)
+ } catch { /* empty */ }
+ })
+
+ res(server)
+ })
+
+ })
+}
+
+async function reRunServer (server: ServerInfo, configOverride?: any) {
+ const newServer = await runServer(server.serverNumber, configOverride)
+ server.app = newServer.app
+
+ return server
+}
+
+async function checkTmpIsEmpty (server: ServerInfo) {
+ return checkDirectoryIsEmpty(server, 'tmp')
+}
+
+async function checkDirectoryIsEmpty (server: ServerInfo, directory: string) {
+ const testDirectory = 'test' + server.serverNumber
+
+ const directoryPath = join(root(), testDirectory, directory)
+
+ const directoryExists = existsSync(directoryPath)
+ expect(directoryExists).to.be.true
+
+ const files = await readdir(directoryPath)
+ expect(files).to.have.lengthOf(0)
+}
+
+function killallServers (servers: ServerInfo[]) {
+ for (const server of servers) {
+ process.kill(-server.app.pid)
+ }
+}
+
+async function waitUntilLog (server: ServerInfo, str: string, count = 1) {
+ const logfile = join(root(), 'test' + server.serverNumber, 'logs/peertube.log')
+
+ while (true) {
+ const buf = await readFile(logfile)
+
+ const matches = buf.toString().match(new RegExp(str, 'g'))
+ if (matches && matches.length === count) return
+
+ await wait(1000)
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ checkDirectoryIsEmpty,
+ checkTmpIsEmpty,
+ ServerInfo,
+ flushAndRunMultipleServers,
+ flushTests,
+ runServer,
+ killallServers,
+ reRunServer,
+ waitUntilLog
+}
--- /dev/null
+import { makeGetRequest } from '../requests/requests'
+
+function getStats (url: string, useCache = false) {
+ const path = '/api/v1/server/stats'
+
+ const query = {
+ t: useCache ? undefined : new Date().getTime()
+ }
+
+ return makeGetRequest({
+ url,
+ path,
+ query,
+ statusCodeExpected: 200
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ getStats
+}
--- /dev/null
+import * as io from 'socket.io-client'
+
+function getUserNotificationSocket (serverUrl: string, accessToken: string) {
+ return io(serverUrl + '/user-notifications', {
+ query: { accessToken }
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ getUserNotificationSocket
+}
--- /dev/null
+/* tslint:disable:no-unused-expression */
+
+import * as request from 'supertest'
+import { expect } from 'chai'
+import { existsSync, readdir } from 'fs-extra'
+import { join } from 'path'
+import { Account } from '../../models/actors'
+import { root } from '../miscs/miscs'
+import { makeGetRequest } from '../requests/requests'
+import { VideoRateType } from '../../models/videos'
+
+function getAccountsList (url: string, sort = '-createdAt', statusCodeExpected = 200) {
+ const path = '/api/v1/accounts'
+
+ return makeGetRequest({
+ url,
+ query: { sort },
+ path,
+ statusCodeExpected
+ })
+}
+
+function getAccount (url: string, accountName: string, statusCodeExpected = 200) {
+ const path = '/api/v1/accounts/' + accountName
+
+ return makeGetRequest({
+ url,
+ path,
+ statusCodeExpected
+ })
+}
+
+async function expectAccountFollows (url: string, nameWithDomain: string, followersCount: number, followingCount: number) {
+ const res = await getAccountsList(url)
+ const account = res.body.data.find((a: Account) => a.name + '@' + a.host === nameWithDomain)
+
+ const message = `${nameWithDomain} on ${url}`
+ expect(account.followersCount).to.equal(followersCount, message)
+ expect(account.followingCount).to.equal(followingCount, message)
+}
+
+async function checkActorFilesWereRemoved (actorUUID: string, serverNumber: number) {
+ const testDirectory = 'test' + serverNumber
+
+ for (const directory of [ 'avatars' ]) {
+ const directoryPath = join(root(), testDirectory, directory)
+
+ const directoryExists = existsSync(directoryPath)
+ expect(directoryExists).to.be.true
+
+ const files = await readdir(directoryPath)
+ for (const file of files) {
+ expect(file).to.not.contain(actorUUID)
+ }
+ }
+}
+
+function getAccountRatings (url: string, accountName: string, accessToken: string, rating?: VideoRateType, statusCodeExpected = 200) {
+ const path = '/api/v1/accounts/' + accountName + '/ratings'
+
+ const query = rating ? { rating } : {}
+
+ return request(url)
+ .get(path)
+ .query(query)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .expect(statusCodeExpected)
+ .expect('Content-Type', /json/)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ getAccount,
+ expectAccountFollows,
+ getAccountsList,
+ checkActorFilesWereRemoved,
+ getAccountRatings
+}
--- /dev/null
+/* tslint:disable:no-unused-expression */
+
+import { makeGetRequest, makeDeleteRequest, makePostBodyRequest } from '../requests/requests'
+
+function getAccountBlocklistByAccount (
+ url: string,
+ token: string,
+ start: number,
+ count: number,
+ sort = '-createdAt',
+ statusCodeExpected = 200
+) {
+ const path = '/api/v1/users/me/blocklist/accounts'
+
+ return makeGetRequest({
+ url,
+ token,
+ query: { start, count, sort },
+ path,
+ statusCodeExpected
+ })
+}
+
+function addAccountToAccountBlocklist (url: string, token: string, accountToBlock: string, statusCodeExpected = 204) {
+ const path = '/api/v1/users/me/blocklist/accounts'
+
+ return makePostBodyRequest({
+ url,
+ path,
+ token,
+ fields: {
+ accountName: accountToBlock
+ },
+ statusCodeExpected
+ })
+}
+
+function removeAccountFromAccountBlocklist (url: string, token: string, accountToUnblock: string, statusCodeExpected = 204) {
+ const path = '/api/v1/users/me/blocklist/accounts/' + accountToUnblock
+
+ return makeDeleteRequest({
+ url,
+ path,
+ token,
+ statusCodeExpected
+ })
+}
+
+function getServerBlocklistByAccount (
+ url: string,
+ token: string,
+ start: number,
+ count: number,
+ sort = '-createdAt',
+ statusCodeExpected = 200
+) {
+ const path = '/api/v1/users/me/blocklist/servers'
+
+ return makeGetRequest({
+ url,
+ token,
+ query: { start, count, sort },
+ path,
+ statusCodeExpected
+ })
+}
+
+function addServerToAccountBlocklist (url: string, token: string, serverToBlock: string, statusCodeExpected = 204) {
+ const path = '/api/v1/users/me/blocklist/servers'
+
+ return makePostBodyRequest({
+ url,
+ path,
+ token,
+ fields: {
+ host: serverToBlock
+ },
+ statusCodeExpected
+ })
+}
+
+function removeServerFromAccountBlocklist (url: string, token: string, serverToBlock: string, statusCodeExpected = 204) {
+ const path = '/api/v1/users/me/blocklist/servers/' + serverToBlock
+
+ return makeDeleteRequest({
+ url,
+ path,
+ token,
+ statusCodeExpected
+ })
+}
+
+function getAccountBlocklistByServer (
+ url: string,
+ token: string,
+ start: number,
+ count: number,
+ sort = '-createdAt',
+ statusCodeExpected = 200
+) {
+ const path = '/api/v1/server/blocklist/accounts'
+
+ return makeGetRequest({
+ url,
+ token,
+ query: { start, count, sort },
+ path,
+ statusCodeExpected
+ })
+}
+
+function addAccountToServerBlocklist (url: string, token: string, accountToBlock: string, statusCodeExpected = 204) {
+ const path = '/api/v1/server/blocklist/accounts'
+
+ return makePostBodyRequest({
+ url,
+ path,
+ token,
+ fields: {
+ accountName: accountToBlock
+ },
+ statusCodeExpected
+ })
+}
+
+function removeAccountFromServerBlocklist (url: string, token: string, accountToUnblock: string, statusCodeExpected = 204) {
+ const path = '/api/v1/server/blocklist/accounts/' + accountToUnblock
+
+ return makeDeleteRequest({
+ url,
+ path,
+ token,
+ statusCodeExpected
+ })
+}
+
+function getServerBlocklistByServer (
+ url: string,
+ token: string,
+ start: number,
+ count: number,
+ sort = '-createdAt',
+ statusCodeExpected = 200
+) {
+ const path = '/api/v1/server/blocklist/servers'
+
+ return makeGetRequest({
+ url,
+ token,
+ query: { start, count, sort },
+ path,
+ statusCodeExpected
+ })
+}
+
+function addServerToServerBlocklist (url: string, token: string, serverToBlock: string, statusCodeExpected = 204) {
+ const path = '/api/v1/server/blocklist/servers'
+
+ return makePostBodyRequest({
+ url,
+ path,
+ token,
+ fields: {
+ host: serverToBlock
+ },
+ statusCodeExpected
+ })
+}
+
+function removeServerFromServerBlocklist (url: string, token: string, serverToBlock: string, statusCodeExpected = 204) {
+ const path = '/api/v1/server/blocklist/servers/' + serverToBlock
+
+ return makeDeleteRequest({
+ url,
+ path,
+ token,
+ statusCodeExpected
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ getAccountBlocklistByAccount,
+ addAccountToAccountBlocklist,
+ removeAccountFromAccountBlocklist,
+ getServerBlocklistByAccount,
+ addServerToAccountBlocklist,
+ removeServerFromAccountBlocklist,
+
+ getAccountBlocklistByServer,
+ addAccountToServerBlocklist,
+ removeAccountFromServerBlocklist,
+ getServerBlocklistByServer,
+ addServerToServerBlocklist,
+ removeServerFromServerBlocklist
+}
--- /dev/null
+import * as request from 'supertest'
+
+import { ServerInfo } from '../server/servers'
+
+type Client = { id: string, secret: string }
+type User = { username: string, password: string }
+type Server = { url: string, client: Client, user: User }
+
+function login (url: string, client: Client, user: User, expectedStatus = 200) {
+ const path = '/api/v1/users/token'
+
+ const body = {
+ client_id: client.id,
+ client_secret: client.secret,
+ username: user.username,
+ password: user.password,
+ response_type: 'code',
+ grant_type: 'password',
+ scope: 'upload'
+ }
+
+ return request(url)
+ .post(path)
+ .type('form')
+ .send(body)
+ .expect(expectedStatus)
+}
+
+async function serverLogin (server: Server) {
+ const res = await login(server.url, server.client, server.user, 200)
+
+ return res.body.access_token as string
+}
+
+async function userLogin (server: Server, user: User, expectedStatus = 200) {
+ const res = await login(server.url, server.client, user, expectedStatus)
+
+ return res.body.access_token as string
+}
+
+function setAccessTokensToServers (servers: ServerInfo[]) {
+ const tasks: Promise<any>[] = []
+
+ for (const server of servers) {
+ const p = serverLogin(server).then(t => server.accessToken = t)
+ tasks.push(p)
+ }
+
+ return Promise.all(tasks)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ login,
+ serverLogin,
+ userLogin,
+ setAccessTokensToServers,
+ Server,
+ Client,
+ User
+}
--- /dev/null
+/* tslint:disable:no-unused-expression */
+
+import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
+import { UserNotification, UserNotificationSetting, UserNotificationType } from '../../models/users'
+import { ServerInfo } from '..'
+import { expect } from 'chai'
+import { inspect } from 'util'
+
+function updateMyNotificationSettings (url: string, token: string, settings: UserNotificationSetting, statusCodeExpected = 204) {
+ const path = '/api/v1/users/me/notification-settings'
+
+ return makePutBodyRequest({
+ url,
+ path,
+ token,
+ fields: settings,
+ statusCodeExpected
+ })
+}
+
+async function getUserNotifications (
+ url: string,
+ token: string,
+ start: number,
+ count: number,
+ unread?: boolean,
+ sort = '-createdAt',
+ statusCodeExpected = 200
+) {
+ const path = '/api/v1/users/me/notifications'
+
+ return makeGetRequest({
+ url,
+ path,
+ token,
+ query: {
+ start,
+ count,
+ sort,
+ unread
+ },
+ statusCodeExpected
+ })
+}
+
+function markAsReadNotifications (url: string, token: string, ids: number[], statusCodeExpected = 204) {
+ const path = '/api/v1/users/me/notifications/read'
+
+ return makePostBodyRequest({
+ url,
+ path,
+ token,
+ fields: { ids },
+ statusCodeExpected
+ })
+}
+function markAsReadAllNotifications (url: string, token: string, statusCodeExpected = 204) {
+ const path = '/api/v1/users/me/notifications/read-all'
+
+ return makePostBodyRequest({
+ url,
+ path,
+ token,
+ statusCodeExpected
+ })
+}
+
+async function getLastNotification (serverUrl: string, accessToken: string) {
+ const res = await getUserNotifications(serverUrl, accessToken, 0, 1, undefined, '-createdAt')
+
+ if (res.body.total === 0) return undefined
+
+ return res.body.data[0] as UserNotification
+}
+
+type CheckerBaseParams = {
+ server: ServerInfo
+ emails: object[]
+ socketNotifications: UserNotification[]
+ token: string,
+ check?: { web: boolean, mail: boolean }
+}
+
+type CheckerType = 'presence' | 'absence'
+
+async function checkNotification (
+ base: CheckerBaseParams,
+ notificationChecker: (notification: UserNotification, type: CheckerType) => void,
+ emailNotificationFinder: (email: object) => boolean,
+ checkType: CheckerType
+) {
+ const check = base.check || { web: true, mail: true }
+
+ if (check.web) {
+ const notification = await getLastNotification(base.server.url, base.token)
+
+ if (notification || checkType !== 'absence') {
+ notificationChecker(notification, checkType)
+ }
+
+ const socketNotification = base.socketNotifications.find(n => {
+ try {
+ notificationChecker(n, 'presence')
+ return true
+ } catch {
+ return false
+ }
+ })
+
+ if (checkType === 'presence') {
+ const obj = inspect(base.socketNotifications, { depth: 5 })
+ expect(socketNotification, 'The socket notification is absent. ' + obj).to.not.be.undefined
+ } else {
+ const obj = inspect(socketNotification, { depth: 5 })
+ expect(socketNotification, 'The socket notification is present. ' + obj).to.be.undefined
+ }
+ }
+
+ if (check.mail) {
+ // Last email
+ const email = base.emails
+ .slice()
+ .reverse()
+ .find(e => emailNotificationFinder(e))
+
+ if (checkType === 'presence') {
+ expect(email, 'The email is absent. ' + inspect(base.emails)).to.not.be.undefined
+ } else {
+ expect(email, 'The email is present. ' + inspect(email)).to.be.undefined
+ }
+ }
+}
+
+function checkVideo (video: any, videoName?: string, videoUUID?: string) {
+ expect(video.name).to.be.a('string')
+ expect(video.name).to.not.be.empty
+ if (videoName) expect(video.name).to.equal(videoName)
+
+ expect(video.uuid).to.be.a('string')
+ expect(video.uuid).to.not.be.empty
+ if (videoUUID) expect(video.uuid).to.equal(videoUUID)
+
+ expect(video.id).to.be.a('number')
+}
+
+function checkActor (actor: any) {
+ expect(actor.displayName).to.be.a('string')
+ expect(actor.displayName).to.not.be.empty
+ expect(actor.host).to.not.be.undefined
+}
+
+function checkComment (comment: any, commentId: number, threadId: number) {
+ expect(comment.id).to.equal(commentId)
+ expect(comment.threadId).to.equal(threadId)
+}
+
+async function checkNewVideoFromSubscription (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) {
+ const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION
+
+ function notificationChecker (notification: UserNotification, type: CheckerType) {
+ if (type === 'presence') {
+ expect(notification).to.not.be.undefined
+ expect(notification.type).to.equal(notificationType)
+
+ checkVideo(notification.video, videoName, videoUUID)
+ checkActor(notification.video.channel)
+ } else {
+ expect(notification).to.satisfy((n: UserNotification) => {
+ return n === undefined || n.type !== UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION || n.video.name !== videoName
+ })
+ }
+ }
+
+ function emailFinder (email: object) {
+ const text = email[ 'text' ]
+ return text.indexOf(videoUUID) !== -1 && text.indexOf('Your subscription') !== -1
+ }
+
+ await checkNotification(base, notificationChecker, emailFinder, type)
+}
+
+async function checkVideoIsPublished (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) {
+ const notificationType = UserNotificationType.MY_VIDEO_PUBLISHED
+
+ function notificationChecker (notification: UserNotification, type: CheckerType) {
+ if (type === 'presence') {
+ expect(notification).to.not.be.undefined
+ expect(notification.type).to.equal(notificationType)
+
+ checkVideo(notification.video, videoName, videoUUID)
+ checkActor(notification.video.channel)
+ } else {
+ expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName)
+ }
+ }
+
+ function emailFinder (email: object) {
+ const text: string = email[ 'text' ]
+ return text.includes(videoUUID) && text.includes('Your video')
+ }
+
+ await checkNotification(base, notificationChecker, emailFinder, type)
+}
+
+async function checkMyVideoImportIsFinished (
+ base: CheckerBaseParams,
+ videoName: string,
+ videoUUID: string,
+ url: string,
+ success: boolean,
+ type: CheckerType
+) {
+ const notificationType = success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR
+
+ function notificationChecker (notification: UserNotification, type: CheckerType) {
+ if (type === 'presence') {
+ expect(notification).to.not.be.undefined
+ expect(notification.type).to.equal(notificationType)
+
+ expect(notification.videoImport.targetUrl).to.equal(url)
+
+ if (success) checkVideo(notification.videoImport.video, videoName, videoUUID)
+ } else {
+ expect(notification.videoImport).to.satisfy(i => i === undefined || i.targetUrl !== url)
+ }
+ }
+
+ function emailFinder (email: object) {
+ const text: string = email[ 'text' ]
+ const toFind = success ? ' finished' : ' error'
+
+ return text.includes(url) && text.includes(toFind)
+ }
+
+ await checkNotification(base, notificationChecker, emailFinder, type)
+}
+
+async function checkUserRegistered (base: CheckerBaseParams, username: string, type: CheckerType) {
+ const notificationType = UserNotificationType.NEW_USER_REGISTRATION
+
+ function notificationChecker (notification: UserNotification, type: CheckerType) {
+ if (type === 'presence') {
+ expect(notification).to.not.be.undefined
+ expect(notification.type).to.equal(notificationType)
+
+ checkActor(notification.account)
+ expect(notification.account.name).to.equal(username)
+ } else {
+ expect(notification).to.satisfy(n => n.type !== notificationType || n.account.name !== username)
+ }
+ }
+
+ function emailFinder (email: object) {
+ const text: string = email[ 'text' ]
+
+ return text.includes(' registered ') && text.includes(username)
+ }
+
+ await checkNotification(base, notificationChecker, emailFinder, type)
+}
+
+async function checkNewActorFollow (
+ base: CheckerBaseParams,
+ followType: 'channel' | 'account',
+ followerName: string,
+ followerDisplayName: string,
+ followingDisplayName: string,
+ type: CheckerType
+) {
+ const notificationType = UserNotificationType.NEW_FOLLOW
+
+ function notificationChecker (notification: UserNotification, type: CheckerType) {
+ if (type === 'presence') {
+ expect(notification).to.not.be.undefined
+ expect(notification.type).to.equal(notificationType)
+
+ checkActor(notification.actorFollow.follower)
+ expect(notification.actorFollow.follower.displayName).to.equal(followerDisplayName)
+ expect(notification.actorFollow.follower.name).to.equal(followerName)
+ expect(notification.actorFollow.follower.host).to.not.be.undefined
+
+ expect(notification.actorFollow.following.displayName).to.equal(followingDisplayName)
+ expect(notification.actorFollow.following.type).to.equal(followType)
+ } else {
+ expect(notification).to.satisfy(n => {
+ return n.type !== notificationType ||
+ (n.actorFollow.follower.name !== followerName && n.actorFollow.following !== followingDisplayName)
+ })
+ }
+ }
+
+ function emailFinder (email: object) {
+ const text: string = email[ 'text' ]
+
+ return text.includes('Your ' + followType) && text.includes(followingDisplayName) && text.includes(followerDisplayName)
+ }
+
+ await checkNotification(base, notificationChecker, emailFinder, type)
+}
+
+async function checkNewInstanceFollower (base: CheckerBaseParams, followerHost: string, type: CheckerType) {
+ const notificationType = UserNotificationType.NEW_INSTANCE_FOLLOWER
+
+ function notificationChecker (notification: UserNotification, type: CheckerType) {
+ if (type === 'presence') {
+ expect(notification).to.not.be.undefined
+ expect(notification.type).to.equal(notificationType)
+
+ checkActor(notification.actorFollow.follower)
+ expect(notification.actorFollow.follower.name).to.equal('peertube')
+ expect(notification.actorFollow.follower.host).to.equal(followerHost)
+
+ expect(notification.actorFollow.following.name).to.equal('peertube')
+ } else {
+ expect(notification).to.satisfy(n => {
+ return n.type !== notificationType || n.actorFollow.follower.host !== followerHost
+ })
+ }
+ }
+
+ function emailFinder (email: object) {
+ const text: string = email[ 'text' ]
+
+ return text.includes('instance has a new follower') && text.includes(followerHost)
+ }
+
+ await checkNotification(base, notificationChecker, emailFinder, type)
+}
+
+async function checkCommentMention (
+ base: CheckerBaseParams,
+ uuid: string,
+ commentId: number,
+ threadId: number,
+ byAccountDisplayName: string,
+ type: CheckerType
+) {
+ const notificationType = UserNotificationType.COMMENT_MENTION
+
+ function notificationChecker (notification: UserNotification, type: CheckerType) {
+ if (type === 'presence') {
+ expect(notification).to.not.be.undefined
+ expect(notification.type).to.equal(notificationType)
+
+ checkComment(notification.comment, commentId, threadId)
+ checkActor(notification.comment.account)
+ expect(notification.comment.account.displayName).to.equal(byAccountDisplayName)
+
+ checkVideo(notification.comment.video, undefined, uuid)
+ } else {
+ expect(notification).to.satisfy(n => n.type !== notificationType || n.comment.id !== commentId)
+ }
+ }
+
+ function emailFinder (email: object) {
+ const text: string = email[ 'text' ]
+
+ return text.includes(' mentioned ') && text.includes(uuid) && text.includes(byAccountDisplayName)
+ }
+
+ await checkNotification(base, notificationChecker, emailFinder, type)
+}
+
+let lastEmailCount = 0
+async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, commentId: number, threadId: number, type: CheckerType) {
+ const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO
+
+ function notificationChecker (notification: UserNotification, type: CheckerType) {
+ if (type === 'presence') {
+ expect(notification).to.not.be.undefined
+ expect(notification.type).to.equal(notificationType)
+
+ checkComment(notification.comment, commentId, threadId)
+ checkActor(notification.comment.account)
+ checkVideo(notification.comment.video, undefined, uuid)
+ } else {
+ expect(notification).to.satisfy((n: UserNotification) => {
+ return n === undefined || n.comment === undefined || n.comment.id !== commentId
+ })
+ }
+ }
+
+ const commentUrl = `http://localhost:9001/videos/watch/${uuid};threadId=${threadId}`
+ function emailFinder (email: object) {
+ return email[ 'text' ].indexOf(commentUrl) !== -1
+ }
+
+ await checkNotification(base, notificationChecker, emailFinder, type)
+
+ if (type === 'presence') {
+ // We cannot detect email duplicates, so check we received another email
+ expect(base.emails).to.have.length.above(lastEmailCount)
+ lastEmailCount = base.emails.length
+ }
+}
+
+async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
+ const notificationType = UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS
+
+ function notificationChecker (notification: UserNotification, type: CheckerType) {
+ if (type === 'presence') {
+ expect(notification).to.not.be.undefined
+ expect(notification.type).to.equal(notificationType)
+
+ expect(notification.videoAbuse.id).to.be.a('number')
+ checkVideo(notification.videoAbuse.video, videoName, videoUUID)
+ } else {
+ expect(notification).to.satisfy((n: UserNotification) => {
+ return n === undefined || n.videoAbuse === undefined || n.videoAbuse.video.uuid !== videoUUID
+ })
+ }
+ }
+
+ function emailFinder (email: object) {
+ const text = email[ 'text' ]
+ return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1
+ }
+
+ await checkNotification(base, notificationChecker, emailFinder, type)
+}
+
+async function checkVideoAutoBlacklistForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
+ const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS
+
+ function notificationChecker (notification: UserNotification, type: CheckerType) {
+ if (type === 'presence') {
+ expect(notification).to.not.be.undefined
+ expect(notification.type).to.equal(notificationType)
+
+ expect(notification.video.id).to.be.a('number')
+ checkVideo(notification.video, videoName, videoUUID)
+ } else {
+ expect(notification).to.satisfy((n: UserNotification) => {
+ return n === undefined || n.video === undefined || n.video.uuid !== videoUUID
+ })
+ }
+ }
+
+ function emailFinder (email: object) {
+ const text = email[ 'text' ]
+ return text.indexOf(videoUUID) !== -1 && email[ 'text' ].indexOf('video-auto-blacklist/list') !== -1
+ }
+
+ await checkNotification(base, notificationChecker, emailFinder, type)
+}
+
+async function checkNewBlacklistOnMyVideo (
+ base: CheckerBaseParams,
+ videoUUID: string,
+ videoName: string,
+ blacklistType: 'blacklist' | 'unblacklist'
+) {
+ const notificationType = blacklistType === 'blacklist'
+ ? UserNotificationType.BLACKLIST_ON_MY_VIDEO
+ : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO
+
+ function notificationChecker (notification: UserNotification) {
+ expect(notification).to.not.be.undefined
+ expect(notification.type).to.equal(notificationType)
+
+ const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video
+
+ checkVideo(video, videoName, videoUUID)
+ }
+
+ function emailFinder (email: object) {
+ const text = email[ 'text' ]
+ return text.indexOf(videoUUID) !== -1 && text.indexOf(' ' + blacklistType) !== -1
+ }
+
+ await checkNotification(base, notificationChecker, emailFinder, 'presence')
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ CheckerBaseParams,
+ CheckerType,
+ checkNotification,
+ markAsReadAllNotifications,
+ checkMyVideoImportIsFinished,
+ checkUserRegistered,
+ checkVideoIsPublished,
+ checkNewVideoFromSubscription,
+ checkNewActorFollow,
+ checkNewCommentOnMyVideo,
+ checkNewBlacklistOnMyVideo,
+ checkCommentMention,
+ updateMyNotificationSettings,
+ checkNewVideoAbuseForModerators,
+ checkVideoAutoBlacklistForModerators,
+ getUserNotifications,
+ markAsReadNotifications,
+ getLastNotification,
+ checkNewInstanceFollower
+}
--- /dev/null
+import { makeDeleteRequest, makeGetRequest, makePostBodyRequest } from '../requests/requests'
+
+function addUserSubscription (url: string, token: string, targetUri: string, statusCodeExpected = 204) {
+ const path = '/api/v1/users/me/subscriptions'
+
+ return makePostBodyRequest({
+ url,
+ path,
+ token,
+ statusCodeExpected,
+ fields: { uri: targetUri }
+ })
+}
+
+function listUserSubscriptions (url: string, token: string, sort = '-createdAt', statusCodeExpected = 200) {
+ const path = '/api/v1/users/me/subscriptions'
+
+ return makeGetRequest({
+ url,
+ path,
+ token,
+ statusCodeExpected,
+ query: { sort }
+ })
+}
+
+function listUserSubscriptionVideos (url: string, token: string, sort = '-createdAt', statusCodeExpected = 200) {
+ const path = '/api/v1/users/me/subscriptions/videos'
+
+ return makeGetRequest({
+ url,
+ path,
+ token,
+ statusCodeExpected,
+ query: { sort }
+ })
+}
+
+function getUserSubscription (url: string, token: string, uri: string, statusCodeExpected = 200) {
+ const path = '/api/v1/users/me/subscriptions/' + uri
+
+ return makeGetRequest({
+ url,
+ path,
+ token,
+ statusCodeExpected
+ })
+}
+
+function removeUserSubscription (url: string, token: string, uri: string, statusCodeExpected = 204) {
+ const path = '/api/v1/users/me/subscriptions/' + uri
+
+ return makeDeleteRequest({
+ url,
+ path,
+ token,
+ statusCodeExpected
+ })
+}
+
+function areSubscriptionsExist (url: string, token: string, uris: string[], statusCodeExpected = 200) {
+ const path = '/api/v1/users/me/subscriptions/exist'
+
+ return makeGetRequest({
+ url,
+ path,
+ query: { 'uris[]': uris },
+ token,
+ statusCodeExpected
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ areSubscriptionsExist,
+ addUserSubscription,
+ listUserSubscriptions,
+ getUserSubscription,
+ listUserSubscriptionVideos,
+ removeUserSubscription
+}
--- /dev/null
+import * as request from 'supertest'
+import { makePostBodyRequest, makePutBodyRequest, updateAvatarRequest } from '../requests/requests'
+
+import { UserRole } from '../../index'
+import { NSFWPolicyType } from '../../models/videos/nsfw-policy.type'
+import { ServerInfo, userLogin } from '..'
+import { UserAdminFlag } from '../../models/users/user-flag.model'
+
+type CreateUserArgs = { url: string,
+ accessToken: string,
+ username: string,
+ password: string,
+ videoQuota?: number,
+ videoQuotaDaily?: number,
+ role?: UserRole,
+ adminFlags?: UserAdminFlag,
+ specialStatus?: number
+}
+function createUser (parameters: CreateUserArgs) {
+ const {
+ url,
+ accessToken,
+ username,
+ adminFlags,
+ password = 'password',
+ videoQuota = 1000000,
+ videoQuotaDaily = -1,
+ role = UserRole.USER,
+ specialStatus = 200
+ } = parameters
+
+ const path = '/api/v1/users'
+ const body = {
+ username,
+ password,
+ role,
+ adminFlags,
+ email: username + '@example.com',
+ videoQuota,
+ videoQuotaDaily
+ }
+
+ return request(url)
+ .post(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .send(body)
+ .expect(specialStatus)
+}
+
+async function generateUserAccessToken (server: ServerInfo, username: string) {
+ const password = 'my super password'
+ await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password })
+
+ return userLogin(server, { username, password })
+}
+
+function registerUser (url: string, username: string, password: string, specialStatus = 204) {
+ const path = '/api/v1/users/register'
+ const body = {
+ username,
+ password,
+ email: username + '@example.com'
+ }
+
+ return request(url)
+ .post(path)
+ .set('Accept', 'application/json')
+ .send(body)
+ .expect(specialStatus)
+}
+
+function getMyUserInformation (url: string, accessToken: string, specialStatus = 200) {
+ const path = '/api/v1/users/me'
+
+ return request(url)
+ .get(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .expect(specialStatus)
+ .expect('Content-Type', /json/)
+}
+
+function deleteMe (url: string, accessToken: string, specialStatus = 204) {
+ const path = '/api/v1/users/me'
+
+ return request(url)
+ .delete(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .expect(specialStatus)
+}
+
+function getMyUserVideoQuotaUsed (url: string, accessToken: string, specialStatus = 200) {
+ const path = '/api/v1/users/me/video-quota-used'
+
+ return request(url)
+ .get(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .expect(specialStatus)
+ .expect('Content-Type', /json/)
+}
+
+function getUserInformation (url: string, accessToken: string, userId: number) {
+ const path = '/api/v1/users/' + userId
+
+ return request(url)
+ .get(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function getMyUserVideoRating (url: string, accessToken: string, videoId: number | string, specialStatus = 200) {
+ const path = '/api/v1/users/me/videos/' + videoId + '/rating'
+
+ return request(url)
+ .get(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .expect(specialStatus)
+ .expect('Content-Type', /json/)
+}
+
+function getUsersList (url: string, accessToken: string) {
+ const path = '/api/v1/users'
+
+ return request(url)
+ .get(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function getUsersListPaginationAndSort (url: string, accessToken: string, start: number, count: number, sort: string, search?: string) {
+ const path = '/api/v1/users'
+
+ return request(url)
+ .get(path)
+ .query({ start })
+ .query({ count })
+ .query({ sort })
+ .query({ search })
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function removeUser (url: string, userId: number | string, accessToken: string, expectedStatus = 204) {
+ const path = '/api/v1/users'
+
+ return request(url)
+ .delete(path + '/' + userId)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .expect(expectedStatus)
+}
+
+function blockUser (url: string, userId: number | string, accessToken: string, expectedStatus = 204, reason?: string) {
+ const path = '/api/v1/users'
+ let body: any
+ if (reason) body = { reason }
+
+ return request(url)
+ .post(path + '/' + userId + '/block')
+ .send(body)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .expect(expectedStatus)
+}
+
+function unblockUser (url: string, userId: number | string, accessToken: string, expectedStatus = 204) {
+ const path = '/api/v1/users'
+
+ return request(url)
+ .post(path + '/' + userId + '/unblock')
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .expect(expectedStatus)
+}
+
+function updateMyUser (options: {
+ url: string
+ accessToken: string
+ currentPassword?: string
+ newPassword?: string
+ nsfwPolicy?: NSFWPolicyType
+ email?: string
+ autoPlayVideo?: boolean
+ displayName?: string
+ description?: string
+ videosHistoryEnabled?: boolean
+}) {
+ const path = '/api/v1/users/me'
+
+ const toSend = {}
+ if (options.currentPassword !== undefined && options.currentPassword !== null) toSend['currentPassword'] = options.currentPassword
+ if (options.newPassword !== undefined && options.newPassword !== null) toSend['password'] = options.newPassword
+ if (options.nsfwPolicy !== undefined && options.nsfwPolicy !== null) toSend['nsfwPolicy'] = options.nsfwPolicy
+ if (options.autoPlayVideo !== undefined && options.autoPlayVideo !== null) toSend['autoPlayVideo'] = options.autoPlayVideo
+ 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,
+ path,
+ token: options.accessToken,
+ fields: toSend,
+ statusCodeExpected: 204
+ })
+}
+
+function updateMyAvatar (options: {
+ url: string,
+ accessToken: string,
+ fixture: string
+}) {
+ const path = '/api/v1/users/me/avatar/pick'
+
+ return updateAvatarRequest(Object.assign(options, { path }))
+}
+
+function updateUser (options: {
+ url: string
+ userId: number,
+ accessToken: string,
+ email?: string,
+ emailVerified?: boolean,
+ videoQuota?: number,
+ videoQuotaDaily?: number,
+ password?: string,
+ adminFlags?: UserAdminFlag,
+ role?: UserRole
+}) {
+ const path = '/api/v1/users/' + options.userId
+
+ const toSend = {}
+ if (options.password !== undefined && options.password !== null) toSend['password'] = options.password
+ if (options.email !== undefined && options.email !== null) toSend['email'] = options.email
+ if (options.emailVerified !== undefined && options.emailVerified !== null) toSend['emailVerified'] = options.emailVerified
+ if (options.videoQuota !== undefined && options.videoQuota !== null) toSend['videoQuota'] = options.videoQuota
+ if (options.videoQuotaDaily !== undefined && options.videoQuotaDaily !== null) toSend['videoQuotaDaily'] = options.videoQuotaDaily
+ if (options.role !== undefined && options.role !== null) toSend['role'] = options.role
+ if (options.adminFlags !== undefined && options.adminFlags !== null) toSend['adminFlags'] = options.adminFlags
+
+ return makePutBodyRequest({
+ url: options.url,
+ path,
+ token: options.accessToken,
+ fields: toSend,
+ statusCodeExpected: 204
+ })
+}
+
+function askResetPassword (url: string, email: string) {
+ const path = '/api/v1/users/ask-reset-password'
+
+ return makePostBodyRequest({
+ url,
+ path,
+ fields: { email },
+ statusCodeExpected: 204
+ })
+}
+
+function resetPassword (url: string, userId: number, verificationString: string, password: string, statusCodeExpected = 204) {
+ const path = '/api/v1/users/' + userId + '/reset-password'
+
+ return makePostBodyRequest({
+ url,
+ path,
+ fields: { password, verificationString },
+ statusCodeExpected
+ })
+}
+
+function askSendVerifyEmail (url: string, email: string) {
+ const path = '/api/v1/users/ask-send-verify-email'
+
+ return makePostBodyRequest({
+ url,
+ path,
+ fields: { email },
+ statusCodeExpected: 204
+ })
+}
+
+function verifyEmail (url: string, userId: number, verificationString: string, statusCodeExpected = 204) {
+ const path = '/api/v1/users/' + userId + '/verify-email'
+
+ return makePostBodyRequest({
+ url,
+ path,
+ fields: { verificationString },
+ statusCodeExpected
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ createUser,
+ registerUser,
+ getMyUserInformation,
+ getMyUserVideoRating,
+ deleteMe,
+ getMyUserVideoQuotaUsed,
+ getUsersList,
+ getUsersListPaginationAndSort,
+ removeUser,
+ updateUser,
+ updateMyUser,
+ getUserInformation,
+ blockUser,
+ unblockUser,
+ askResetPassword,
+ resetPassword,
+ updateMyAvatar,
+ askSendVerifyEmail,
+ generateUserAccessToken,
+ verifyEmail
+}
--- /dev/null
+import * as request from 'supertest'
+
+function getOEmbed (url: string, oembedUrl: string, format?: string, maxHeight?: number, maxWidth?: number) {
+ const path = '/services/oembed'
+ const query = {
+ url: oembedUrl,
+ format,
+ maxheight: maxHeight,
+ maxwidth: maxWidth
+ }
+
+ return request(url)
+ .get(path)
+ .query(query)
+ .set('Accept', 'application/json')
+ .expect(200)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ getOEmbed
+}
--- /dev/null
+import * as request from 'supertest'
+import { VideoAbuseUpdate } from '../../models/videos/abuse/video-abuse-update.model'
+import { makeDeleteRequest, makePutBodyRequest } from '../requests/requests'
+
+function reportVideoAbuse (url: string, token: string, videoId: number | string, reason: string, specialStatus = 200) {
+ const path = '/api/v1/videos/' + videoId + '/abuse'
+
+ return request(url)
+ .post(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .send({ reason })
+ .expect(specialStatus)
+}
+
+function getVideoAbusesList (url: string, token: string) {
+ const path = '/api/v1/videos/abuse'
+
+ return request(url)
+ .get(path)
+ .query({ sort: 'createdAt' })
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function updateVideoAbuse (
+ url: string,
+ token: string,
+ videoId: string | number,
+ videoAbuseId: number,
+ body: VideoAbuseUpdate,
+ statusCodeExpected = 204
+) {
+ const path = '/api/v1/videos/' + videoId + '/abuse/' + videoAbuseId
+
+ return makePutBodyRequest({
+ url,
+ token,
+ path,
+ fields: body,
+ statusCodeExpected
+ })
+}
+
+function deleteVideoAbuse (url: string, token: string, videoId: string | number, videoAbuseId: number, statusCodeExpected = 204) {
+ const path = '/api/v1/videos/' + videoId + '/abuse/' + videoAbuseId
+
+ return makeDeleteRequest({
+ url,
+ token,
+ path,
+ statusCodeExpected
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ reportVideoAbuse,
+ getVideoAbusesList,
+ updateVideoAbuse,
+ deleteVideoAbuse
+}
--- /dev/null
+import * as request from 'supertest'
+import { VideoBlacklistType } from '../../models/videos'
+import { makeGetRequest } from '..'
+
+function addVideoToBlacklist (
+ url: string,
+ token: string,
+ videoId: number | string,
+ reason?: string,
+ unfederate?: boolean,
+ specialStatus = 204
+) {
+ const path = '/api/v1/videos/' + videoId + '/blacklist'
+
+ return request(url)
+ .post(path)
+ .send({ reason, unfederate })
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .expect(specialStatus)
+}
+
+function updateVideoBlacklist (url: string, token: string, videoId: number, reason?: string, specialStatus = 204) {
+ const path = '/api/v1/videos/' + videoId + '/blacklist'
+
+ return request(url)
+ .put(path)
+ .send({ reason })
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .expect(specialStatus)
+}
+
+function removeVideoFromBlacklist (url: string, token: string, videoId: number | string, specialStatus = 204) {
+ const path = '/api/v1/videos/' + videoId + '/blacklist'
+
+ return request(url)
+ .delete(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .expect(specialStatus)
+}
+
+function getBlacklistedVideosList (parameters: {
+ url: string,
+ token: string,
+ sort?: string,
+ type?: VideoBlacklistType,
+ specialStatus?: number
+}) {
+ let { url, token, sort, type, specialStatus = 200 } = parameters
+ const path = '/api/v1/videos/blacklist/'
+
+ const query = { sort, type }
+
+ return makeGetRequest({
+ url,
+ path,
+ query,
+ token,
+ statusCodeExpected: specialStatus
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ addVideoToBlacklist,
+ removeVideoFromBlacklist,
+ getBlacklistedVideosList,
+ updateVideoBlacklist
+}
--- /dev/null
+import { makeDeleteRequest, makeGetRequest, makeUploadRequest } from '../requests/requests'
+import * as request from 'supertest'
+import * as chai from 'chai'
+import { buildAbsoluteFixturePath } from '../miscs/miscs'
+
+const expect = chai.expect
+
+function createVideoCaption (args: {
+ url: string,
+ accessToken: string
+ videoId: string | number
+ language: string
+ fixture: string,
+ mimeType?: string,
+ statusCodeExpected?: number
+}) {
+ const path = '/api/v1/videos/' + args.videoId + '/captions/' + args.language
+
+ const captionfile = buildAbsoluteFixturePath(args.fixture)
+ const captionfileAttach = args.mimeType ? [ captionfile, { contentType: args.mimeType } ] : captionfile
+
+ return makeUploadRequest({
+ method: 'PUT',
+ url: args.url,
+ path,
+ token: args.accessToken,
+ fields: {},
+ attaches: {
+ captionfile: captionfileAttach
+ },
+ statusCodeExpected: args.statusCodeExpected || 204
+ })
+}
+
+function listVideoCaptions (url: string, videoId: string | number) {
+ const path = '/api/v1/videos/' + videoId + '/captions'
+
+ return makeGetRequest({
+ url,
+ path,
+ statusCodeExpected: 200
+ })
+}
+
+function deleteVideoCaption (url: string, token: string, videoId: string | number, language: string) {
+ const path = '/api/v1/videos/' + videoId + '/captions/' + language
+
+ return makeDeleteRequest({
+ url,
+ token,
+ path,
+ statusCodeExpected: 204
+ })
+}
+
+async function testCaptionFile (url: string, captionPath: string, containsString: string) {
+ const res = await request(url)
+ .get(captionPath)
+ .expect(200)
+
+ expect(res.text).to.contain(containsString)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ createVideoCaption,
+ listVideoCaptions,
+ testCaptionFile,
+ deleteVideoCaption
+}
--- /dev/null
+import * as request from 'supertest'
+
+function changeVideoOwnership (url: string, token: string, videoId: number | string, username, expectedStatus = 204) {
+ const path = '/api/v1/videos/' + videoId + '/give-ownership'
+
+ return request(url)
+ .post(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .send({ username })
+ .expect(expectedStatus)
+}
+
+function getVideoChangeOwnershipList (url: string, token: string) {
+ const path = '/api/v1/videos/ownership'
+
+ return request(url)
+ .get(path)
+ .query({ sort: '-createdAt' })
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function acceptChangeOwnership (url: string, token: string, ownershipId: string, channelId: number, expectedStatus = 204) {
+ const path = '/api/v1/videos/ownership/' + ownershipId + '/accept'
+
+ return request(url)
+ .post(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .send({ channelId })
+ .expect(expectedStatus)
+}
+
+function refuseChangeOwnership (url: string, token: string, ownershipId: string, expectedStatus = 204) {
+ const path = '/api/v1/videos/ownership/' + ownershipId + '/refuse'
+
+ return request(url)
+ .post(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .expect(expectedStatus)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ changeVideoOwnership,
+ getVideoChangeOwnershipList,
+ acceptChangeOwnership,
+ refuseChangeOwnership
+}
--- /dev/null
+import * as request from 'supertest'
+import { VideoChannelCreate, VideoChannelUpdate } from '../../models/videos'
+import { updateAvatarRequest } from '../requests/requests'
+import { getMyUserInformation, ServerInfo } from '..'
+import { User } from '../..'
+
+function getVideoChannelsList (url: string, start: number, count: number, sort?: string) {
+ const path = '/api/v1/video-channels'
+
+ const req = request(url)
+ .get(path)
+ .query({ start: start })
+ .query({ count: count })
+
+ if (sort) req.query({ sort })
+
+ return req.set('Accept', 'application/json')
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function getAccountVideoChannelsList (url: string, accountName: string, specialStatus = 200) {
+ const path = '/api/v1/accounts/' + accountName + '/video-channels'
+
+ return request(url)
+ .get(path)
+ .set('Accept', 'application/json')
+ .expect(specialStatus)
+ .expect('Content-Type', /json/)
+}
+
+function addVideoChannel (
+ url: string,
+ token: string,
+ videoChannelAttributesArg: VideoChannelCreate,
+ expectedStatus = 200
+) {
+ const path = '/api/v1/video-channels/'
+
+ // Default attributes
+ let attributes = {
+ displayName: 'my super video channel',
+ description: 'my super channel description',
+ support: 'my super channel support'
+ }
+ attributes = Object.assign(attributes, videoChannelAttributesArg)
+
+ return request(url)
+ .post(path)
+ .send(attributes)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .expect(expectedStatus)
+}
+
+function updateVideoChannel (
+ url: string,
+ token: string,
+ channelName: string,
+ attributes: VideoChannelUpdate,
+ expectedStatus = 204
+) {
+ const body = {}
+ const path = '/api/v1/video-channels/' + channelName
+
+ if (attributes.displayName) body['displayName'] = attributes.displayName
+ if (attributes.description) body['description'] = attributes.description
+ if (attributes.support) body['support'] = attributes.support
+
+ return request(url)
+ .put(path)
+ .send(body)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .expect(expectedStatus)
+}
+
+function deleteVideoChannel (url: string, token: string, channelName: string, expectedStatus = 204) {
+ const path = '/api/v1/video-channels/' + channelName
+
+ return request(url)
+ .delete(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .expect(expectedStatus)
+}
+
+function getVideoChannel (url: string, channelName: string) {
+ const path = '/api/v1/video-channels/' + channelName
+
+ return request(url)
+ .get(path)
+ .set('Accept', 'application/json')
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function updateVideoChannelAvatar (options: {
+ url: string,
+ accessToken: string,
+ fixture: string,
+ videoChannelName: string | number
+}) {
+
+ const path = '/api/v1/video-channels/' + options.videoChannelName + '/avatar/pick'
+
+ return updateAvatarRequest(Object.assign(options, { path }))
+}
+
+function setDefaultVideoChannel (servers: ServerInfo[]) {
+ const tasks: Promise<any>[] = []
+
+ for (const server of servers) {
+ const p = getMyUserInformation(server.url, server.accessToken)
+ .then(res => server.videoChannel = (res.body as User).videoChannels[0])
+
+ tasks.push(p)
+ }
+
+ return Promise.all(tasks)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ updateVideoChannelAvatar,
+ getVideoChannelsList,
+ getAccountVideoChannelsList,
+ addVideoChannel,
+ updateVideoChannel,
+ deleteVideoChannel,
+ getVideoChannel,
+ setDefaultVideoChannel
+}
--- /dev/null
+import * as request from 'supertest'
+import { makeDeleteRequest } from '../requests/requests'
+
+function getVideoCommentThreads (url: string, videoId: number | string, start: number, count: number, sort?: string, token?: string) {
+ const path = '/api/v1/videos/' + videoId + '/comment-threads'
+
+ const req = request(url)
+ .get(path)
+ .query({ start: start })
+ .query({ count: count })
+
+ if (sort) req.query({ sort })
+ if (token) req.set('Authorization', 'Bearer ' + token)
+
+ return req.set('Accept', 'application/json')
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function getVideoThreadComments (url: string, videoId: number | string, threadId: number, token?: string) {
+ const path = '/api/v1/videos/' + videoId + '/comment-threads/' + threadId
+
+ const req = request(url)
+ .get(path)
+ .set('Accept', 'application/json')
+
+ if (token) req.set('Authorization', 'Bearer ' + token)
+
+ return req.expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function addVideoCommentThread (url: string, token: string, videoId: number | string, text: string, expectedStatus = 200) {
+ const path = '/api/v1/videos/' + videoId + '/comment-threads'
+
+ return request(url)
+ .post(path)
+ .send({ text })
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .expect(expectedStatus)
+}
+
+function addVideoCommentReply (
+ url: string,
+ token: string,
+ videoId: number | string,
+ inReplyToCommentId: number,
+ text: string,
+ expectedStatus = 200
+) {
+ const path = '/api/v1/videos/' + videoId + '/comments/' + inReplyToCommentId
+
+ return request(url)
+ .post(path)
+ .send({ text })
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .expect(expectedStatus)
+}
+
+function deleteVideoComment (
+ url: string,
+ token: string,
+ videoId: number | string,
+ commentId: number,
+ statusCodeExpected = 204
+) {
+ const path = '/api/v1/videos/' + videoId + '/comments/' + commentId
+
+ return makeDeleteRequest({
+ url,
+ path,
+ token,
+ statusCodeExpected
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ getVideoCommentThreads,
+ getVideoThreadComments,
+ addVideoCommentThread,
+ addVideoCommentReply,
+ deleteVideoComment
+}
--- /dev/null
+import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
+
+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 })
+}
+
+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,
+ listMyVideosHistory,
+ removeMyVideosHistory
+}
--- /dev/null
+
+import { VideoImportCreate } from '../../models/videos'
+import { makeGetRequest, makeUploadRequest } from '../requests/requests'
+
+function getYoutubeVideoUrl () {
+ return 'https://youtu.be/msX3jv1XdvM'
+}
+
+function getMagnetURI () {
+ // tslint:disable:max-line-length
+ return 'magnet:?xs=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Ftorrents%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.torrent&xt=urn:btih:0f498834733e8057ed5c6f2ee2b4efd8d84a76ee&dn=super+peertube2+video&tr=wss%3A%2F%2Fpeertube2.cpy.re%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube2.cpy.re%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fwebseed%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.mp4'
+}
+
+function getBadVideoUrl () {
+ return 'https://download.cpy.re/peertube/bad_video.mp4'
+}
+
+function importVideo (url: string, token: string, attributes: VideoImportCreate) {
+ const path = '/api/v1/videos/imports'
+
+ let attaches: any = {}
+ if (attributes.torrentfile) attaches = { torrentfile: attributes.torrentfile }
+
+ return makeUploadRequest({
+ url,
+ path,
+ token,
+ attaches,
+ fields: attributes,
+ statusCodeExpected: 200
+ })
+}
+
+function getMyVideoImports (url: string, token: string, sort?: string) {
+ const path = '/api/v1/users/me/videos/imports'
+
+ const query = {}
+ if (sort) query['sort'] = sort
+
+ return makeGetRequest({
+ url,
+ query,
+ path,
+ token,
+ statusCodeExpected: 200
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ getBadVideoUrl,
+ getYoutubeVideoUrl,
+ importVideo,
+ getMagnetURI,
+ getMyVideoImports
+}
--- /dev/null
+import { makeDeleteRequest, makeGetRequest, makePostBodyRequest, makePutBodyRequest, makeUploadRequest } from '../requests/requests'
+import { VideoPlaylistCreate } from '../../models/videos/playlist/video-playlist-create.model'
+import { omit } from 'lodash'
+import { VideoPlaylistUpdate } from '../../models/videos/playlist/video-playlist-update.model'
+import { VideoPlaylistElementCreate } from '../../models/videos/playlist/video-playlist-element-create.model'
+import { VideoPlaylistElementUpdate } from '../../models/videos/playlist/video-playlist-element-update.model'
+import { videoUUIDToId } from './videos'
+import { join } from 'path'
+import { root } from '..'
+import { readdir } from 'fs-extra'
+import { expect } from 'chai'
+import { VideoPlaylistType } from '../../models/videos/playlist/video-playlist-type.model'
+
+function getVideoPlaylistsList (url: string, start: number, count: number, sort?: string) {
+ const path = '/api/v1/video-playlists'
+
+ const query = {
+ start,
+ count,
+ sort
+ }
+
+ return makeGetRequest({
+ url,
+ path,
+ query,
+ statusCodeExpected: 200
+ })
+}
+
+function getVideoChannelPlaylistsList (url: string, videoChannelName: string, start: number, count: number, sort?: string) {
+ const path = '/api/v1/video-channels/' + videoChannelName + '/video-playlists'
+
+ const query = {
+ start,
+ count,
+ sort
+ }
+
+ return makeGetRequest({
+ url,
+ path,
+ query,
+ statusCodeExpected: 200
+ })
+}
+
+function getAccountPlaylistsList (url: string, accountName: string, start: number, count: number, sort?: string) {
+ const path = '/api/v1/accounts/' + accountName + '/video-playlists'
+
+ const query = {
+ start,
+ count,
+ sort
+ }
+
+ return makeGetRequest({
+ url,
+ path,
+ query,
+ statusCodeExpected: 200
+ })
+}
+
+function getAccountPlaylistsListWithToken (
+ url: string,
+ token: string,
+ accountName: string,
+ start: number,
+ count: number,
+ playlistType?: VideoPlaylistType,
+ sort?: string
+) {
+ const path = '/api/v1/accounts/' + accountName + '/video-playlists'
+
+ const query = {
+ start,
+ count,
+ playlistType,
+ sort
+ }
+
+ return makeGetRequest({
+ url,
+ token,
+ path,
+ query,
+ statusCodeExpected: 200
+ })
+}
+
+function getVideoPlaylist (url: string, playlistId: number | string, statusCodeExpected = 200) {
+ const path = '/api/v1/video-playlists/' + playlistId
+
+ return makeGetRequest({
+ url,
+ path,
+ statusCodeExpected
+ })
+}
+
+function getVideoPlaylistWithToken (url: string, token: string, playlistId: number | string, statusCodeExpected = 200) {
+ const path = '/api/v1/video-playlists/' + playlistId
+
+ return makeGetRequest({
+ url,
+ token,
+ path,
+ statusCodeExpected
+ })
+}
+
+function deleteVideoPlaylist (url: string, token: string, playlistId: number | string, statusCodeExpected = 204) {
+ const path = '/api/v1/video-playlists/' + playlistId
+
+ return makeDeleteRequest({
+ url,
+ path,
+ token,
+ statusCodeExpected
+ })
+}
+
+function createVideoPlaylist (options: {
+ url: string,
+ token: string,
+ playlistAttrs: VideoPlaylistCreate,
+ expectedStatus?: number
+}) {
+ const path = '/api/v1/video-playlists'
+
+ const fields = omit(options.playlistAttrs, 'thumbnailfile')
+
+ const attaches = options.playlistAttrs.thumbnailfile
+ ? { thumbnailfile: options.playlistAttrs.thumbnailfile }
+ : {}
+
+ return makeUploadRequest({
+ method: 'POST',
+ url: options.url,
+ path,
+ token: options.token,
+ fields,
+ attaches,
+ statusCodeExpected: options.expectedStatus || 200
+ })
+}
+
+function updateVideoPlaylist (options: {
+ url: string,
+ token: string,
+ playlistAttrs: VideoPlaylistUpdate,
+ playlistId: number | string,
+ expectedStatus?: number
+}) {
+ const path = '/api/v1/video-playlists/' + options.playlistId
+
+ const fields = omit(options.playlistAttrs, 'thumbnailfile')
+
+ const attaches = options.playlistAttrs.thumbnailfile
+ ? { thumbnailfile: options.playlistAttrs.thumbnailfile }
+ : {}
+
+ return makeUploadRequest({
+ method: 'PUT',
+ url: options.url,
+ path,
+ token: options.token,
+ fields,
+ attaches,
+ statusCodeExpected: options.expectedStatus || 204
+ })
+}
+
+async function addVideoInPlaylist (options: {
+ url: string,
+ token: string,
+ playlistId: number | string,
+ elementAttrs: VideoPlaylistElementCreate | { videoId: string }
+ expectedStatus?: number
+}) {
+ options.elementAttrs.videoId = await videoUUIDToId(options.url, options.elementAttrs.videoId)
+
+ const path = '/api/v1/video-playlists/' + options.playlistId + '/videos'
+
+ return makePostBodyRequest({
+ url: options.url,
+ path,
+ token: options.token,
+ fields: options.elementAttrs,
+ statusCodeExpected: options.expectedStatus || 200
+ })
+}
+
+function updateVideoPlaylistElement (options: {
+ url: string,
+ token: string,
+ playlistId: number | string,
+ videoId: number | string,
+ elementAttrs: VideoPlaylistElementUpdate,
+ expectedStatus?: number
+}) {
+ const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.videoId
+
+ return makePutBodyRequest({
+ url: options.url,
+ path,
+ token: options.token,
+ fields: options.elementAttrs,
+ statusCodeExpected: options.expectedStatus || 204
+ })
+}
+
+function removeVideoFromPlaylist (options: {
+ url: string,
+ token: string,
+ playlistId: number | string,
+ videoId: number | string,
+ expectedStatus?: number
+}) {
+ const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.videoId
+
+ return makeDeleteRequest({
+ url: options.url,
+ path,
+ token: options.token,
+ statusCodeExpected: options.expectedStatus || 204
+ })
+}
+
+function reorderVideosPlaylist (options: {
+ url: string,
+ token: string,
+ playlistId: number | string,
+ elementAttrs: {
+ startPosition: number,
+ insertAfterPosition: number,
+ reorderLength?: number
+ },
+ expectedStatus?: number
+}) {
+ const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/reorder'
+
+ return makePostBodyRequest({
+ url: options.url,
+ path,
+ token: options.token,
+ fields: options.elementAttrs,
+ statusCodeExpected: options.expectedStatus || 204
+ })
+}
+
+async function checkPlaylistFilesWereRemoved (
+ playlistUUID: string,
+ serverNumber: number,
+ directories = [ 'thumbnails' ]
+) {
+ const testDirectory = 'test' + serverNumber
+
+ for (const directory of directories) {
+ const directoryPath = join(root(), testDirectory, directory)
+
+ const files = await readdir(directoryPath)
+ for (const file of files) {
+ expect(file).to.not.contain(playlistUUID)
+ }
+ }
+}
+
+function getVideoPlaylistPrivacies (url: string) {
+ const path = '/api/v1/video-playlists/privacies'
+
+ return makeGetRequest({
+ url,
+ path,
+ statusCodeExpected: 200
+ })
+}
+
+function doVideosExistInMyPlaylist (url: string, token: string, videoIds: number[]) {
+ const path = '/api/v1/users/me/video-playlists/videos-exist'
+
+ return makeGetRequest({
+ url,
+ token,
+ path,
+ query: { videoIds },
+ statusCodeExpected: 200
+ })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ getVideoPlaylistPrivacies,
+
+ getVideoPlaylistsList,
+ getVideoChannelPlaylistsList,
+ getAccountPlaylistsList,
+ getAccountPlaylistsListWithToken,
+
+ getVideoPlaylist,
+ getVideoPlaylistWithToken,
+
+ createVideoPlaylist,
+ updateVideoPlaylist,
+ deleteVideoPlaylist,
+
+ addVideoInPlaylist,
+ updateVideoPlaylistElement,
+ removeVideoFromPlaylist,
+
+ reorderVideosPlaylist,
+
+ checkPlaylistFilesWereRemoved,
+
+ doVideosExistInMyPlaylist
+}
--- /dev/null
+import { makeRawRequest } from '../requests/requests'
+import { sha256 } from '../../../server/helpers/core-utils'
+import { VideoStreamingPlaylist } from '../../models/videos/video-streaming-playlist.model'
+import { expect } from 'chai'
+
+function getPlaylist (url: string, statusCodeExpected = 200) {
+ return makeRawRequest(url, statusCodeExpected)
+}
+
+function getSegment (url: string, statusCodeExpected = 200, range?: string) {
+ return makeRawRequest(url, statusCodeExpected, range)
+}
+
+function getSegmentSha256 (url: string, statusCodeExpected = 200) {
+ return makeRawRequest(url, statusCodeExpected)
+}
+
+async function checkSegmentHash (
+ baseUrlPlaylist: string,
+ baseUrlSegment: string,
+ videoUUID: string,
+ resolution: number,
+ hlsPlaylist: VideoStreamingPlaylist
+) {
+ const res = await getPlaylist(`${baseUrlPlaylist}/${videoUUID}/${resolution}.m3u8`)
+ const playlist = res.text
+
+ const videoName = `${videoUUID}-${resolution}-fragmented.mp4`
+
+ const matches = /#EXT-X-BYTERANGE:(\d+)@(\d+)/.exec(playlist)
+
+ const length = parseInt(matches[1], 10)
+ const offset = parseInt(matches[2], 10)
+ const range = `${offset}-${offset + length - 1}`
+
+ const res2 = await getSegment(`${baseUrlSegment}/${videoUUID}/${videoName}`, 206, `bytes=${range}`)
+
+ const resSha = await getSegmentSha256(hlsPlaylist.segmentsSha256Url)
+
+ const sha256Server = resSha.body[ videoName ][range]
+ expect(sha256(res2.body)).to.equal(sha256Server)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ getPlaylist,
+ getSegment,
+ getSegmentSha256,
+ checkSegmentHash
+}
--- /dev/null
+/* tslint:disable:no-unused-expression */
+
+import { expect } from 'chai'
+import { pathExists, readdir, readFile } from 'fs-extra'
+import * as parseTorrent from 'parse-torrent'
+import { extname, join } from 'path'
+import * as request from 'supertest'
+import {
+ buildAbsoluteFixturePath,
+ getMyUserInformation,
+ immutableAssign,
+ makeGetRequest,
+ makePutBodyRequest,
+ makeUploadRequest,
+ root,
+ ServerInfo,
+ testImage
+} from '../'
+import * as validator from 'validator'
+import { VideoDetails, VideoPrivacy } from '../../models/videos'
+import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, loadLanguages, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants'
+import { dateIsValid, webtorrentAdd } from '../miscs/miscs'
+
+loadLanguages()
+
+type VideoAttributes = {
+ name?: string
+ category?: number
+ licence?: number
+ language?: string
+ nsfw?: boolean
+ commentsEnabled?: boolean
+ downloadEnabled?: boolean
+ waitTranscoding?: boolean
+ description?: string
+ originallyPublishedAt?: string
+ tags?: string[]
+ channelId?: number
+ privacy?: VideoPrivacy
+ fixture?: string
+ thumbnailfile?: string
+ previewfile?: string
+ scheduleUpdate?: {
+ updateAt: string
+ privacy?: VideoPrivacy
+ }
+}
+
+function getVideoCategories (url: string) {
+ const path = '/api/v1/videos/categories'
+
+ return makeGetRequest({
+ url,
+ path,
+ statusCodeExpected: 200
+ })
+}
+
+function getVideoLicences (url: string) {
+ const path = '/api/v1/videos/licences'
+
+ return makeGetRequest({
+ url,
+ path,
+ statusCodeExpected: 200
+ })
+}
+
+function getVideoLanguages (url: string) {
+ const path = '/api/v1/videos/languages'
+
+ return makeGetRequest({
+ url,
+ path,
+ statusCodeExpected: 200
+ })
+}
+
+function getVideoPrivacies (url: string) {
+ const path = '/api/v1/videos/privacies'
+
+ return makeGetRequest({
+ url,
+ path,
+ statusCodeExpected: 200
+ })
+}
+
+function getVideo (url: string, id: number | string, expectedStatus = 200) {
+ const path = '/api/v1/videos/' + id
+
+ return request(url)
+ .get(path)
+ .set('Accept', 'application/json')
+ .expect(expectedStatus)
+}
+
+function viewVideo (url: string, id: number | string, expectedStatus = 204, xForwardedFor?: string) {
+ const path = '/api/v1/videos/' + id + '/views'
+
+ const req = request(url)
+ .post(path)
+ .set('Accept', 'application/json')
+
+ if (xForwardedFor) {
+ req.set('X-Forwarded-For', xForwardedFor)
+ }
+
+ return req.expect(expectedStatus)
+}
+
+function getVideoWithToken (url: string, token: string, id: number | string, expectedStatus = 200) {
+ const path = '/api/v1/videos/' + id
+
+ return request(url)
+ .get(path)
+ .set('Authorization', 'Bearer ' + token)
+ .set('Accept', 'application/json')
+ .expect(expectedStatus)
+}
+
+function getVideoDescription (url: string, descriptionPath: string) {
+ return request(url)
+ .get(descriptionPath)
+ .set('Accept', 'application/json')
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function getVideosList (url: string) {
+ const path = '/api/v1/videos'
+
+ return request(url)
+ .get(path)
+ .query({ sort: 'name' })
+ .set('Accept', 'application/json')
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function getVideosListWithToken (url: string, token: string, query: { nsfw?: boolean } = {}) {
+ const path = '/api/v1/videos'
+
+ return request(url)
+ .get(path)
+ .set('Authorization', 'Bearer ' + token)
+ .query(immutableAssign(query, { sort: 'name' }))
+ .set('Accept', 'application/json')
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function getLocalVideos (url: string) {
+ const path = '/api/v1/videos'
+
+ return request(url)
+ .get(path)
+ .query({ sort: 'name', filter: 'local' })
+ .set('Accept', 'application/json')
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function getMyVideos (url: string, accessToken: string, start: number, count: number, sort?: string) {
+ const path = '/api/v1/users/me/videos'
+
+ const req = request(url)
+ .get(path)
+ .query({ start: start })
+ .query({ count: count })
+
+ if (sort) req.query({ sort })
+
+ return req.set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function getAccountVideos (
+ url: string,
+ accessToken: string,
+ accountName: string,
+ start: number,
+ count: number,
+ sort?: string,
+ query: { nsfw?: boolean } = {}
+) {
+ const path = '/api/v1/accounts/' + accountName + '/videos'
+
+ return makeGetRequest({
+ url,
+ path,
+ query: immutableAssign(query, {
+ start,
+ count,
+ sort
+ }),
+ token: accessToken,
+ statusCodeExpected: 200
+ })
+}
+
+function getVideoChannelVideos (
+ url: string,
+ accessToken: string,
+ videoChannelName: string,
+ start: number,
+ count: number,
+ sort?: string,
+ query: { nsfw?: boolean } = {}
+) {
+ const path = '/api/v1/video-channels/' + videoChannelName + '/videos'
+
+ return makeGetRequest({
+ url,
+ path,
+ query: immutableAssign(query, {
+ start,
+ count,
+ sort
+ }),
+ token: accessToken,
+ statusCodeExpected: 200
+ })
+}
+
+function getPlaylistVideos (
+ url: string,
+ accessToken: string,
+ playlistId: number | string,
+ start: number,
+ count: number,
+ query: { nsfw?: boolean } = {}
+) {
+ const path = '/api/v1/video-playlists/' + playlistId + '/videos'
+
+ return makeGetRequest({
+ url,
+ path,
+ query: immutableAssign(query, {
+ start,
+ count
+ }),
+ token: accessToken,
+ statusCodeExpected: 200
+ })
+}
+
+function getVideosListPagination (url: string, start: number, count: number, sort?: string) {
+ const path = '/api/v1/videos'
+
+ const req = request(url)
+ .get(path)
+ .query({ start: start })
+ .query({ count: count })
+
+ if (sort) req.query({ sort })
+
+ return req.set('Accept', 'application/json')
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function getVideosListSort (url: string, sort: string) {
+ const path = '/api/v1/videos'
+
+ return request(url)
+ .get(path)
+ .query({ sort: sort })
+ .set('Accept', 'application/json')
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function getVideosWithFilters (url: string, query: { tagsAllOf: string[], categoryOneOf: number[] | number }) {
+ const path = '/api/v1/videos'
+
+ return request(url)
+ .get(path)
+ .query(query)
+ .set('Accept', 'application/json')
+ .expect(200)
+ .expect('Content-Type', /json/)
+}
+
+function removeVideo (url: string, token: string, id: number | string, expectedStatus = 204) {
+ const path = '/api/v1/videos'
+
+ return request(url)
+ .delete(path + '/' + id)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + token)
+ .expect(expectedStatus)
+}
+
+async function checkVideoFilesWereRemoved (
+ videoUUID: string,
+ serverNumber: number,
+ directories = [
+ 'redundancy',
+ 'videos',
+ 'thumbnails',
+ 'torrents',
+ 'previews',
+ 'captions',
+ join('playlists', 'hls'),
+ join('redundancy', 'hls')
+ ]
+) {
+ const testDirectory = 'test' + serverNumber
+
+ for (const directory of directories) {
+ const directoryPath = join(root(), testDirectory, directory)
+
+ const directoryExists = await pathExists(directoryPath)
+ if (directoryExists === false) continue
+
+ const files = await readdir(directoryPath)
+ for (const file of files) {
+ expect(file).to.not.contain(videoUUID)
+ }
+ }
+}
+
+async function uploadVideo (url: string, accessToken: string, videoAttributesArg: VideoAttributes, specialStatus = 200) {
+ const path = '/api/v1/videos/upload'
+ let defaultChannelId = '1'
+
+ try {
+ const res = await getMyUserInformation(url, accessToken)
+ defaultChannelId = res.body.videoChannels[0].id
+ } catch (e) { /* empty */ }
+
+ // Override default attributes
+ const attributes = Object.assign({
+ name: 'my super video',
+ category: 5,
+ licence: 4,
+ language: 'zh',
+ channelId: defaultChannelId,
+ nsfw: true,
+ waitTranscoding: false,
+ description: 'my super description',
+ support: 'my super support text',
+ tags: [ 'tag' ],
+ privacy: VideoPrivacy.PUBLIC,
+ commentsEnabled: true,
+ downloadEnabled: true,
+ fixture: 'video_short.webm'
+ }, videoAttributesArg)
+
+ const req = request(url)
+ .post(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .field('name', attributes.name)
+ .field('nsfw', JSON.stringify(attributes.nsfw))
+ .field('commentsEnabled', JSON.stringify(attributes.commentsEnabled))
+ .field('downloadEnabled', JSON.stringify(attributes.downloadEnabled))
+ .field('waitTranscoding', JSON.stringify(attributes.waitTranscoding))
+ .field('privacy', attributes.privacy.toString())
+ .field('channelId', attributes.channelId)
+
+ if (attributes.description !== undefined) {
+ req.field('description', attributes.description)
+ }
+ if (attributes.language !== undefined) {
+ req.field('language', attributes.language.toString())
+ }
+ if (attributes.category !== undefined) {
+ req.field('category', attributes.category.toString())
+ }
+ if (attributes.licence !== undefined) {
+ req.field('licence', attributes.licence.toString())
+ }
+
+ for (let i = 0; i < attributes.tags.length; i++) {
+ req.field('tags[' + i + ']', attributes.tags[i])
+ }
+
+ if (attributes.thumbnailfile !== undefined) {
+ req.attach('thumbnailfile', buildAbsoluteFixturePath(attributes.thumbnailfile))
+ }
+ if (attributes.previewfile !== undefined) {
+ req.attach('previewfile', buildAbsoluteFixturePath(attributes.previewfile))
+ }
+
+ if (attributes.scheduleUpdate) {
+ req.field('scheduleUpdate[updateAt]', attributes.scheduleUpdate.updateAt)
+
+ if (attributes.scheduleUpdate.privacy) {
+ req.field('scheduleUpdate[privacy]', attributes.scheduleUpdate.privacy)
+ }
+ }
+
+ if (attributes.originallyPublishedAt !== undefined) {
+ req.field('originallyPublishedAt', attributes.originallyPublishedAt)
+ }
+
+ return req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture))
+ .expect(specialStatus)
+}
+
+function updateVideo (url: string, accessToken: string, id: number | string, attributes: VideoAttributes, statusCodeExpected = 204) {
+ const path = '/api/v1/videos/' + id
+ const body = {}
+
+ if (attributes.name) body['name'] = attributes.name
+ if (attributes.category) body['category'] = attributes.category
+ if (attributes.licence) body['licence'] = attributes.licence
+ if (attributes.language) body['language'] = attributes.language
+ if (attributes.nsfw !== undefined) body['nsfw'] = JSON.stringify(attributes.nsfw)
+ if (attributes.commentsEnabled !== undefined) body['commentsEnabled'] = JSON.stringify(attributes.commentsEnabled)
+ if (attributes.downloadEnabled !== undefined) body['downloadEnabled'] = JSON.stringify(attributes.downloadEnabled)
+ if (attributes.originallyPublishedAt !== undefined) body['originallyPublishedAt'] = attributes.originallyPublishedAt
+ if (attributes.description) body['description'] = attributes.description
+ if (attributes.tags) body['tags'] = attributes.tags
+ if (attributes.privacy) body['privacy'] = attributes.privacy
+ if (attributes.channelId) body['channelId'] = attributes.channelId
+ if (attributes.scheduleUpdate) body['scheduleUpdate'] = attributes.scheduleUpdate
+
+ // Upload request
+ if (attributes.thumbnailfile || attributes.previewfile) {
+ const attaches: any = {}
+ if (attributes.thumbnailfile) attaches.thumbnailfile = attributes.thumbnailfile
+ if (attributes.previewfile) attaches.previewfile = attributes.previewfile
+
+ return makeUploadRequest({
+ url,
+ method: 'PUT',
+ path,
+ token: accessToken,
+ fields: body,
+ attaches,
+ statusCodeExpected
+ })
+ }
+
+ return makePutBodyRequest({
+ url,
+ path,
+ fields: body,
+ token: accessToken,
+ statusCodeExpected
+ })
+}
+
+function rateVideo (url: string, accessToken: string, id: number, rating: string, specialStatus = 204) {
+ const path = '/api/v1/videos/' + id + '/rate'
+
+ return request(url)
+ .put(path)
+ .set('Accept', 'application/json')
+ .set('Authorization', 'Bearer ' + accessToken)
+ .send({ rating })
+ .expect(specialStatus)
+}
+
+function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) {
+ return new Promise<any>((res, rej) => {
+ const torrentName = videoUUID + '-' + resolution + '.torrent'
+ const torrentPath = join(root(), 'test' + server.serverNumber, 'torrents', torrentName)
+ readFile(torrentPath, (err, data) => {
+ if (err) return rej(err)
+
+ return res(parseTorrent(data))
+ })
+ })
+}
+
+async function completeVideoCheck (
+ url: string,
+ video: any,
+ attributes: {
+ name: string
+ category: number
+ licence: number
+ language: string
+ nsfw: boolean
+ commentsEnabled: boolean
+ downloadEnabled: boolean
+ description: string
+ publishedAt?: string
+ support: string
+ originallyPublishedAt?: string,
+ account: {
+ name: string
+ host: string
+ }
+ isLocal: boolean
+ tags: string[]
+ privacy: number
+ likes?: number
+ dislikes?: number
+ duration: number
+ channel: {
+ displayName: string
+ name: string
+ description
+ isLocal: boolean
+ }
+ fixture: string
+ files: {
+ resolution: number
+ size: number
+ }[],
+ thumbnailfile?: string
+ previewfile?: string
+ }
+) {
+ if (!attributes.likes) attributes.likes = 0
+ if (!attributes.dislikes) attributes.dislikes = 0
+
+ expect(video.name).to.equal(attributes.name)
+ expect(video.category.id).to.equal(attributes.category)
+ expect(video.category.label).to.equal(attributes.category !== null ? VIDEO_CATEGORIES[attributes.category] : 'Misc')
+ expect(video.licence.id).to.equal(attributes.licence)
+ expect(video.licence.label).to.equal(attributes.licence !== null ? VIDEO_LICENCES[attributes.licence] : 'Unknown')
+ expect(video.language.id).to.equal(attributes.language)
+ expect(video.language.label).to.equal(attributes.language !== null ? VIDEO_LANGUAGES[attributes.language] : 'Unknown')
+ expect(video.privacy.id).to.deep.equal(attributes.privacy)
+ expect(video.privacy.label).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
+ expect(video.nsfw).to.equal(attributes.nsfw)
+ expect(video.description).to.equal(attributes.description)
+ expect(video.account.id).to.be.a('number')
+ expect(video.account.uuid).to.be.a('string')
+ expect(video.account.host).to.equal(attributes.account.host)
+ expect(video.account.name).to.equal(attributes.account.name)
+ expect(video.channel.displayName).to.equal(attributes.channel.displayName)
+ expect(video.channel.name).to.equal(attributes.channel.name)
+ expect(video.likes).to.equal(attributes.likes)
+ expect(video.dislikes).to.equal(attributes.dislikes)
+ expect(video.isLocal).to.equal(attributes.isLocal)
+ expect(video.duration).to.equal(attributes.duration)
+ expect(dateIsValid(video.createdAt)).to.be.true
+ expect(dateIsValid(video.publishedAt)).to.be.true
+ expect(dateIsValid(video.updatedAt)).to.be.true
+
+ if (attributes.publishedAt) {
+ expect(video.publishedAt).to.equal(attributes.publishedAt)
+ }
+
+ if (attributes.originallyPublishedAt) {
+ expect(video.originallyPublishedAt).to.equal(attributes.originallyPublishedAt)
+ } else {
+ expect(video.originallyPublishedAt).to.be.null
+ }
+
+ const res = await getVideo(url, video.uuid)
+ const videoDetails: VideoDetails = res.body
+
+ expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
+ expect(videoDetails.tags).to.deep.equal(attributes.tags)
+ expect(videoDetails.account.name).to.equal(attributes.account.name)
+ expect(videoDetails.account.host).to.equal(attributes.account.host)
+ expect(video.channel.displayName).to.equal(attributes.channel.displayName)
+ expect(video.channel.name).to.equal(attributes.channel.name)
+ expect(videoDetails.channel.host).to.equal(attributes.account.host)
+ expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
+ expect(dateIsValid(videoDetails.channel.createdAt.toString())).to.be.true
+ expect(dateIsValid(videoDetails.channel.updatedAt.toString())).to.be.true
+ expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
+ expect(videoDetails.downloadEnabled).to.equal(attributes.downloadEnabled)
+
+ for (const attributeFile of attributes.files) {
+ const file = videoDetails.files.find(f => f.resolution.id === attributeFile.resolution)
+ expect(file).not.to.be.undefined
+
+ let extension = extname(attributes.fixture)
+ // Transcoding enabled on server 2, extension will always be .mp4
+ if (attributes.account.host === 'localhost:9002') extension = '.mp4'
+
+ const magnetUri = file.magnetUri
+ expect(file.magnetUri).to.have.lengthOf.above(2)
+ expect(file.torrentUrl).to.equal(`http://${attributes.account.host}/static/torrents/${videoDetails.uuid}-${file.resolution.id}.torrent`)
+ expect(file.fileUrl).to.equal(`http://${attributes.account.host}/static/webseed/${videoDetails.uuid}-${file.resolution.id}${extension}`)
+ expect(file.resolution.id).to.equal(attributeFile.resolution)
+ expect(file.resolution.label).to.equal(attributeFile.resolution + 'p')
+
+ const minSize = attributeFile.size - ((10 * attributeFile.size) / 100)
+ const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
+ expect(file.size,
+ 'File size for resolution ' + file.resolution.label + ' outside confidence interval (' + minSize + '> size <' + maxSize + ')')
+ .to.be.above(minSize).and.below(maxSize)
+
+ {
+ await testImage(url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath)
+ }
+
+ if (attributes.previewfile) {
+ await testImage(url, attributes.previewfile, videoDetails.previewPath)
+ }
+
+ const torrent = await webtorrentAdd(magnetUri, true)
+ expect(torrent.files).to.be.an('array')
+ expect(torrent.files.length).to.equal(1)
+ expect(torrent.files[0].path).to.exist.and.to.not.equal('')
+ }
+}
+
+async function videoUUIDToId (url: string, id: number | string) {
+ if (validator.isUUID('' + id) === false) return id
+
+ const res = await getVideo(url, id)
+ return res.body.id
+}
+
+async function uploadVideoAndGetId (options: { server: ServerInfo, videoName: string, nsfw?: boolean, token?: string }) {
+ const videoAttrs: any = { name: options.videoName }
+ if (options.nsfw) videoAttrs.nsfw = options.nsfw
+
+ const res = await uploadVideo(options.server.url, options.token || options.server.accessToken, videoAttrs)
+
+ return { id: res.body.video.id, uuid: res.body.video.uuid }
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+ getVideoDescription,
+ getVideoCategories,
+ getVideoLicences,
+ videoUUIDToId,
+ getVideoPrivacies,
+ getVideoLanguages,
+ getMyVideos,
+ getAccountVideos,
+ getVideoChannelVideos,
+ getVideo,
+ getVideoWithToken,
+ getVideosList,
+ getVideosListPagination,
+ getVideosListSort,
+ removeVideo,
+ getVideosListWithToken,
+ uploadVideo,
+ getVideosWithFilters,
+ updateVideo,
+ rateVideo,
+ viewVideo,
+ parseTorrentVideo,
+ getLocalVideos,
+ completeVideoCheck,
+ checkVideoFilesWereRemoved,
+ getPlaylistVideos,
+ uploadVideoAndGetId
+}
+++ /dev/null
-import { exec } from 'child_process'
-
-import { ServerInfo } from '../server/servers'
-
-function getEnvCli (server?: ServerInfo) {
- return `NODE_ENV=test NODE_APP_INSTANCE=${server.serverNumber}`
-}
-
-async function execCLI (command: string) {
- return new Promise<string>((res, rej) => {
- exec(command, (err, stdout, stderr) => {
- if (err) return rej(err)
-
- return res(stdout)
- })
- })
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- execCLI,
- getEnvCli
-}
+++ /dev/null
-import * as request from 'supertest'
-
-type FeedType = 'videos' | 'video-comments'
-
-function getXMLfeed (url: string, feed: FeedType, format?: string) {
- const path = '/feeds/' + feed + '.xml'
-
- return request(url)
- .get(path)
- .query((format) ? { format: format } : {})
- .set('Accept', 'application/xml')
- .expect(200)
- .expect('Content-Type', /xml/)
-}
-
-function getJSONfeed (url: string, feed: FeedType, query: any = {}) {
- const path = '/feeds/' + feed + '.json'
-
- return request(url)
- .get(path)
- .query(query)
- .set('Accept', 'application/json')
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- getXMLfeed,
- getJSONfeed
-}
+++ /dev/null
-export * from './server/activitypub'
-export * from './cli/cli'
-export * from './server/clients'
-export * from './server/config'
-export * from './server/jobs'
-export * from './users/login'
-export * from './miscs/miscs'
-export * from './miscs/stubs'
-export * from './miscs/sql'
-export * from './server/follows'
-export * from './requests/requests'
-export * from './requests/check-api-params'
-export * from './server/servers'
-export * from './videos/services'
-export * from './videos/video-playlists'
-export * from './users/users'
-export * from './users/accounts'
-export * from './videos/video-abuses'
-export * from './videos/video-blacklist'
-export * from './videos/video-channels'
-export * from './videos/video-comments'
-export * from './videos/video-streaming-playlists'
-export * from './videos/videos'
-export * from './videos/video-change-ownership'
-export * from './feeds/feeds'
-export * from './search/videos'
+++ /dev/null
-import { makeGetRequest } from '../requests/requests'
-import { LogLevel } from '../../models/server/log-level.type'
-
-function getLogs (url: string, accessToken: string, startDate: Date, endDate?: Date, level?: LogLevel) {
- const path = '/api/v1/server/logs'
-
- return makeGetRequest({
- url,
- path,
- token: accessToken,
- query: { startDate, endDate, level },
- statusCodeExpected: 200
- })
-}
-
-export {
- getLogs
-}
+++ /dev/null
-const MailDev = require('maildev')
-
-// must run maildev as forked ChildProcess
-// failed instantiation stops main process with exit code 0
-process.on('message', (msg) => {
- if (msg.start) {
- const maildev = new MailDev({
- ip: '127.0.0.1',
- smtp: 1025,
- disableWeb: true,
- silent: true
- })
-
- maildev.on('new', email => {
- process.send({ email })
- })
-
- maildev.listen(err => {
- if (err) {
- // cannot send as Error object
- return process.send({ err: err.message })
- }
-
- return process.send({ err: null })
- })
- }
-})
+++ /dev/null
-import { fork, ChildProcess } from 'child_process'
-
-class MockSmtpServer {
-
- private static instance: MockSmtpServer
- private started = false
- private emailChildProcess: ChildProcess
- private emails: object[]
-
- private constructor () {
- this.emailChildProcess = fork(`${__dirname}/email-child-process`, [])
-
- this.emailChildProcess.on('message', (msg) => {
- if (msg.email) {
- return this.emails.push(msg.email)
- }
- })
-
- process.on('exit', () => this.kill())
- }
-
- collectEmails (emailsCollection: object[]) {
- return new Promise((res, rej) => {
- if (this.started) {
- this.emails = emailsCollection
- return res()
- }
-
- // ensure maildev isn't started until
- // unexpected exit can be reported to test runner
- this.emailChildProcess.send({ start: true })
- this.emailChildProcess.on('exit', () => {
- return rej(new Error('maildev exited unexpectedly, confirm port not in use'))
- })
- this.emailChildProcess.on('message', (msg) => {
- if (msg.err) {
- return rej(new Error(msg.err))
- }
- this.started = true
- this.emails = emailsCollection
- return res()
- })
- })
- }
-
- kill () {
- if (!this.emailChildProcess) return
-
- process.kill(this.emailChildProcess.pid)
-
- this.emailChildProcess = null
- MockSmtpServer.instance = null
- }
-
- static get Instance () {
- return this.instance || (this.instance = new this())
- }
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- MockSmtpServer
-}
+++ /dev/null
-/* tslint:disable:no-unused-expression */
-
-import * as chai from 'chai'
-import { isAbsolute, join } from 'path'
-import * as request from 'supertest'
-import * as WebTorrent from 'webtorrent'
-import { pathExists, readFile } from 'fs-extra'
-import * as ffmpeg from 'fluent-ffmpeg'
-
-const expect = chai.expect
-let webtorrent = new WebTorrent()
-
-function immutableAssign <T, U> (target: T, source: U) {
- return Object.assign<{}, T, U>({}, target, source)
-}
-
- // Default interval -> 5 minutes
-function dateIsValid (dateString: string, interval = 300000) {
- const dateToCheck = new Date(dateString)
- const now = new Date()
-
- return Math.abs(now.getTime() - dateToCheck.getTime()) <= interval
-}
-
-function wait (milliseconds: number) {
- return new Promise(resolve => setTimeout(resolve, milliseconds))
-}
-
-function webtorrentAdd (torrent: string, refreshWebTorrent = false) {
- if (refreshWebTorrent === true) webtorrent = new WebTorrent()
-
- return new Promise<WebTorrent.Torrent>(res => webtorrent.add(torrent, res))
-}
-
-function root () {
- // We are in /shared/utils/miscs
- return join(__dirname, '..', '..', '..')
-}
-
-async function testImage (url: string, imageName: string, imagePath: string, extension = '.jpg') {
- const res = await request(url)
- .get(imagePath)
- .expect(200)
-
- const body = res.body
-
- const data = await readFile(join(root(), 'server', 'tests', 'fixtures', imageName + extension))
- const minLength = body.length - ((20 * body.length) / 100)
- const maxLength = body.length + ((20 * body.length) / 100)
-
- expect(data.length).to.be.above(minLength)
- expect(data.length).to.be.below(maxLength)
-}
-
-function buildAbsoluteFixturePath (path: string, customTravisPath = false) {
- if (isAbsolute(path)) {
- return path
- }
-
- if (customTravisPath && process.env.TRAVIS) return join(process.env.HOME, 'fixtures', path)
-
- return join(root(), 'server', 'tests', 'fixtures', path)
-}
-
-async function generateHighBitrateVideo () {
- const tempFixturePath = buildAbsoluteFixturePath('video_high_bitrate_1080p.mp4', true)
-
- const exists = await pathExists(tempFixturePath)
- if (!exists) {
-
- // Generate a random, high bitrate video on the fly, so we don't have to include
- // a large file in the repo. The video needs to have a certain minimum length so
- // that FFmpeg properly applies bitrate limits.
- // https://stackoverflow.com/a/15795112
- return new Promise<string>(async (res, rej) => {
- ffmpeg()
- .outputOptions([ '-f rawvideo', '-video_size 1920x1080', '-i /dev/urandom' ])
- .outputOptions([ '-ac 2', '-f s16le', '-i /dev/urandom', '-t 10' ])
- .outputOptions([ '-maxrate 10M', '-bufsize 10M' ])
- .output(tempFixturePath)
- .on('error', rej)
- .on('end', () => res(tempFixturePath))
- .run()
- })
- }
-
- return tempFixturePath
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- dateIsValid,
- wait,
- webtorrentAdd,
- immutableAssign,
- testImage,
- buildAbsoluteFixturePath,
- root,
- generateHighBitrateVideo
-}
+++ /dev/null
-import * as Sequelize from 'sequelize'
-
-let sequelizes: { [ id: number ]: Sequelize.Sequelize } = {}
-
-function getSequelize (serverNumber: number) {
- if (sequelizes[serverNumber]) return sequelizes[serverNumber]
-
- const dbname = 'peertube_test' + serverNumber
- const username = 'peertube'
- const password = 'peertube'
- const host = 'localhost'
- const port = 5432
-
- const seq = new Sequelize(dbname, username, password, {
- dialect: 'postgres',
- host,
- port,
- operatorsAliases: false,
- logging: false
- })
-
- sequelizes[serverNumber] = seq
-
- return seq
-}
-
-function setActorField (serverNumber: number, to: string, field: string, value: string) {
- const seq = getSequelize(serverNumber)
-
- const options = { type: Sequelize.QueryTypes.UPDATE }
-
- return seq.query(`UPDATE actor SET "${field}" = '${value}' WHERE url = '${to}'`, options)
-}
-
-function setVideoField (serverNumber: number, uuid: string, field: string, value: string) {
- const seq = getSequelize(serverNumber)
-
- const options = { type: Sequelize.QueryTypes.UPDATE }
-
- return seq.query(`UPDATE video SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options)
-}
-
-function setPlaylistField (serverNumber: number, uuid: string, field: string, value: string) {
- const seq = getSequelize(serverNumber)
-
- const options = { type: Sequelize.QueryTypes.UPDATE }
-
- return seq.query(`UPDATE "videoPlaylist" SET "${field}" = '${value}' WHERE uuid = '${uuid}'`, options)
-}
-
-async function countVideoViewsOf (serverNumber: number, uuid: string) {
- const seq = getSequelize(serverNumber)
-
- // tslint:disable
- const query = `SELECT SUM("videoView"."views") AS "total" FROM "videoView" INNER JOIN "video" ON "video"."id" = "videoView"."videoId" WHERE "video"."uuid" = '${uuid}'`
-
- const options = { type: Sequelize.QueryTypes.SELECT }
- const [ { total } ] = await seq.query(query, options)
-
- if (!total) return 0
-
- return parseInt(total, 10)
-}
-
-async function closeAllSequelize (servers: any[]) {
- for (let i = 1; i <= servers.length; i++) {
- if (sequelizes[ i ]) {
- await sequelizes[ i ].close()
- delete sequelizes[ i ]
- }
- }
-}
-
-export {
- setVideoField,
- setPlaylistField,
- setActorField,
- countVideoViewsOf,
- closeAllSequelize
-}
+++ /dev/null
-function buildRequestStub (): any {
- return { }
-}
-
-function buildResponseStub (): any {
- return {
- locals: {}
- }
-}
-
-export {
- buildResponseStub,
- buildRequestStub
-}
+++ /dev/null
-import { makeGetRequest } from '../requests/requests'
-
-function getVideosOverview (url: string, useCache = false) {
- const path = '/api/v1/overviews/videos'
-
- const query = {
- t: useCache ? undefined : new Date().getTime()
- }
-
- return makeGetRequest({
- url,
- path,
- query,
- statusCodeExpected: 200
- })
-}
-
-export { getVideosOverview }
+++ /dev/null
-import { doRequest } from '../../../server/helpers/requests'
-import { HTTP_SIGNATURE } from '../../../server/initializers/constants'
-import { buildGlobalHeaders } from '../../../server/lib/job-queue/handlers/utils/activitypub-http-utils'
-import { activityPubContextify } from '../../../server/helpers/activitypub'
-
-function makePOSTAPRequest (url: string, body: any, httpSignature: any, headers: any) {
- const options = {
- method: 'POST',
- uri: url,
- json: body,
- httpSignature,
- headers
- }
-
- return doRequest(options)
-}
-
-async function makeFollowRequest (to: { url: string }, by: { url: string, privateKey }) {
- const follow = {
- type: 'Follow',
- id: by.url + '/toto',
- actor: by.url,
- object: to.url
- }
-
- const body = activityPubContextify(follow)
-
- const httpSignature = {
- algorithm: HTTP_SIGNATURE.ALGORITHM,
- authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME,
- keyId: by.url,
- key: by.privateKey,
- headers: HTTP_SIGNATURE.HEADERS_TO_SIGN
- }
- const headers = buildGlobalHeaders(body)
-
- return makePOSTAPRequest(to.url, body, httpSignature, headers)
-}
-
-export {
- makePOSTAPRequest,
- makeFollowRequest
-}
+++ /dev/null
-import { makeGetRequest } from './requests'
-import { immutableAssign } from '../miscs/miscs'
-
-function checkBadStartPagination (url: string, path: string, token?: string, query = {}) {
- return makeGetRequest({
- url,
- path,
- token,
- query: immutableAssign(query, { start: 'hello' }),
- statusCodeExpected: 400
- })
-}
-
-function checkBadCountPagination (url: string, path: string, token?: string, query = {}) {
- return makeGetRequest({
- url,
- path,
- token,
- query: immutableAssign(query, { count: 'hello' }),
- statusCodeExpected: 400
- })
-}
-
-function checkBadSortPagination (url: string, path: string, token?: string, query = {}) {
- return makeGetRequest({
- url,
- path,
- token,
- query: immutableAssign(query, { sort: 'hello' }),
- statusCodeExpected: 400
- })
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- checkBadStartPagination,
- checkBadCountPagination,
- checkBadSortPagination
-}
+++ /dev/null
-import * as request from 'supertest'
-import { buildAbsoluteFixturePath, root } from '../miscs/miscs'
-import { isAbsolute, join } from 'path'
-import { parse } from 'url'
-
-function get4KFileUrl () {
- return 'https://download.cpy.re/peertube/4k_file.txt'
-}
-
-function makeRawRequest (url: string, statusCodeExpected?: number, range?: string) {
- const { host, protocol, pathname } = parse(url)
-
- return makeGetRequest({ url: `${protocol}//${host}`, path: pathname, statusCodeExpected, range })
-}
-
-function makeGetRequest (options: {
- url: string,
- path?: string,
- query?: any,
- token?: string,
- statusCodeExpected?: number,
- contentType?: string,
- range?: string
-}) {
- if (!options.statusCodeExpected) options.statusCodeExpected = 400
- if (options.contentType === undefined) options.contentType = 'application/json'
-
- const req = request(options.url).get(options.path)
-
- if (options.contentType) req.set('Accept', options.contentType)
- if (options.token) req.set('Authorization', 'Bearer ' + options.token)
- if (options.query) req.query(options.query)
- if (options.range) req.set('Range', options.range)
-
- return req.expect(options.statusCodeExpected)
-}
-
-function makeDeleteRequest (options: {
- url: string,
- path: string,
- token?: string,
- statusCodeExpected?: number
-}) {
- if (!options.statusCodeExpected) options.statusCodeExpected = 400
-
- const req = request(options.url)
- .delete(options.path)
- .set('Accept', 'application/json')
-
- if (options.token) req.set('Authorization', 'Bearer ' + options.token)
-
- return req.expect(options.statusCodeExpected)
-}
-
-function makeUploadRequest (options: {
- url: string,
- method?: 'POST' | 'PUT',
- path: string,
- token?: string,
- fields: { [ fieldName: string ]: any },
- attaches: { [ attachName: string ]: any | any[] },
- statusCodeExpected?: number
-}) {
- if (!options.statusCodeExpected) options.statusCodeExpected = 400
-
- let req: request.Test
- if (options.method === 'PUT') {
- req = request(options.url).put(options.path)
- } else {
- req = request(options.url).post(options.path)
- }
-
- req.set('Accept', 'application/json')
-
- if (options.token) req.set('Authorization', 'Bearer ' + options.token)
-
- Object.keys(options.fields).forEach(field => {
- const value = options.fields[field]
-
- if (value === undefined) return
-
- if (Array.isArray(value)) {
- for (let i = 0; i < value.length; i++) {
- req.field(field + '[' + i + ']', value[i])
- }
- } else {
- req.field(field, value)
- }
- })
-
- Object.keys(options.attaches).forEach(attach => {
- const value = options.attaches[attach]
- if (Array.isArray(value)) {
- req.attach(attach, buildAbsoluteFixturePath(value[0]), value[1])
- } else {
- req.attach(attach, buildAbsoluteFixturePath(value))
- }
- })
-
- return req.expect(options.statusCodeExpected)
-}
-
-function makePostBodyRequest (options: {
- url: string,
- path: string,
- token?: string,
- fields?: { [ fieldName: string ]: any },
- statusCodeExpected?: number
-}) {
- if (!options.fields) options.fields = {}
- if (!options.statusCodeExpected) options.statusCodeExpected = 400
-
- const req = request(options.url)
- .post(options.path)
- .set('Accept', 'application/json')
-
- if (options.token) req.set('Authorization', 'Bearer ' + options.token)
-
- return req.send(options.fields)
- .expect(options.statusCodeExpected)
-}
-
-function makePutBodyRequest (options: {
- url: string,
- path: string,
- token?: string,
- fields: { [ fieldName: string ]: any },
- statusCodeExpected?: number
-}) {
- if (!options.statusCodeExpected) options.statusCodeExpected = 400
-
- const req = request(options.url)
- .put(options.path)
- .set('Accept', 'application/json')
-
- if (options.token) req.set('Authorization', 'Bearer ' + options.token)
-
- return req.send(options.fields)
- .expect(options.statusCodeExpected)
-}
-
-function makeHTMLRequest (url: string, path: string) {
- return request(url)
- .get(path)
- .set('Accept', 'text/html')
- .expect(200)
-}
-
-function updateAvatarRequest (options: {
- url: string,
- path: string,
- accessToken: string,
- fixture: string
-}) {
- let filePath = ''
- if (isAbsolute(options.fixture)) {
- filePath = options.fixture
- } else {
- filePath = join(root(), 'server', 'tests', 'fixtures', options.fixture)
- }
-
- return makeUploadRequest({
- url: options.url,
- path: options.path,
- token: options.accessToken,
- fields: {},
- attaches: { avatarfile: filePath },
- statusCodeExpected: 200
- })
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- get4KFileUrl,
- makeHTMLRequest,
- makeGetRequest,
- makeUploadRequest,
- makePostBodyRequest,
- makePutBodyRequest,
- makeDeleteRequest,
- makeRawRequest,
- updateAvatarRequest
-}
+++ /dev/null
-import { makeGetRequest } from '../requests/requests'
-
-function searchVideoChannel (url: string, search: string, token?: string, statusCodeExpected = 200) {
- const path = '/api/v1/search/video-channels'
-
- return makeGetRequest({
- url,
- path,
- query: {
- sort: '-createdAt',
- search
- },
- token,
- statusCodeExpected
- })
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- searchVideoChannel
-}
+++ /dev/null
-/* tslint:disable:no-unused-expression */
-
-import * as request from 'supertest'
-import { VideosSearchQuery } from '../../models/search'
-import { immutableAssign } from '../miscs/miscs'
-
-function searchVideo (url: string, search: string) {
- const path = '/api/v1/search/videos'
- const req = request(url)
- .get(path)
- .query({ sort: '-publishedAt', search })
- .set('Accept', 'application/json')
-
- return req.expect(200)
- .expect('Content-Type', /json/)
-}
-
-function searchVideoWithToken (url: string, search: string, token: string, query: { nsfw?: boolean } = {}) {
- const path = '/api/v1/search/videos'
- const req = request(url)
- .get(path)
- .set('Authorization', 'Bearer ' + token)
- .query(immutableAssign(query, { sort: '-publishedAt', search }))
- .set('Accept', 'application/json')
-
- return req.expect(200)
- .expect('Content-Type', /json/)
-}
-
-function searchVideoWithPagination (url: string, search: string, start: number, count: number, sort?: string) {
- const path = '/api/v1/search/videos'
-
- const req = request(url)
- .get(path)
- .query({ start })
- .query({ search })
- .query({ count })
-
- if (sort) req.query({ sort })
-
- return req.set('Accept', 'application/json')
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function searchVideoWithSort (url: string, search: string, sort: string) {
- const path = '/api/v1/search/videos'
-
- return request(url)
- .get(path)
- .query({ search })
- .query({ sort })
- .set('Accept', 'application/json')
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function advancedVideosSearch (url: string, options: VideosSearchQuery) {
- const path = '/api/v1/search/videos'
-
- return request(url)
- .get(path)
- .query(options)
- .set('Accept', 'application/json')
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- searchVideo,
- advancedVideosSearch,
- searchVideoWithToken,
- searchVideoWithPagination,
- searchVideoWithSort
-}
+++ /dev/null
-import * as request from 'supertest'
-
-function makeActivityPubGetRequest (url: string, path: string, expectedStatus = 200) {
- return request(url)
- .get(path)
- .set('Accept', 'application/activity+json,text/html;q=0.9,\\*/\\*;q=0.8')
- .expect(expectedStatus)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- makeActivityPubGetRequest
-}
+++ /dev/null
-import * as request from 'supertest'
-import * as urlUtil from 'url'
-
-function getClient (url: string) {
- const path = '/api/v1/oauth-clients/local'
-
- return request(url)
- .get(path)
- .set('Host', urlUtil.parse(url).host)
- .set('Accept', 'application/json')
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- getClient
-}
+++ /dev/null
-import { makeDeleteRequest, makeGetRequest, makePutBodyRequest } from '../requests/requests'
-import { CustomConfig } from '../../models/server/custom-config.model'
-
-function getConfig (url: string) {
- const path = '/api/v1/config'
-
- return makeGetRequest({
- url,
- path,
- statusCodeExpected: 200
- })
-}
-
-function getAbout (url: string) {
- const path = '/api/v1/config/about'
-
- return makeGetRequest({
- url,
- path,
- statusCodeExpected: 200
- })
-}
-
-function getCustomConfig (url: string, token: string, statusCodeExpected = 200) {
- const path = '/api/v1/config/custom'
-
- return makeGetRequest({
- url,
- token,
- path,
- statusCodeExpected
- })
-}
-
-function updateCustomConfig (url: string, token: string, newCustomConfig: CustomConfig, statusCodeExpected = 200) {
- const path = '/api/v1/config/custom'
-
- return makePutBodyRequest({
- url,
- token,
- path,
- fields: newCustomConfig,
- statusCodeExpected
- })
-}
-
-function updateCustomSubConfig (url: string, token: string, newConfig: any) {
- const updateParams: CustomConfig = {
- instance: {
- name: 'PeerTube updated',
- shortDescription: 'my short description',
- description: 'my super description',
- terms: 'my super terms',
- defaultClientRoute: '/videos/recently-added',
- isNSFW: true,
- defaultNSFWPolicy: 'blur',
- customizations: {
- javascript: 'alert("coucou")',
- css: 'body { background-color: red; }'
- }
- },
- services: {
- twitter: {
- username: '@MySuperUsername',
- whitelisted: true
- }
- },
- cache: {
- previews: {
- size: 2
- },
- captions: {
- size: 3
- }
- },
- signup: {
- enabled: false,
- limit: 5,
- requiresEmailVerification: false
- },
- admin: {
- email: 'superadmin1@example.com'
- },
- contactForm: {
- enabled: true
- },
- user: {
- videoQuota: 5242881,
- videoQuotaDaily: 318742
- },
- transcoding: {
- enabled: true,
- allowAdditionalExtensions: true,
- threads: 1,
- resolutions: {
- '240p': false,
- '360p': true,
- '480p': true,
- '720p': false,
- '1080p': false
- },
- hls: {
- enabled: false
- }
- },
- import: {
- videos: {
- http: {
- enabled: false
- },
- torrent: {
- enabled: false
- }
- }
- },
- autoBlacklist: {
- videos: {
- ofUsers: {
- enabled: false
- }
- }
- },
- followers: {
- instance: {
- enabled: true,
- manualApproval: false
- }
- }
- }
-
- Object.assign(updateParams, newConfig)
-
- return updateCustomConfig(url, token, updateParams)
-}
-
-function deleteCustomConfig (url: string, token: string, statusCodeExpected = 200) {
- const path = '/api/v1/config/custom'
-
- return makeDeleteRequest({
- url,
- token,
- path,
- statusCodeExpected
- })
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- getConfig,
- getCustomConfig,
- updateCustomConfig,
- getAbout,
- deleteCustomConfig,
- updateCustomSubConfig
-}
+++ /dev/null
-import * as request from 'supertest'
-import { ContactForm } from '../../models/server'
-
-function sendContactForm (options: {
- url: string,
- fromEmail: string,
- fromName: string,
- body: string,
- expectedStatus?: number
-}) {
- const path = '/api/v1/server/contact'
-
- const body: ContactForm = {
- fromEmail: options.fromEmail,
- fromName: options.fromName,
- body: options.body
- }
- return request(options.url)
- .post(path)
- .send(body)
- .expect(options.expectedStatus || 204)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- sendContactForm
-}
+++ /dev/null
-import * as request from 'supertest'
-import { ServerInfo } from './servers'
-import { waitJobs } from './jobs'
-import { makeGetRequest, makePostBodyRequest } from '..'
-
-function getFollowersListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string) {
- const path = '/api/v1/server/followers'
-
- return request(url)
- .get(path)
- .query({ start })
- .query({ count })
- .query({ sort })
- .query({ search })
- .set('Accept', 'application/json')
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function acceptFollower (url: string, token: string, follower: string, statusCodeExpected = 204) {
- const path = '/api/v1/server/followers/' + follower + '/accept'
-
- return makePostBodyRequest({
- url,
- token,
- path,
- statusCodeExpected
- })
-}
-
-function rejectFollower (url: string, token: string, follower: string, statusCodeExpected = 204) {
- const path = '/api/v1/server/followers/' + follower + '/reject'
-
- return makePostBodyRequest({
- url,
- token,
- path,
- statusCodeExpected
- })
-}
-
-function getFollowingListPaginationAndSort (url: string, start: number, count: number, sort: string, search?: string) {
- const path = '/api/v1/server/following'
-
- return request(url)
- .get(path)
- .query({ start })
- .query({ count })
- .query({ sort })
- .query({ search })
- .set('Accept', 'application/json')
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function follow (follower: string, following: string[], accessToken: string, expectedStatus = 204) {
- const path = '/api/v1/server/following'
-
- const followingHosts = following.map(f => f.replace(/^http:\/\//, ''))
- return request(follower)
- .post(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .send({ 'hosts': followingHosts })
- .expect(expectedStatus)
-}
-
-async function unfollow (url: string, accessToken: string, target: ServerInfo, expectedStatus = 204) {
- const path = '/api/v1/server/following/' + target.host
-
- return request(url)
- .delete(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .expect(expectedStatus)
-}
-
-function removeFollower (url: string, accessToken: string, follower: ServerInfo, expectedStatus = 204) {
- const path = '/api/v1/server/followers/peertube@' + follower.host
-
- return request(url)
- .delete(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .expect(expectedStatus)
-}
-
-async function doubleFollow (server1: ServerInfo, server2: ServerInfo) {
- await Promise.all([
- follow(server1.url, [ server2.url ], server1.accessToken),
- follow(server2.url, [ server1.url ], server2.accessToken)
- ])
-
- // Wait request propagation
- await waitJobs([ server1, server2 ])
-
- return true
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- getFollowersListPaginationAndSort,
- getFollowingListPaginationAndSort,
- unfollow,
- removeFollower,
- follow,
- doubleFollow,
- acceptFollower,
- rejectFollower
-}
+++ /dev/null
-import * as request from 'supertest'
-import { Job, JobState } from '../../models'
-import { wait } from '../miscs/miscs'
-import { ServerInfo } from './servers'
-
-function getJobsList (url: string, accessToken: string, state: JobState) {
- const path = '/api/v1/jobs/' + state
-
- return request(url)
- .get(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function getJobsListPaginationAndSort (url: string, accessToken: string, state: JobState, start: number, count: number, sort: string) {
- const path = '/api/v1/jobs/' + state
-
- return request(url)
- .get(path)
- .query({ start })
- .query({ count })
- .query({ sort })
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-async function waitJobs (serversArg: ServerInfo[] | ServerInfo) {
- const pendingJobWait = process.env.NODE_PENDING_JOB_WAIT ? parseInt(process.env.NODE_PENDING_JOB_WAIT, 10) : 2000
- let servers: ServerInfo[]
-
- if (Array.isArray(serversArg) === false) servers = [ serversArg as ServerInfo ]
- else servers = serversArg as ServerInfo[]
-
- const states: JobState[] = [ 'waiting', 'active', 'delayed' ]
- let pendingRequests = false
-
- function tasksBuilder () {
- const tasks: Promise<any>[] = []
- pendingRequests = false
-
- // Check if each server has pending request
- for (const server of servers) {
- for (const state of states) {
- const p = getJobsListPaginationAndSort(server.url, server.accessToken, state, 0, 10, '-createdAt')
- .then(res => res.body.data)
- .then((jobs: Job[]) => jobs.filter(j => j.type !== 'videos-views'))
- .then(jobs => {
- if (jobs.length !== 0) pendingRequests = true
- })
- tasks.push(p)
- }
- }
-
- return tasks
- }
-
- do {
- await Promise.all(tasksBuilder())
-
- // Retry, in case of new jobs were created
- if (pendingRequests === false) {
- await wait(pendingJobWait)
- await Promise.all(tasksBuilder())
- }
-
- if (pendingRequests) {
- await wait(1000)
- }
- } while (pendingRequests)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- getJobsList,
- waitJobs,
- getJobsListPaginationAndSort
-}
+++ /dev/null
-import { makePutBodyRequest } from '../requests/requests'
-
-async function updateRedundancy (url: string, accessToken: string, host: string, redundancyAllowed: boolean, expectedStatus = 204) {
- const path = '/api/v1/server/redundancy/' + host
-
- return makePutBodyRequest({
- url,
- path,
- token: accessToken,
- fields: { redundancyAllowed },
- statusCodeExpected: expectedStatus
- })
-}
-
-export {
- updateRedundancy
-}
+++ /dev/null
-/* tslint:disable:no-unused-expression */
-
-import { ChildProcess, exec, fork } from 'child_process'
-import { join } from 'path'
-import { root, wait } from '../miscs/miscs'
-import { readdir, readFile } from 'fs-extra'
-import { existsSync } from 'fs'
-import { expect } from 'chai'
-import { VideoChannel } from '../../models/videos'
-
-interface ServerInfo {
- app: ChildProcess,
- url: string
- host: string
- serverNumber: number
-
- client: {
- id: string,
- secret: string
- }
-
- user: {
- username: string,
- password: string,
- email?: string
- }
-
- accessToken?: string
- videoChannel?: VideoChannel
-
- video?: {
- id: number
- uuid: string
- name: string
- account: {
- name: string
- }
- }
-
- remoteVideo?: {
- id: number
- uuid: string
- }
-
- videos?: { id: number, uuid: string }[]
-}
-
-function flushAndRunMultipleServers (totalServers: number, configOverride?: Object) {
- let apps = []
- let i = 0
-
- return new Promise<ServerInfo[]>(res => {
- function anotherServerDone (serverNumber, app) {
- apps[serverNumber - 1] = app
- i++
- if (i === totalServers) {
- return res(apps)
- }
- }
-
- flushTests()
- .then(() => {
- for (let j = 1; j <= totalServers; j++) {
- runServer(j, configOverride).then(app => anotherServerDone(j, app))
- }
- })
- })
-}
-
-function flushTests () {
- return new Promise<void>((res, rej) => {
- return exec('npm run clean:server:test', err => {
- if (err) return rej(err)
-
- return res()
- })
- })
-}
-
-function runServer (serverNumber: number, configOverride?: Object, args = []) {
- const server: ServerInfo = {
- app: null,
- serverNumber: serverNumber,
- url: `http://localhost:${9000 + serverNumber}`,
- host: `localhost:${9000 + serverNumber}`,
- client: {
- id: null,
- secret: null
- },
- user: {
- username: null,
- password: null
- }
- }
-
- // These actions are async so we need to be sure that they have both been done
- const serverRunString = {
- 'Server listening': false
- }
- const key = 'Database peertube_test' + serverNumber + ' is ready'
- serverRunString[key] = false
-
- const regexps = {
- client_id: 'Client id: (.+)',
- client_secret: 'Client secret: (.+)',
- user_username: 'Username: (.+)',
- user_password: 'User password: (.+)'
- }
-
- // Share the environment
- const env = Object.create(process.env)
- env['NODE_ENV'] = 'test'
- env['NODE_APP_INSTANCE'] = serverNumber.toString()
-
- if (configOverride !== undefined) {
- env['NODE_CONFIG'] = JSON.stringify(configOverride)
- }
-
- const options = {
- silent: true,
- env: env,
- detached: true
- }
-
- return new Promise<ServerInfo>(res => {
- server.app = fork(join(root(), 'dist', 'server.js'), args, options)
- server.app.stdout.on('data', function onStdout (data) {
- let dontContinue = false
-
- // Capture things if we want to
- for (const key of Object.keys(regexps)) {
- const regexp = regexps[key]
- const matches = data.toString().match(regexp)
- if (matches !== null) {
- if (key === 'client_id') server.client.id = matches[1]
- else if (key === 'client_secret') server.client.secret = matches[1]
- else if (key === 'user_username') server.user.username = matches[1]
- else if (key === 'user_password') server.user.password = matches[1]
- }
- }
-
- // Check if all required sentences are here
- for (const key of Object.keys(serverRunString)) {
- if (data.toString().indexOf(key) !== -1) serverRunString[key] = true
- if (serverRunString[key] === false) dontContinue = true
- }
-
- // If no, there is maybe one thing not already initialized (client/user credentials generation...)
- if (dontContinue === true) return
-
- server.app.stdout.removeListener('data', onStdout)
-
- process.on('exit', () => {
- try {
- process.kill(server.app.pid)
- } catch { /* empty */ }
- })
-
- res(server)
- })
-
- })
-}
-
-async function reRunServer (server: ServerInfo, configOverride?: any) {
- const newServer = await runServer(server.serverNumber, configOverride)
- server.app = newServer.app
-
- return server
-}
-
-async function checkTmpIsEmpty (server: ServerInfo) {
- return checkDirectoryIsEmpty(server, 'tmp')
-}
-
-async function checkDirectoryIsEmpty (server: ServerInfo, directory: string) {
- const testDirectory = 'test' + server.serverNumber
-
- const directoryPath = join(root(), testDirectory, directory)
-
- const directoryExists = existsSync(directoryPath)
- expect(directoryExists).to.be.true
-
- const files = await readdir(directoryPath)
- expect(files).to.have.lengthOf(0)
-}
-
-function killallServers (servers: ServerInfo[]) {
- for (const server of servers) {
- process.kill(-server.app.pid)
- }
-}
-
-async function waitUntilLog (server: ServerInfo, str: string, count = 1) {
- const logfile = join(root(), 'test' + server.serverNumber, 'logs/peertube.log')
-
- while (true) {
- const buf = await readFile(logfile)
-
- const matches = buf.toString().match(new RegExp(str, 'g'))
- if (matches && matches.length === count) return
-
- await wait(1000)
- }
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- checkDirectoryIsEmpty,
- checkTmpIsEmpty,
- ServerInfo,
- flushAndRunMultipleServers,
- flushTests,
- runServer,
- killallServers,
- reRunServer,
- waitUntilLog
-}
+++ /dev/null
-import { makeGetRequest } from '../requests/requests'
-
-function getStats (url: string, useCache = false) {
- const path = '/api/v1/server/stats'
-
- const query = {
- t: useCache ? undefined : new Date().getTime()
- }
-
- return makeGetRequest({
- url,
- path,
- query,
- statusCodeExpected: 200
- })
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- getStats
-}
+++ /dev/null
-import * as io from 'socket.io-client'
-
-function getUserNotificationSocket (serverUrl: string, accessToken: string) {
- return io(serverUrl + '/user-notifications', {
- query: { accessToken }
- })
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- getUserNotificationSocket
-}
+++ /dev/null
-/* tslint:disable:no-unused-expression */
-
-import * as request from 'supertest'
-import { expect } from 'chai'
-import { existsSync, readdir } from 'fs-extra'
-import { join } from 'path'
-import { Account } from '../../models/actors'
-import { root } from '../miscs/miscs'
-import { makeGetRequest } from '../requests/requests'
-import { VideoRateType } from '../../models/videos'
-
-function getAccountsList (url: string, sort = '-createdAt', statusCodeExpected = 200) {
- const path = '/api/v1/accounts'
-
- return makeGetRequest({
- url,
- query: { sort },
- path,
- statusCodeExpected
- })
-}
-
-function getAccount (url: string, accountName: string, statusCodeExpected = 200) {
- const path = '/api/v1/accounts/' + accountName
-
- return makeGetRequest({
- url,
- path,
- statusCodeExpected
- })
-}
-
-async function expectAccountFollows (url: string, nameWithDomain: string, followersCount: number, followingCount: number) {
- const res = await getAccountsList(url)
- const account = res.body.data.find((a: Account) => a.name + '@' + a.host === nameWithDomain)
-
- const message = `${nameWithDomain} on ${url}`
- expect(account.followersCount).to.equal(followersCount, message)
- expect(account.followingCount).to.equal(followingCount, message)
-}
-
-async function checkActorFilesWereRemoved (actorUUID: string, serverNumber: number) {
- const testDirectory = 'test' + serverNumber
-
- for (const directory of [ 'avatars' ]) {
- const directoryPath = join(root(), testDirectory, directory)
-
- const directoryExists = existsSync(directoryPath)
- expect(directoryExists).to.be.true
-
- const files = await readdir(directoryPath)
- for (const file of files) {
- expect(file).to.not.contain(actorUUID)
- }
- }
-}
-
-function getAccountRatings (url: string, accountName: string, accessToken: string, rating?: VideoRateType, statusCodeExpected = 200) {
- const path = '/api/v1/accounts/' + accountName + '/ratings'
-
- const query = rating ? { rating } : {}
-
- return request(url)
- .get(path)
- .query(query)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .expect(statusCodeExpected)
- .expect('Content-Type', /json/)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- getAccount,
- expectAccountFollows,
- getAccountsList,
- checkActorFilesWereRemoved,
- getAccountRatings
-}
+++ /dev/null
-/* tslint:disable:no-unused-expression */
-
-import { makeGetRequest, makeDeleteRequest, makePostBodyRequest } from '../requests/requests'
-
-function getAccountBlocklistByAccount (
- url: string,
- token: string,
- start: number,
- count: number,
- sort = '-createdAt',
- statusCodeExpected = 200
-) {
- const path = '/api/v1/users/me/blocklist/accounts'
-
- return makeGetRequest({
- url,
- token,
- query: { start, count, sort },
- path,
- statusCodeExpected
- })
-}
-
-function addAccountToAccountBlocklist (url: string, token: string, accountToBlock: string, statusCodeExpected = 204) {
- const path = '/api/v1/users/me/blocklist/accounts'
-
- return makePostBodyRequest({
- url,
- path,
- token,
- fields: {
- accountName: accountToBlock
- },
- statusCodeExpected
- })
-}
-
-function removeAccountFromAccountBlocklist (url: string, token: string, accountToUnblock: string, statusCodeExpected = 204) {
- const path = '/api/v1/users/me/blocklist/accounts/' + accountToUnblock
-
- return makeDeleteRequest({
- url,
- path,
- token,
- statusCodeExpected
- })
-}
-
-function getServerBlocklistByAccount (
- url: string,
- token: string,
- start: number,
- count: number,
- sort = '-createdAt',
- statusCodeExpected = 200
-) {
- const path = '/api/v1/users/me/blocklist/servers'
-
- return makeGetRequest({
- url,
- token,
- query: { start, count, sort },
- path,
- statusCodeExpected
- })
-}
-
-function addServerToAccountBlocklist (url: string, token: string, serverToBlock: string, statusCodeExpected = 204) {
- const path = '/api/v1/users/me/blocklist/servers'
-
- return makePostBodyRequest({
- url,
- path,
- token,
- fields: {
- host: serverToBlock
- },
- statusCodeExpected
- })
-}
-
-function removeServerFromAccountBlocklist (url: string, token: string, serverToBlock: string, statusCodeExpected = 204) {
- const path = '/api/v1/users/me/blocklist/servers/' + serverToBlock
-
- return makeDeleteRequest({
- url,
- path,
- token,
- statusCodeExpected
- })
-}
-
-function getAccountBlocklistByServer (
- url: string,
- token: string,
- start: number,
- count: number,
- sort = '-createdAt',
- statusCodeExpected = 200
-) {
- const path = '/api/v1/server/blocklist/accounts'
-
- return makeGetRequest({
- url,
- token,
- query: { start, count, sort },
- path,
- statusCodeExpected
- })
-}
-
-function addAccountToServerBlocklist (url: string, token: string, accountToBlock: string, statusCodeExpected = 204) {
- const path = '/api/v1/server/blocklist/accounts'
-
- return makePostBodyRequest({
- url,
- path,
- token,
- fields: {
- accountName: accountToBlock
- },
- statusCodeExpected
- })
-}
-
-function removeAccountFromServerBlocklist (url: string, token: string, accountToUnblock: string, statusCodeExpected = 204) {
- const path = '/api/v1/server/blocklist/accounts/' + accountToUnblock
-
- return makeDeleteRequest({
- url,
- path,
- token,
- statusCodeExpected
- })
-}
-
-function getServerBlocklistByServer (
- url: string,
- token: string,
- start: number,
- count: number,
- sort = '-createdAt',
- statusCodeExpected = 200
-) {
- const path = '/api/v1/server/blocklist/servers'
-
- return makeGetRequest({
- url,
- token,
- query: { start, count, sort },
- path,
- statusCodeExpected
- })
-}
-
-function addServerToServerBlocklist (url: string, token: string, serverToBlock: string, statusCodeExpected = 204) {
- const path = '/api/v1/server/blocklist/servers'
-
- return makePostBodyRequest({
- url,
- path,
- token,
- fields: {
- host: serverToBlock
- },
- statusCodeExpected
- })
-}
-
-function removeServerFromServerBlocklist (url: string, token: string, serverToBlock: string, statusCodeExpected = 204) {
- const path = '/api/v1/server/blocklist/servers/' + serverToBlock
-
- return makeDeleteRequest({
- url,
- path,
- token,
- statusCodeExpected
- })
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- getAccountBlocklistByAccount,
- addAccountToAccountBlocklist,
- removeAccountFromAccountBlocklist,
- getServerBlocklistByAccount,
- addServerToAccountBlocklist,
- removeServerFromAccountBlocklist,
-
- getAccountBlocklistByServer,
- addAccountToServerBlocklist,
- removeAccountFromServerBlocklist,
- getServerBlocklistByServer,
- addServerToServerBlocklist,
- removeServerFromServerBlocklist
-}
+++ /dev/null
-import * as request from 'supertest'
-
-import { ServerInfo } from '../server/servers'
-
-type Client = { id: string, secret: string }
-type User = { username: string, password: string }
-type Server = { url: string, client: Client, user: User }
-
-function login (url: string, client: Client, user: User, expectedStatus = 200) {
- const path = '/api/v1/users/token'
-
- const body = {
- client_id: client.id,
- client_secret: client.secret,
- username: user.username,
- password: user.password,
- response_type: 'code',
- grant_type: 'password',
- scope: 'upload'
- }
-
- return request(url)
- .post(path)
- .type('form')
- .send(body)
- .expect(expectedStatus)
-}
-
-async function serverLogin (server: Server) {
- const res = await login(server.url, server.client, server.user, 200)
-
- return res.body.access_token as string
-}
-
-async function userLogin (server: Server, user: User, expectedStatus = 200) {
- const res = await login(server.url, server.client, user, expectedStatus)
-
- return res.body.access_token as string
-}
-
-function setAccessTokensToServers (servers: ServerInfo[]) {
- const tasks: Promise<any>[] = []
-
- for (const server of servers) {
- const p = serverLogin(server).then(t => server.accessToken = t)
- tasks.push(p)
- }
-
- return Promise.all(tasks)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- login,
- serverLogin,
- userLogin,
- setAccessTokensToServers,
- Server,
- Client,
- User
-}
+++ /dev/null
-/* tslint:disable:no-unused-expression */
-
-import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
-import { UserNotification, UserNotificationSetting, UserNotificationType } from '../../models/users'
-import { ServerInfo } from '..'
-import { expect } from 'chai'
-import { inspect } from 'util'
-
-function updateMyNotificationSettings (url: string, token: string, settings: UserNotificationSetting, statusCodeExpected = 204) {
- const path = '/api/v1/users/me/notification-settings'
-
- return makePutBodyRequest({
- url,
- path,
- token,
- fields: settings,
- statusCodeExpected
- })
-}
-
-async function getUserNotifications (
- url: string,
- token: string,
- start: number,
- count: number,
- unread?: boolean,
- sort = '-createdAt',
- statusCodeExpected = 200
-) {
- const path = '/api/v1/users/me/notifications'
-
- return makeGetRequest({
- url,
- path,
- token,
- query: {
- start,
- count,
- sort,
- unread
- },
- statusCodeExpected
- })
-}
-
-function markAsReadNotifications (url: string, token: string, ids: number[], statusCodeExpected = 204) {
- const path = '/api/v1/users/me/notifications/read'
-
- return makePostBodyRequest({
- url,
- path,
- token,
- fields: { ids },
- statusCodeExpected
- })
-}
-function markAsReadAllNotifications (url: string, token: string, statusCodeExpected = 204) {
- const path = '/api/v1/users/me/notifications/read-all'
-
- return makePostBodyRequest({
- url,
- path,
- token,
- statusCodeExpected
- })
-}
-
-async function getLastNotification (serverUrl: string, accessToken: string) {
- const res = await getUserNotifications(serverUrl, accessToken, 0, 1, undefined, '-createdAt')
-
- if (res.body.total === 0) return undefined
-
- return res.body.data[0] as UserNotification
-}
-
-type CheckerBaseParams = {
- server: ServerInfo
- emails: object[]
- socketNotifications: UserNotification[]
- token: string,
- check?: { web: boolean, mail: boolean }
-}
-
-type CheckerType = 'presence' | 'absence'
-
-async function checkNotification (
- base: CheckerBaseParams,
- notificationChecker: (notification: UserNotification, type: CheckerType) => void,
- emailNotificationFinder: (email: object) => boolean,
- checkType: CheckerType
-) {
- const check = base.check || { web: true, mail: true }
-
- if (check.web) {
- const notification = await getLastNotification(base.server.url, base.token)
-
- if (notification || checkType !== 'absence') {
- notificationChecker(notification, checkType)
- }
-
- const socketNotification = base.socketNotifications.find(n => {
- try {
- notificationChecker(n, 'presence')
- return true
- } catch {
- return false
- }
- })
-
- if (checkType === 'presence') {
- const obj = inspect(base.socketNotifications, { depth: 5 })
- expect(socketNotification, 'The socket notification is absent. ' + obj).to.not.be.undefined
- } else {
- const obj = inspect(socketNotification, { depth: 5 })
- expect(socketNotification, 'The socket notification is present. ' + obj).to.be.undefined
- }
- }
-
- if (check.mail) {
- // Last email
- const email = base.emails
- .slice()
- .reverse()
- .find(e => emailNotificationFinder(e))
-
- if (checkType === 'presence') {
- expect(email, 'The email is absent. ' + inspect(base.emails)).to.not.be.undefined
- } else {
- expect(email, 'The email is present. ' + inspect(email)).to.be.undefined
- }
- }
-}
-
-function checkVideo (video: any, videoName?: string, videoUUID?: string) {
- expect(video.name).to.be.a('string')
- expect(video.name).to.not.be.empty
- if (videoName) expect(video.name).to.equal(videoName)
-
- expect(video.uuid).to.be.a('string')
- expect(video.uuid).to.not.be.empty
- if (videoUUID) expect(video.uuid).to.equal(videoUUID)
-
- expect(video.id).to.be.a('number')
-}
-
-function checkActor (actor: any) {
- expect(actor.displayName).to.be.a('string')
- expect(actor.displayName).to.not.be.empty
- expect(actor.host).to.not.be.undefined
-}
-
-function checkComment (comment: any, commentId: number, threadId: number) {
- expect(comment.id).to.equal(commentId)
- expect(comment.threadId).to.equal(threadId)
-}
-
-async function checkNewVideoFromSubscription (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) {
- const notificationType = UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION
-
- function notificationChecker (notification: UserNotification, type: CheckerType) {
- if (type === 'presence') {
- expect(notification).to.not.be.undefined
- expect(notification.type).to.equal(notificationType)
-
- checkVideo(notification.video, videoName, videoUUID)
- checkActor(notification.video.channel)
- } else {
- expect(notification).to.satisfy((n: UserNotification) => {
- return n === undefined || n.type !== UserNotificationType.NEW_VIDEO_FROM_SUBSCRIPTION || n.video.name !== videoName
- })
- }
- }
-
- function emailFinder (email: object) {
- const text = email[ 'text' ]
- return text.indexOf(videoUUID) !== -1 && text.indexOf('Your subscription') !== -1
- }
-
- await checkNotification(base, notificationChecker, emailFinder, type)
-}
-
-async function checkVideoIsPublished (base: CheckerBaseParams, videoName: string, videoUUID: string, type: CheckerType) {
- const notificationType = UserNotificationType.MY_VIDEO_PUBLISHED
-
- function notificationChecker (notification: UserNotification, type: CheckerType) {
- if (type === 'presence') {
- expect(notification).to.not.be.undefined
- expect(notification.type).to.equal(notificationType)
-
- checkVideo(notification.video, videoName, videoUUID)
- checkActor(notification.video.channel)
- } else {
- expect(notification.video).to.satisfy(v => v === undefined || v.name !== videoName)
- }
- }
-
- function emailFinder (email: object) {
- const text: string = email[ 'text' ]
- return text.includes(videoUUID) && text.includes('Your video')
- }
-
- await checkNotification(base, notificationChecker, emailFinder, type)
-}
-
-async function checkMyVideoImportIsFinished (
- base: CheckerBaseParams,
- videoName: string,
- videoUUID: string,
- url: string,
- success: boolean,
- type: CheckerType
-) {
- const notificationType = success ? UserNotificationType.MY_VIDEO_IMPORT_SUCCESS : UserNotificationType.MY_VIDEO_IMPORT_ERROR
-
- function notificationChecker (notification: UserNotification, type: CheckerType) {
- if (type === 'presence') {
- expect(notification).to.not.be.undefined
- expect(notification.type).to.equal(notificationType)
-
- expect(notification.videoImport.targetUrl).to.equal(url)
-
- if (success) checkVideo(notification.videoImport.video, videoName, videoUUID)
- } else {
- expect(notification.videoImport).to.satisfy(i => i === undefined || i.targetUrl !== url)
- }
- }
-
- function emailFinder (email: object) {
- const text: string = email[ 'text' ]
- const toFind = success ? ' finished' : ' error'
-
- return text.includes(url) && text.includes(toFind)
- }
-
- await checkNotification(base, notificationChecker, emailFinder, type)
-}
-
-async function checkUserRegistered (base: CheckerBaseParams, username: string, type: CheckerType) {
- const notificationType = UserNotificationType.NEW_USER_REGISTRATION
-
- function notificationChecker (notification: UserNotification, type: CheckerType) {
- if (type === 'presence') {
- expect(notification).to.not.be.undefined
- expect(notification.type).to.equal(notificationType)
-
- checkActor(notification.account)
- expect(notification.account.name).to.equal(username)
- } else {
- expect(notification).to.satisfy(n => n.type !== notificationType || n.account.name !== username)
- }
- }
-
- function emailFinder (email: object) {
- const text: string = email[ 'text' ]
-
- return text.includes(' registered ') && text.includes(username)
- }
-
- await checkNotification(base, notificationChecker, emailFinder, type)
-}
-
-async function checkNewActorFollow (
- base: CheckerBaseParams,
- followType: 'channel' | 'account',
- followerName: string,
- followerDisplayName: string,
- followingDisplayName: string,
- type: CheckerType
-) {
- const notificationType = UserNotificationType.NEW_FOLLOW
-
- function notificationChecker (notification: UserNotification, type: CheckerType) {
- if (type === 'presence') {
- expect(notification).to.not.be.undefined
- expect(notification.type).to.equal(notificationType)
-
- checkActor(notification.actorFollow.follower)
- expect(notification.actorFollow.follower.displayName).to.equal(followerDisplayName)
- expect(notification.actorFollow.follower.name).to.equal(followerName)
- expect(notification.actorFollow.follower.host).to.not.be.undefined
-
- expect(notification.actorFollow.following.displayName).to.equal(followingDisplayName)
- expect(notification.actorFollow.following.type).to.equal(followType)
- } else {
- expect(notification).to.satisfy(n => {
- return n.type !== notificationType ||
- (n.actorFollow.follower.name !== followerName && n.actorFollow.following !== followingDisplayName)
- })
- }
- }
-
- function emailFinder (email: object) {
- const text: string = email[ 'text' ]
-
- return text.includes('Your ' + followType) && text.includes(followingDisplayName) && text.includes(followerDisplayName)
- }
-
- await checkNotification(base, notificationChecker, emailFinder, type)
-}
-
-async function checkNewInstanceFollower (base: CheckerBaseParams, followerHost: string, type: CheckerType) {
- const notificationType = UserNotificationType.NEW_INSTANCE_FOLLOWER
-
- function notificationChecker (notification: UserNotification, type: CheckerType) {
- if (type === 'presence') {
- expect(notification).to.not.be.undefined
- expect(notification.type).to.equal(notificationType)
-
- checkActor(notification.actorFollow.follower)
- expect(notification.actorFollow.follower.name).to.equal('peertube')
- expect(notification.actorFollow.follower.host).to.equal(followerHost)
-
- expect(notification.actorFollow.following.name).to.equal('peertube')
- } else {
- expect(notification).to.satisfy(n => {
- return n.type !== notificationType || n.actorFollow.follower.host !== followerHost
- })
- }
- }
-
- function emailFinder (email: object) {
- const text: string = email[ 'text' ]
-
- return text.includes('instance has a new follower') && text.includes(followerHost)
- }
-
- await checkNotification(base, notificationChecker, emailFinder, type)
-}
-
-async function checkCommentMention (
- base: CheckerBaseParams,
- uuid: string,
- commentId: number,
- threadId: number,
- byAccountDisplayName: string,
- type: CheckerType
-) {
- const notificationType = UserNotificationType.COMMENT_MENTION
-
- function notificationChecker (notification: UserNotification, type: CheckerType) {
- if (type === 'presence') {
- expect(notification).to.not.be.undefined
- expect(notification.type).to.equal(notificationType)
-
- checkComment(notification.comment, commentId, threadId)
- checkActor(notification.comment.account)
- expect(notification.comment.account.displayName).to.equal(byAccountDisplayName)
-
- checkVideo(notification.comment.video, undefined, uuid)
- } else {
- expect(notification).to.satisfy(n => n.type !== notificationType || n.comment.id !== commentId)
- }
- }
-
- function emailFinder (email: object) {
- const text: string = email[ 'text' ]
-
- return text.includes(' mentioned ') && text.includes(uuid) && text.includes(byAccountDisplayName)
- }
-
- await checkNotification(base, notificationChecker, emailFinder, type)
-}
-
-let lastEmailCount = 0
-async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string, commentId: number, threadId: number, type: CheckerType) {
- const notificationType = UserNotificationType.NEW_COMMENT_ON_MY_VIDEO
-
- function notificationChecker (notification: UserNotification, type: CheckerType) {
- if (type === 'presence') {
- expect(notification).to.not.be.undefined
- expect(notification.type).to.equal(notificationType)
-
- checkComment(notification.comment, commentId, threadId)
- checkActor(notification.comment.account)
- checkVideo(notification.comment.video, undefined, uuid)
- } else {
- expect(notification).to.satisfy((n: UserNotification) => {
- return n === undefined || n.comment === undefined || n.comment.id !== commentId
- })
- }
- }
-
- const commentUrl = `http://localhost:9001/videos/watch/${uuid};threadId=${threadId}`
- function emailFinder (email: object) {
- return email[ 'text' ].indexOf(commentUrl) !== -1
- }
-
- await checkNotification(base, notificationChecker, emailFinder, type)
-
- if (type === 'presence') {
- // We cannot detect email duplicates, so check we received another email
- expect(base.emails).to.have.length.above(lastEmailCount)
- lastEmailCount = base.emails.length
- }
-}
-
-async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
- const notificationType = UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS
-
- function notificationChecker (notification: UserNotification, type: CheckerType) {
- if (type === 'presence') {
- expect(notification).to.not.be.undefined
- expect(notification.type).to.equal(notificationType)
-
- expect(notification.videoAbuse.id).to.be.a('number')
- checkVideo(notification.videoAbuse.video, videoName, videoUUID)
- } else {
- expect(notification).to.satisfy((n: UserNotification) => {
- return n === undefined || n.videoAbuse === undefined || n.videoAbuse.video.uuid !== videoUUID
- })
- }
- }
-
- function emailFinder (email: object) {
- const text = email[ 'text' ]
- return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1
- }
-
- await checkNotification(base, notificationChecker, emailFinder, type)
-}
-
-async function checkVideoAutoBlacklistForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
- const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS
-
- function notificationChecker (notification: UserNotification, type: CheckerType) {
- if (type === 'presence') {
- expect(notification).to.not.be.undefined
- expect(notification.type).to.equal(notificationType)
-
- expect(notification.video.id).to.be.a('number')
- checkVideo(notification.video, videoName, videoUUID)
- } else {
- expect(notification).to.satisfy((n: UserNotification) => {
- return n === undefined || n.video === undefined || n.video.uuid !== videoUUID
- })
- }
- }
-
- function emailFinder (email: object) {
- const text = email[ 'text' ]
- return text.indexOf(videoUUID) !== -1 && email[ 'text' ].indexOf('video-auto-blacklist/list') !== -1
- }
-
- await checkNotification(base, notificationChecker, emailFinder, type)
-}
-
-async function checkNewBlacklistOnMyVideo (
- base: CheckerBaseParams,
- videoUUID: string,
- videoName: string,
- blacklistType: 'blacklist' | 'unblacklist'
-) {
- const notificationType = blacklistType === 'blacklist'
- ? UserNotificationType.BLACKLIST_ON_MY_VIDEO
- : UserNotificationType.UNBLACKLIST_ON_MY_VIDEO
-
- function notificationChecker (notification: UserNotification) {
- expect(notification).to.not.be.undefined
- expect(notification.type).to.equal(notificationType)
-
- const video = blacklistType === 'blacklist' ? notification.videoBlacklist.video : notification.video
-
- checkVideo(video, videoName, videoUUID)
- }
-
- function emailFinder (email: object) {
- const text = email[ 'text' ]
- return text.indexOf(videoUUID) !== -1 && text.indexOf(' ' + blacklistType) !== -1
- }
-
- await checkNotification(base, notificationChecker, emailFinder, 'presence')
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- CheckerBaseParams,
- CheckerType,
- checkNotification,
- markAsReadAllNotifications,
- checkMyVideoImportIsFinished,
- checkUserRegistered,
- checkVideoIsPublished,
- checkNewVideoFromSubscription,
- checkNewActorFollow,
- checkNewCommentOnMyVideo,
- checkNewBlacklistOnMyVideo,
- checkCommentMention,
- updateMyNotificationSettings,
- checkNewVideoAbuseForModerators,
- checkVideoAutoBlacklistForModerators,
- getUserNotifications,
- markAsReadNotifications,
- getLastNotification,
- checkNewInstanceFollower
-}
+++ /dev/null
-import { makeDeleteRequest, makeGetRequest, makePostBodyRequest } from '../requests/requests'
-
-function addUserSubscription (url: string, token: string, targetUri: string, statusCodeExpected = 204) {
- const path = '/api/v1/users/me/subscriptions'
-
- return makePostBodyRequest({
- url,
- path,
- token,
- statusCodeExpected,
- fields: { uri: targetUri }
- })
-}
-
-function listUserSubscriptions (url: string, token: string, sort = '-createdAt', statusCodeExpected = 200) {
- const path = '/api/v1/users/me/subscriptions'
-
- return makeGetRequest({
- url,
- path,
- token,
- statusCodeExpected,
- query: { sort }
- })
-}
-
-function listUserSubscriptionVideos (url: string, token: string, sort = '-createdAt', statusCodeExpected = 200) {
- const path = '/api/v1/users/me/subscriptions/videos'
-
- return makeGetRequest({
- url,
- path,
- token,
- statusCodeExpected,
- query: { sort }
- })
-}
-
-function getUserSubscription (url: string, token: string, uri: string, statusCodeExpected = 200) {
- const path = '/api/v1/users/me/subscriptions/' + uri
-
- return makeGetRequest({
- url,
- path,
- token,
- statusCodeExpected
- })
-}
-
-function removeUserSubscription (url: string, token: string, uri: string, statusCodeExpected = 204) {
- const path = '/api/v1/users/me/subscriptions/' + uri
-
- return makeDeleteRequest({
- url,
- path,
- token,
- statusCodeExpected
- })
-}
-
-function areSubscriptionsExist (url: string, token: string, uris: string[], statusCodeExpected = 200) {
- const path = '/api/v1/users/me/subscriptions/exist'
-
- return makeGetRequest({
- url,
- path,
- query: { 'uris[]': uris },
- token,
- statusCodeExpected
- })
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- areSubscriptionsExist,
- addUserSubscription,
- listUserSubscriptions,
- getUserSubscription,
- listUserSubscriptionVideos,
- removeUserSubscription
-}
+++ /dev/null
-import * as request from 'supertest'
-import { makePostBodyRequest, makePutBodyRequest, updateAvatarRequest } from '../requests/requests'
-
-import { UserRole } from '../../index'
-import { NSFWPolicyType } from '../../models/videos/nsfw-policy.type'
-import { ServerInfo, userLogin } from '..'
-import { UserAdminFlag } from '../../models/users/user-flag.model'
-
-type CreateUserArgs = { url: string,
- accessToken: string,
- username: string,
- password: string,
- videoQuota?: number,
- videoQuotaDaily?: number,
- role?: UserRole,
- adminFlags?: UserAdminFlag,
- specialStatus?: number
-}
-function createUser (parameters: CreateUserArgs) {
- const {
- url,
- accessToken,
- username,
- adminFlags,
- password = 'password',
- videoQuota = 1000000,
- videoQuotaDaily = -1,
- role = UserRole.USER,
- specialStatus = 200
- } = parameters
-
- const path = '/api/v1/users'
- const body = {
- username,
- password,
- role,
- adminFlags,
- email: username + '@example.com',
- videoQuota,
- videoQuotaDaily
- }
-
- return request(url)
- .post(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .send(body)
- .expect(specialStatus)
-}
-
-async function generateUserAccessToken (server: ServerInfo, username: string) {
- const password = 'my super password'
- await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password })
-
- return userLogin(server, { username, password })
-}
-
-function registerUser (url: string, username: string, password: string, specialStatus = 204) {
- const path = '/api/v1/users/register'
- const body = {
- username,
- password,
- email: username + '@example.com'
- }
-
- return request(url)
- .post(path)
- .set('Accept', 'application/json')
- .send(body)
- .expect(specialStatus)
-}
-
-function getMyUserInformation (url: string, accessToken: string, specialStatus = 200) {
- const path = '/api/v1/users/me'
-
- return request(url)
- .get(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .expect(specialStatus)
- .expect('Content-Type', /json/)
-}
-
-function deleteMe (url: string, accessToken: string, specialStatus = 204) {
- const path = '/api/v1/users/me'
-
- return request(url)
- .delete(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .expect(specialStatus)
-}
-
-function getMyUserVideoQuotaUsed (url: string, accessToken: string, specialStatus = 200) {
- const path = '/api/v1/users/me/video-quota-used'
-
- return request(url)
- .get(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .expect(specialStatus)
- .expect('Content-Type', /json/)
-}
-
-function getUserInformation (url: string, accessToken: string, userId: number) {
- const path = '/api/v1/users/' + userId
-
- return request(url)
- .get(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function getMyUserVideoRating (url: string, accessToken: string, videoId: number | string, specialStatus = 200) {
- const path = '/api/v1/users/me/videos/' + videoId + '/rating'
-
- return request(url)
- .get(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .expect(specialStatus)
- .expect('Content-Type', /json/)
-}
-
-function getUsersList (url: string, accessToken: string) {
- const path = '/api/v1/users'
-
- return request(url)
- .get(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function getUsersListPaginationAndSort (url: string, accessToken: string, start: number, count: number, sort: string, search?: string) {
- const path = '/api/v1/users'
-
- return request(url)
- .get(path)
- .query({ start })
- .query({ count })
- .query({ sort })
- .query({ search })
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function removeUser (url: string, userId: number | string, accessToken: string, expectedStatus = 204) {
- const path = '/api/v1/users'
-
- return request(url)
- .delete(path + '/' + userId)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .expect(expectedStatus)
-}
-
-function blockUser (url: string, userId: number | string, accessToken: string, expectedStatus = 204, reason?: string) {
- const path = '/api/v1/users'
- let body: any
- if (reason) body = { reason }
-
- return request(url)
- .post(path + '/' + userId + '/block')
- .send(body)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .expect(expectedStatus)
-}
-
-function unblockUser (url: string, userId: number | string, accessToken: string, expectedStatus = 204) {
- const path = '/api/v1/users'
-
- return request(url)
- .post(path + '/' + userId + '/unblock')
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .expect(expectedStatus)
-}
-
-function updateMyUser (options: {
- url: string
- accessToken: string
- currentPassword?: string
- newPassword?: string
- nsfwPolicy?: NSFWPolicyType
- email?: string
- autoPlayVideo?: boolean
- displayName?: string
- description?: string
- videosHistoryEnabled?: boolean
-}) {
- const path = '/api/v1/users/me'
-
- const toSend = {}
- if (options.currentPassword !== undefined && options.currentPassword !== null) toSend['currentPassword'] = options.currentPassword
- if (options.newPassword !== undefined && options.newPassword !== null) toSend['password'] = options.newPassword
- if (options.nsfwPolicy !== undefined && options.nsfwPolicy !== null) toSend['nsfwPolicy'] = options.nsfwPolicy
- if (options.autoPlayVideo !== undefined && options.autoPlayVideo !== null) toSend['autoPlayVideo'] = options.autoPlayVideo
- 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,
- path,
- token: options.accessToken,
- fields: toSend,
- statusCodeExpected: 204
- })
-}
-
-function updateMyAvatar (options: {
- url: string,
- accessToken: string,
- fixture: string
-}) {
- const path = '/api/v1/users/me/avatar/pick'
-
- return updateAvatarRequest(Object.assign(options, { path }))
-}
-
-function updateUser (options: {
- url: string
- userId: number,
- accessToken: string,
- email?: string,
- emailVerified?: boolean,
- videoQuota?: number,
- videoQuotaDaily?: number,
- password?: string,
- adminFlags?: UserAdminFlag,
- role?: UserRole
-}) {
- const path = '/api/v1/users/' + options.userId
-
- const toSend = {}
- if (options.password !== undefined && options.password !== null) toSend['password'] = options.password
- if (options.email !== undefined && options.email !== null) toSend['email'] = options.email
- if (options.emailVerified !== undefined && options.emailVerified !== null) toSend['emailVerified'] = options.emailVerified
- if (options.videoQuota !== undefined && options.videoQuota !== null) toSend['videoQuota'] = options.videoQuota
- if (options.videoQuotaDaily !== undefined && options.videoQuotaDaily !== null) toSend['videoQuotaDaily'] = options.videoQuotaDaily
- if (options.role !== undefined && options.role !== null) toSend['role'] = options.role
- if (options.adminFlags !== undefined && options.adminFlags !== null) toSend['adminFlags'] = options.adminFlags
-
- return makePutBodyRequest({
- url: options.url,
- path,
- token: options.accessToken,
- fields: toSend,
- statusCodeExpected: 204
- })
-}
-
-function askResetPassword (url: string, email: string) {
- const path = '/api/v1/users/ask-reset-password'
-
- return makePostBodyRequest({
- url,
- path,
- fields: { email },
- statusCodeExpected: 204
- })
-}
-
-function resetPassword (url: string, userId: number, verificationString: string, password: string, statusCodeExpected = 204) {
- const path = '/api/v1/users/' + userId + '/reset-password'
-
- return makePostBodyRequest({
- url,
- path,
- fields: { password, verificationString },
- statusCodeExpected
- })
-}
-
-function askSendVerifyEmail (url: string, email: string) {
- const path = '/api/v1/users/ask-send-verify-email'
-
- return makePostBodyRequest({
- url,
- path,
- fields: { email },
- statusCodeExpected: 204
- })
-}
-
-function verifyEmail (url: string, userId: number, verificationString: string, statusCodeExpected = 204) {
- const path = '/api/v1/users/' + userId + '/verify-email'
-
- return makePostBodyRequest({
- url,
- path,
- fields: { verificationString },
- statusCodeExpected
- })
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- createUser,
- registerUser,
- getMyUserInformation,
- getMyUserVideoRating,
- deleteMe,
- getMyUserVideoQuotaUsed,
- getUsersList,
- getUsersListPaginationAndSort,
- removeUser,
- updateUser,
- updateMyUser,
- getUserInformation,
- blockUser,
- unblockUser,
- askResetPassword,
- resetPassword,
- updateMyAvatar,
- askSendVerifyEmail,
- generateUserAccessToken,
- verifyEmail
-}
+++ /dev/null
-import * as request from 'supertest'
-
-function getOEmbed (url: string, oembedUrl: string, format?: string, maxHeight?: number, maxWidth?: number) {
- const path = '/services/oembed'
- const query = {
- url: oembedUrl,
- format,
- maxheight: maxHeight,
- maxwidth: maxWidth
- }
-
- return request(url)
- .get(path)
- .query(query)
- .set('Accept', 'application/json')
- .expect(200)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- getOEmbed
-}
+++ /dev/null
-import * as request from 'supertest'
-import { VideoAbuseUpdate } from '../../models/videos/abuse/video-abuse-update.model'
-import { makeDeleteRequest, makePutBodyRequest } from '../requests/requests'
-
-function reportVideoAbuse (url: string, token: string, videoId: number | string, reason: string, specialStatus = 200) {
- const path = '/api/v1/videos/' + videoId + '/abuse'
-
- return request(url)
- .post(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + token)
- .send({ reason })
- .expect(specialStatus)
-}
-
-function getVideoAbusesList (url: string, token: string) {
- const path = '/api/v1/videos/abuse'
-
- return request(url)
- .get(path)
- .query({ sort: 'createdAt' })
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + token)
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function updateVideoAbuse (
- url: string,
- token: string,
- videoId: string | number,
- videoAbuseId: number,
- body: VideoAbuseUpdate,
- statusCodeExpected = 204
-) {
- const path = '/api/v1/videos/' + videoId + '/abuse/' + videoAbuseId
-
- return makePutBodyRequest({
- url,
- token,
- path,
- fields: body,
- statusCodeExpected
- })
-}
-
-function deleteVideoAbuse (url: string, token: string, videoId: string | number, videoAbuseId: number, statusCodeExpected = 204) {
- const path = '/api/v1/videos/' + videoId + '/abuse/' + videoAbuseId
-
- return makeDeleteRequest({
- url,
- token,
- path,
- statusCodeExpected
- })
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- reportVideoAbuse,
- getVideoAbusesList,
- updateVideoAbuse,
- deleteVideoAbuse
-}
+++ /dev/null
-import * as request from 'supertest'
-import { VideoBlacklistType } from '../../models/videos'
-import { makeGetRequest } from '..'
-
-function addVideoToBlacklist (
- url: string,
- token: string,
- videoId: number | string,
- reason?: string,
- unfederate?: boolean,
- specialStatus = 204
-) {
- const path = '/api/v1/videos/' + videoId + '/blacklist'
-
- return request(url)
- .post(path)
- .send({ reason, unfederate })
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + token)
- .expect(specialStatus)
-}
-
-function updateVideoBlacklist (url: string, token: string, videoId: number, reason?: string, specialStatus = 204) {
- const path = '/api/v1/videos/' + videoId + '/blacklist'
-
- return request(url)
- .put(path)
- .send({ reason })
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + token)
- .expect(specialStatus)
-}
-
-function removeVideoFromBlacklist (url: string, token: string, videoId: number | string, specialStatus = 204) {
- const path = '/api/v1/videos/' + videoId + '/blacklist'
-
- return request(url)
- .delete(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + token)
- .expect(specialStatus)
-}
-
-function getBlacklistedVideosList (parameters: {
- url: string,
- token: string,
- sort?: string,
- type?: VideoBlacklistType,
- specialStatus?: number
-}) {
- let { url, token, sort, type, specialStatus = 200 } = parameters
- const path = '/api/v1/videos/blacklist/'
-
- const query = { sort, type }
-
- return makeGetRequest({
- url,
- path,
- query,
- token,
- statusCodeExpected: specialStatus
- })
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- addVideoToBlacklist,
- removeVideoFromBlacklist,
- getBlacklistedVideosList,
- updateVideoBlacklist
-}
+++ /dev/null
-import { makeDeleteRequest, makeGetRequest, makeUploadRequest } from '../requests/requests'
-import * as request from 'supertest'
-import * as chai from 'chai'
-import { buildAbsoluteFixturePath } from '../miscs/miscs'
-
-const expect = chai.expect
-
-function createVideoCaption (args: {
- url: string,
- accessToken: string
- videoId: string | number
- language: string
- fixture: string,
- mimeType?: string,
- statusCodeExpected?: number
-}) {
- const path = '/api/v1/videos/' + args.videoId + '/captions/' + args.language
-
- const captionfile = buildAbsoluteFixturePath(args.fixture)
- const captionfileAttach = args.mimeType ? [ captionfile, { contentType: args.mimeType } ] : captionfile
-
- return makeUploadRequest({
- method: 'PUT',
- url: args.url,
- path,
- token: args.accessToken,
- fields: {},
- attaches: {
- captionfile: captionfileAttach
- },
- statusCodeExpected: args.statusCodeExpected || 204
- })
-}
-
-function listVideoCaptions (url: string, videoId: string | number) {
- const path = '/api/v1/videos/' + videoId + '/captions'
-
- return makeGetRequest({
- url,
- path,
- statusCodeExpected: 200
- })
-}
-
-function deleteVideoCaption (url: string, token: string, videoId: string | number, language: string) {
- const path = '/api/v1/videos/' + videoId + '/captions/' + language
-
- return makeDeleteRequest({
- url,
- token,
- path,
- statusCodeExpected: 204
- })
-}
-
-async function testCaptionFile (url: string, captionPath: string, containsString: string) {
- const res = await request(url)
- .get(captionPath)
- .expect(200)
-
- expect(res.text).to.contain(containsString)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- createVideoCaption,
- listVideoCaptions,
- testCaptionFile,
- deleteVideoCaption
-}
+++ /dev/null
-import * as request from 'supertest'
-
-function changeVideoOwnership (url: string, token: string, videoId: number | string, username, expectedStatus = 204) {
- const path = '/api/v1/videos/' + videoId + '/give-ownership'
-
- return request(url)
- .post(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + token)
- .send({ username })
- .expect(expectedStatus)
-}
-
-function getVideoChangeOwnershipList (url: string, token: string) {
- const path = '/api/v1/videos/ownership'
-
- return request(url)
- .get(path)
- .query({ sort: '-createdAt' })
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + token)
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function acceptChangeOwnership (url: string, token: string, ownershipId: string, channelId: number, expectedStatus = 204) {
- const path = '/api/v1/videos/ownership/' + ownershipId + '/accept'
-
- return request(url)
- .post(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + token)
- .send({ channelId })
- .expect(expectedStatus)
-}
-
-function refuseChangeOwnership (url: string, token: string, ownershipId: string, expectedStatus = 204) {
- const path = '/api/v1/videos/ownership/' + ownershipId + '/refuse'
-
- return request(url)
- .post(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + token)
- .expect(expectedStatus)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- changeVideoOwnership,
- getVideoChangeOwnershipList,
- acceptChangeOwnership,
- refuseChangeOwnership
-}
+++ /dev/null
-import * as request from 'supertest'
-import { VideoChannelCreate, VideoChannelUpdate } from '../../models/videos'
-import { updateAvatarRequest } from '../requests/requests'
-import { getMyUserInformation, ServerInfo } from '..'
-import { User } from '../..'
-
-function getVideoChannelsList (url: string, start: number, count: number, sort?: string) {
- const path = '/api/v1/video-channels'
-
- const req = request(url)
- .get(path)
- .query({ start: start })
- .query({ count: count })
-
- if (sort) req.query({ sort })
-
- return req.set('Accept', 'application/json')
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function getAccountVideoChannelsList (url: string, accountName: string, specialStatus = 200) {
- const path = '/api/v1/accounts/' + accountName + '/video-channels'
-
- return request(url)
- .get(path)
- .set('Accept', 'application/json')
- .expect(specialStatus)
- .expect('Content-Type', /json/)
-}
-
-function addVideoChannel (
- url: string,
- token: string,
- videoChannelAttributesArg: VideoChannelCreate,
- expectedStatus = 200
-) {
- const path = '/api/v1/video-channels/'
-
- // Default attributes
- let attributes = {
- displayName: 'my super video channel',
- description: 'my super channel description',
- support: 'my super channel support'
- }
- attributes = Object.assign(attributes, videoChannelAttributesArg)
-
- return request(url)
- .post(path)
- .send(attributes)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + token)
- .expect(expectedStatus)
-}
-
-function updateVideoChannel (
- url: string,
- token: string,
- channelName: string,
- attributes: VideoChannelUpdate,
- expectedStatus = 204
-) {
- const body = {}
- const path = '/api/v1/video-channels/' + channelName
-
- if (attributes.displayName) body['displayName'] = attributes.displayName
- if (attributes.description) body['description'] = attributes.description
- if (attributes.support) body['support'] = attributes.support
-
- return request(url)
- .put(path)
- .send(body)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + token)
- .expect(expectedStatus)
-}
-
-function deleteVideoChannel (url: string, token: string, channelName: string, expectedStatus = 204) {
- const path = '/api/v1/video-channels/' + channelName
-
- return request(url)
- .delete(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + token)
- .expect(expectedStatus)
-}
-
-function getVideoChannel (url: string, channelName: string) {
- const path = '/api/v1/video-channels/' + channelName
-
- return request(url)
- .get(path)
- .set('Accept', 'application/json')
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function updateVideoChannelAvatar (options: {
- url: string,
- accessToken: string,
- fixture: string,
- videoChannelName: string | number
-}) {
-
- const path = '/api/v1/video-channels/' + options.videoChannelName + '/avatar/pick'
-
- return updateAvatarRequest(Object.assign(options, { path }))
-}
-
-function setDefaultVideoChannel (servers: ServerInfo[]) {
- const tasks: Promise<any>[] = []
-
- for (const server of servers) {
- const p = getMyUserInformation(server.url, server.accessToken)
- .then(res => server.videoChannel = (res.body as User).videoChannels[0])
-
- tasks.push(p)
- }
-
- return Promise.all(tasks)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- updateVideoChannelAvatar,
- getVideoChannelsList,
- getAccountVideoChannelsList,
- addVideoChannel,
- updateVideoChannel,
- deleteVideoChannel,
- getVideoChannel,
- setDefaultVideoChannel
-}
+++ /dev/null
-import * as request from 'supertest'
-import { makeDeleteRequest } from '../requests/requests'
-
-function getVideoCommentThreads (url: string, videoId: number | string, start: number, count: number, sort?: string, token?: string) {
- const path = '/api/v1/videos/' + videoId + '/comment-threads'
-
- const req = request(url)
- .get(path)
- .query({ start: start })
- .query({ count: count })
-
- if (sort) req.query({ sort })
- if (token) req.set('Authorization', 'Bearer ' + token)
-
- return req.set('Accept', 'application/json')
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function getVideoThreadComments (url: string, videoId: number | string, threadId: number, token?: string) {
- const path = '/api/v1/videos/' + videoId + '/comment-threads/' + threadId
-
- const req = request(url)
- .get(path)
- .set('Accept', 'application/json')
-
- if (token) req.set('Authorization', 'Bearer ' + token)
-
- return req.expect(200)
- .expect('Content-Type', /json/)
-}
-
-function addVideoCommentThread (url: string, token: string, videoId: number | string, text: string, expectedStatus = 200) {
- const path = '/api/v1/videos/' + videoId + '/comment-threads'
-
- return request(url)
- .post(path)
- .send({ text })
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + token)
- .expect(expectedStatus)
-}
-
-function addVideoCommentReply (
- url: string,
- token: string,
- videoId: number | string,
- inReplyToCommentId: number,
- text: string,
- expectedStatus = 200
-) {
- const path = '/api/v1/videos/' + videoId + '/comments/' + inReplyToCommentId
-
- return request(url)
- .post(path)
- .send({ text })
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + token)
- .expect(expectedStatus)
-}
-
-function deleteVideoComment (
- url: string,
- token: string,
- videoId: number | string,
- commentId: number,
- statusCodeExpected = 204
-) {
- const path = '/api/v1/videos/' + videoId + '/comments/' + commentId
-
- return makeDeleteRequest({
- url,
- path,
- token,
- statusCodeExpected
- })
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- getVideoCommentThreads,
- getVideoThreadComments,
- addVideoCommentThread,
- addVideoCommentReply,
- deleteVideoComment
-}
+++ /dev/null
-import { makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
-
-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 })
-}
-
-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,
- listMyVideosHistory,
- removeMyVideosHistory
-}
+++ /dev/null
-
-import { VideoImportCreate } from '../../models/videos'
-import { makeGetRequest, makeUploadRequest } from '../requests/requests'
-
-function getYoutubeVideoUrl () {
- return 'https://youtu.be/msX3jv1XdvM'
-}
-
-function getMagnetURI () {
- // tslint:disable:max-line-length
- return 'magnet:?xs=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Ftorrents%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.torrent&xt=urn:btih:0f498834733e8057ed5c6f2ee2b4efd8d84a76ee&dn=super+peertube2+video&tr=wss%3A%2F%2Fpeertube2.cpy.re%3A443%2Ftracker%2Fsocket&tr=https%3A%2F%2Fpeertube2.cpy.re%2Ftracker%2Fannounce&ws=https%3A%2F%2Fpeertube2.cpy.re%2Fstatic%2Fwebseed%2Fb209ca00-c8bb-4b2b-b421-1ede169f3dbc-720.mp4'
-}
-
-function getBadVideoUrl () {
- return 'https://download.cpy.re/peertube/bad_video.mp4'
-}
-
-function importVideo (url: string, token: string, attributes: VideoImportCreate) {
- const path = '/api/v1/videos/imports'
-
- let attaches: any = {}
- if (attributes.torrentfile) attaches = { torrentfile: attributes.torrentfile }
-
- return makeUploadRequest({
- url,
- path,
- token,
- attaches,
- fields: attributes,
- statusCodeExpected: 200
- })
-}
-
-function getMyVideoImports (url: string, token: string, sort?: string) {
- const path = '/api/v1/users/me/videos/imports'
-
- const query = {}
- if (sort) query['sort'] = sort
-
- return makeGetRequest({
- url,
- query,
- path,
- token,
- statusCodeExpected: 200
- })
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- getBadVideoUrl,
- getYoutubeVideoUrl,
- importVideo,
- getMagnetURI,
- getMyVideoImports
-}
+++ /dev/null
-import { makeDeleteRequest, makeGetRequest, makePostBodyRequest, makePutBodyRequest, makeUploadRequest } from '../requests/requests'
-import { VideoPlaylistCreate } from '../../models/videos/playlist/video-playlist-create.model'
-import { omit } from 'lodash'
-import { VideoPlaylistUpdate } from '../../models/videos/playlist/video-playlist-update.model'
-import { VideoPlaylistElementCreate } from '../../models/videos/playlist/video-playlist-element-create.model'
-import { VideoPlaylistElementUpdate } from '../../models/videos/playlist/video-playlist-element-update.model'
-import { videoUUIDToId } from './videos'
-import { join } from 'path'
-import { root } from '..'
-import { readdir } from 'fs-extra'
-import { expect } from 'chai'
-import { VideoPlaylistType } from '../../models/videos/playlist/video-playlist-type.model'
-
-function getVideoPlaylistsList (url: string, start: number, count: number, sort?: string) {
- const path = '/api/v1/video-playlists'
-
- const query = {
- start,
- count,
- sort
- }
-
- return makeGetRequest({
- url,
- path,
- query,
- statusCodeExpected: 200
- })
-}
-
-function getVideoChannelPlaylistsList (url: string, videoChannelName: string, start: number, count: number, sort?: string) {
- const path = '/api/v1/video-channels/' + videoChannelName + '/video-playlists'
-
- const query = {
- start,
- count,
- sort
- }
-
- return makeGetRequest({
- url,
- path,
- query,
- statusCodeExpected: 200
- })
-}
-
-function getAccountPlaylistsList (url: string, accountName: string, start: number, count: number, sort?: string) {
- const path = '/api/v1/accounts/' + accountName + '/video-playlists'
-
- const query = {
- start,
- count,
- sort
- }
-
- return makeGetRequest({
- url,
- path,
- query,
- statusCodeExpected: 200
- })
-}
-
-function getAccountPlaylistsListWithToken (
- url: string,
- token: string,
- accountName: string,
- start: number,
- count: number,
- playlistType?: VideoPlaylistType,
- sort?: string
-) {
- const path = '/api/v1/accounts/' + accountName + '/video-playlists'
-
- const query = {
- start,
- count,
- playlistType,
- sort
- }
-
- return makeGetRequest({
- url,
- token,
- path,
- query,
- statusCodeExpected: 200
- })
-}
-
-function getVideoPlaylist (url: string, playlistId: number | string, statusCodeExpected = 200) {
- const path = '/api/v1/video-playlists/' + playlistId
-
- return makeGetRequest({
- url,
- path,
- statusCodeExpected
- })
-}
-
-function getVideoPlaylistWithToken (url: string, token: string, playlistId: number | string, statusCodeExpected = 200) {
- const path = '/api/v1/video-playlists/' + playlistId
-
- return makeGetRequest({
- url,
- token,
- path,
- statusCodeExpected
- })
-}
-
-function deleteVideoPlaylist (url: string, token: string, playlistId: number | string, statusCodeExpected = 204) {
- const path = '/api/v1/video-playlists/' + playlistId
-
- return makeDeleteRequest({
- url,
- path,
- token,
- statusCodeExpected
- })
-}
-
-function createVideoPlaylist (options: {
- url: string,
- token: string,
- playlistAttrs: VideoPlaylistCreate,
- expectedStatus?: number
-}) {
- const path = '/api/v1/video-playlists'
-
- const fields = omit(options.playlistAttrs, 'thumbnailfile')
-
- const attaches = options.playlistAttrs.thumbnailfile
- ? { thumbnailfile: options.playlistAttrs.thumbnailfile }
- : {}
-
- return makeUploadRequest({
- method: 'POST',
- url: options.url,
- path,
- token: options.token,
- fields,
- attaches,
- statusCodeExpected: options.expectedStatus || 200
- })
-}
-
-function updateVideoPlaylist (options: {
- url: string,
- token: string,
- playlistAttrs: VideoPlaylistUpdate,
- playlistId: number | string,
- expectedStatus?: number
-}) {
- const path = '/api/v1/video-playlists/' + options.playlistId
-
- const fields = omit(options.playlistAttrs, 'thumbnailfile')
-
- const attaches = options.playlistAttrs.thumbnailfile
- ? { thumbnailfile: options.playlistAttrs.thumbnailfile }
- : {}
-
- return makeUploadRequest({
- method: 'PUT',
- url: options.url,
- path,
- token: options.token,
- fields,
- attaches,
- statusCodeExpected: options.expectedStatus || 204
- })
-}
-
-async function addVideoInPlaylist (options: {
- url: string,
- token: string,
- playlistId: number | string,
- elementAttrs: VideoPlaylistElementCreate | { videoId: string }
- expectedStatus?: number
-}) {
- options.elementAttrs.videoId = await videoUUIDToId(options.url, options.elementAttrs.videoId)
-
- const path = '/api/v1/video-playlists/' + options.playlistId + '/videos'
-
- return makePostBodyRequest({
- url: options.url,
- path,
- token: options.token,
- fields: options.elementAttrs,
- statusCodeExpected: options.expectedStatus || 200
- })
-}
-
-function updateVideoPlaylistElement (options: {
- url: string,
- token: string,
- playlistId: number | string,
- videoId: number | string,
- elementAttrs: VideoPlaylistElementUpdate,
- expectedStatus?: number
-}) {
- const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.videoId
-
- return makePutBodyRequest({
- url: options.url,
- path,
- token: options.token,
- fields: options.elementAttrs,
- statusCodeExpected: options.expectedStatus || 204
- })
-}
-
-function removeVideoFromPlaylist (options: {
- url: string,
- token: string,
- playlistId: number | string,
- videoId: number | string,
- expectedStatus?: number
-}) {
- const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/' + options.videoId
-
- return makeDeleteRequest({
- url: options.url,
- path,
- token: options.token,
- statusCodeExpected: options.expectedStatus || 204
- })
-}
-
-function reorderVideosPlaylist (options: {
- url: string,
- token: string,
- playlistId: number | string,
- elementAttrs: {
- startPosition: number,
- insertAfterPosition: number,
- reorderLength?: number
- },
- expectedStatus?: number
-}) {
- const path = '/api/v1/video-playlists/' + options.playlistId + '/videos/reorder'
-
- return makePostBodyRequest({
- url: options.url,
- path,
- token: options.token,
- fields: options.elementAttrs,
- statusCodeExpected: options.expectedStatus || 204
- })
-}
-
-async function checkPlaylistFilesWereRemoved (
- playlistUUID: string,
- serverNumber: number,
- directories = [ 'thumbnails' ]
-) {
- const testDirectory = 'test' + serverNumber
-
- for (const directory of directories) {
- const directoryPath = join(root(), testDirectory, directory)
-
- const files = await readdir(directoryPath)
- for (const file of files) {
- expect(file).to.not.contain(playlistUUID)
- }
- }
-}
-
-function getVideoPlaylistPrivacies (url: string) {
- const path = '/api/v1/video-playlists/privacies'
-
- return makeGetRequest({
- url,
- path,
- statusCodeExpected: 200
- })
-}
-
-function doVideosExistInMyPlaylist (url: string, token: string, videoIds: number[]) {
- const path = '/api/v1/users/me/video-playlists/videos-exist'
-
- return makeGetRequest({
- url,
- token,
- path,
- query: { videoIds },
- statusCodeExpected: 200
- })
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- getVideoPlaylistPrivacies,
-
- getVideoPlaylistsList,
- getVideoChannelPlaylistsList,
- getAccountPlaylistsList,
- getAccountPlaylistsListWithToken,
-
- getVideoPlaylist,
- getVideoPlaylistWithToken,
-
- createVideoPlaylist,
- updateVideoPlaylist,
- deleteVideoPlaylist,
-
- addVideoInPlaylist,
- updateVideoPlaylistElement,
- removeVideoFromPlaylist,
-
- reorderVideosPlaylist,
-
- checkPlaylistFilesWereRemoved,
-
- doVideosExistInMyPlaylist
-}
+++ /dev/null
-import { makeRawRequest } from '../requests/requests'
-import { sha256 } from '../../../server/helpers/core-utils'
-import { VideoStreamingPlaylist } from '../../models/videos/video-streaming-playlist.model'
-import { expect } from 'chai'
-
-function getPlaylist (url: string, statusCodeExpected = 200) {
- return makeRawRequest(url, statusCodeExpected)
-}
-
-function getSegment (url: string, statusCodeExpected = 200, range?: string) {
- return makeRawRequest(url, statusCodeExpected, range)
-}
-
-function getSegmentSha256 (url: string, statusCodeExpected = 200) {
- return makeRawRequest(url, statusCodeExpected)
-}
-
-async function checkSegmentHash (
- baseUrlPlaylist: string,
- baseUrlSegment: string,
- videoUUID: string,
- resolution: number,
- hlsPlaylist: VideoStreamingPlaylist
-) {
- const res = await getPlaylist(`${baseUrlPlaylist}/${videoUUID}/${resolution}.m3u8`)
- const playlist = res.text
-
- const videoName = `${videoUUID}-${resolution}-fragmented.mp4`
-
- const matches = /#EXT-X-BYTERANGE:(\d+)@(\d+)/.exec(playlist)
-
- const length = parseInt(matches[1], 10)
- const offset = parseInt(matches[2], 10)
- const range = `${offset}-${offset + length - 1}`
-
- const res2 = await getSegment(`${baseUrlSegment}/${videoUUID}/${videoName}`, 206, `bytes=${range}`)
-
- const resSha = await getSegmentSha256(hlsPlaylist.segmentsSha256Url)
-
- const sha256Server = resSha.body[ videoName ][range]
- expect(sha256(res2.body)).to.equal(sha256Server)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- getPlaylist,
- getSegment,
- getSegmentSha256,
- checkSegmentHash
-}
+++ /dev/null
-/* tslint:disable:no-unused-expression */
-
-import { expect } from 'chai'
-import { pathExists, readdir, readFile } from 'fs-extra'
-import * as parseTorrent from 'parse-torrent'
-import { extname, join } from 'path'
-import * as request from 'supertest'
-import {
- buildAbsoluteFixturePath,
- getMyUserInformation,
- immutableAssign,
- makeGetRequest,
- makePutBodyRequest,
- makeUploadRequest,
- root,
- ServerInfo,
- testImage
-} from '../'
-import * as validator from 'validator'
-import { VideoDetails, VideoPrivacy } from '../../models/videos'
-import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, loadLanguages, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../server/initializers/constants'
-import { dateIsValid, webtorrentAdd } from '../miscs/miscs'
-
-loadLanguages()
-
-type VideoAttributes = {
- name?: string
- category?: number
- licence?: number
- language?: string
- nsfw?: boolean
- commentsEnabled?: boolean
- downloadEnabled?: boolean
- waitTranscoding?: boolean
- description?: string
- originallyPublishedAt?: string
- tags?: string[]
- channelId?: number
- privacy?: VideoPrivacy
- fixture?: string
- thumbnailfile?: string
- previewfile?: string
- scheduleUpdate?: {
- updateAt: string
- privacy?: VideoPrivacy
- }
-}
-
-function getVideoCategories (url: string) {
- const path = '/api/v1/videos/categories'
-
- return makeGetRequest({
- url,
- path,
- statusCodeExpected: 200
- })
-}
-
-function getVideoLicences (url: string) {
- const path = '/api/v1/videos/licences'
-
- return makeGetRequest({
- url,
- path,
- statusCodeExpected: 200
- })
-}
-
-function getVideoLanguages (url: string) {
- const path = '/api/v1/videos/languages'
-
- return makeGetRequest({
- url,
- path,
- statusCodeExpected: 200
- })
-}
-
-function getVideoPrivacies (url: string) {
- const path = '/api/v1/videos/privacies'
-
- return makeGetRequest({
- url,
- path,
- statusCodeExpected: 200
- })
-}
-
-function getVideo (url: string, id: number | string, expectedStatus = 200) {
- const path = '/api/v1/videos/' + id
-
- return request(url)
- .get(path)
- .set('Accept', 'application/json')
- .expect(expectedStatus)
-}
-
-function viewVideo (url: string, id: number | string, expectedStatus = 204, xForwardedFor?: string) {
- const path = '/api/v1/videos/' + id + '/views'
-
- const req = request(url)
- .post(path)
- .set('Accept', 'application/json')
-
- if (xForwardedFor) {
- req.set('X-Forwarded-For', xForwardedFor)
- }
-
- return req.expect(expectedStatus)
-}
-
-function getVideoWithToken (url: string, token: string, id: number | string, expectedStatus = 200) {
- const path = '/api/v1/videos/' + id
-
- return request(url)
- .get(path)
- .set('Authorization', 'Bearer ' + token)
- .set('Accept', 'application/json')
- .expect(expectedStatus)
-}
-
-function getVideoDescription (url: string, descriptionPath: string) {
- return request(url)
- .get(descriptionPath)
- .set('Accept', 'application/json')
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function getVideosList (url: string) {
- const path = '/api/v1/videos'
-
- return request(url)
- .get(path)
- .query({ sort: 'name' })
- .set('Accept', 'application/json')
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function getVideosListWithToken (url: string, token: string, query: { nsfw?: boolean } = {}) {
- const path = '/api/v1/videos'
-
- return request(url)
- .get(path)
- .set('Authorization', 'Bearer ' + token)
- .query(immutableAssign(query, { sort: 'name' }))
- .set('Accept', 'application/json')
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function getLocalVideos (url: string) {
- const path = '/api/v1/videos'
-
- return request(url)
- .get(path)
- .query({ sort: 'name', filter: 'local' })
- .set('Accept', 'application/json')
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function getMyVideos (url: string, accessToken: string, start: number, count: number, sort?: string) {
- const path = '/api/v1/users/me/videos'
-
- const req = request(url)
- .get(path)
- .query({ start: start })
- .query({ count: count })
-
- if (sort) req.query({ sort })
-
- return req.set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function getAccountVideos (
- url: string,
- accessToken: string,
- accountName: string,
- start: number,
- count: number,
- sort?: string,
- query: { nsfw?: boolean } = {}
-) {
- const path = '/api/v1/accounts/' + accountName + '/videos'
-
- return makeGetRequest({
- url,
- path,
- query: immutableAssign(query, {
- start,
- count,
- sort
- }),
- token: accessToken,
- statusCodeExpected: 200
- })
-}
-
-function getVideoChannelVideos (
- url: string,
- accessToken: string,
- videoChannelName: string,
- start: number,
- count: number,
- sort?: string,
- query: { nsfw?: boolean } = {}
-) {
- const path = '/api/v1/video-channels/' + videoChannelName + '/videos'
-
- return makeGetRequest({
- url,
- path,
- query: immutableAssign(query, {
- start,
- count,
- sort
- }),
- token: accessToken,
- statusCodeExpected: 200
- })
-}
-
-function getPlaylistVideos (
- url: string,
- accessToken: string,
- playlistId: number | string,
- start: number,
- count: number,
- query: { nsfw?: boolean } = {}
-) {
- const path = '/api/v1/video-playlists/' + playlistId + '/videos'
-
- return makeGetRequest({
- url,
- path,
- query: immutableAssign(query, {
- start,
- count
- }),
- token: accessToken,
- statusCodeExpected: 200
- })
-}
-
-function getVideosListPagination (url: string, start: number, count: number, sort?: string) {
- const path = '/api/v1/videos'
-
- const req = request(url)
- .get(path)
- .query({ start: start })
- .query({ count: count })
-
- if (sort) req.query({ sort })
-
- return req.set('Accept', 'application/json')
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function getVideosListSort (url: string, sort: string) {
- const path = '/api/v1/videos'
-
- return request(url)
- .get(path)
- .query({ sort: sort })
- .set('Accept', 'application/json')
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function getVideosWithFilters (url: string, query: { tagsAllOf: string[], categoryOneOf: number[] | number }) {
- const path = '/api/v1/videos'
-
- return request(url)
- .get(path)
- .query(query)
- .set('Accept', 'application/json')
- .expect(200)
- .expect('Content-Type', /json/)
-}
-
-function removeVideo (url: string, token: string, id: number | string, expectedStatus = 204) {
- const path = '/api/v1/videos'
-
- return request(url)
- .delete(path + '/' + id)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + token)
- .expect(expectedStatus)
-}
-
-async function checkVideoFilesWereRemoved (
- videoUUID: string,
- serverNumber: number,
- directories = [
- 'redundancy',
- 'videos',
- 'thumbnails',
- 'torrents',
- 'previews',
- 'captions',
- join('playlists', 'hls'),
- join('redundancy', 'hls')
- ]
-) {
- const testDirectory = 'test' + serverNumber
-
- for (const directory of directories) {
- const directoryPath = join(root(), testDirectory, directory)
-
- const directoryExists = await pathExists(directoryPath)
- if (directoryExists === false) continue
-
- const files = await readdir(directoryPath)
- for (const file of files) {
- expect(file).to.not.contain(videoUUID)
- }
- }
-}
-
-async function uploadVideo (url: string, accessToken: string, videoAttributesArg: VideoAttributes, specialStatus = 200) {
- const path = '/api/v1/videos/upload'
- let defaultChannelId = '1'
-
- try {
- const res = await getMyUserInformation(url, accessToken)
- defaultChannelId = res.body.videoChannels[0].id
- } catch (e) { /* empty */ }
-
- // Override default attributes
- const attributes = Object.assign({
- name: 'my super video',
- category: 5,
- licence: 4,
- language: 'zh',
- channelId: defaultChannelId,
- nsfw: true,
- waitTranscoding: false,
- description: 'my super description',
- support: 'my super support text',
- tags: [ 'tag' ],
- privacy: VideoPrivacy.PUBLIC,
- commentsEnabled: true,
- downloadEnabled: true,
- fixture: 'video_short.webm'
- }, videoAttributesArg)
-
- const req = request(url)
- .post(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .field('name', attributes.name)
- .field('nsfw', JSON.stringify(attributes.nsfw))
- .field('commentsEnabled', JSON.stringify(attributes.commentsEnabled))
- .field('downloadEnabled', JSON.stringify(attributes.downloadEnabled))
- .field('waitTranscoding', JSON.stringify(attributes.waitTranscoding))
- .field('privacy', attributes.privacy.toString())
- .field('channelId', attributes.channelId)
-
- if (attributes.description !== undefined) {
- req.field('description', attributes.description)
- }
- if (attributes.language !== undefined) {
- req.field('language', attributes.language.toString())
- }
- if (attributes.category !== undefined) {
- req.field('category', attributes.category.toString())
- }
- if (attributes.licence !== undefined) {
- req.field('licence', attributes.licence.toString())
- }
-
- for (let i = 0; i < attributes.tags.length; i++) {
- req.field('tags[' + i + ']', attributes.tags[i])
- }
-
- if (attributes.thumbnailfile !== undefined) {
- req.attach('thumbnailfile', buildAbsoluteFixturePath(attributes.thumbnailfile))
- }
- if (attributes.previewfile !== undefined) {
- req.attach('previewfile', buildAbsoluteFixturePath(attributes.previewfile))
- }
-
- if (attributes.scheduleUpdate) {
- req.field('scheduleUpdate[updateAt]', attributes.scheduleUpdate.updateAt)
-
- if (attributes.scheduleUpdate.privacy) {
- req.field('scheduleUpdate[privacy]', attributes.scheduleUpdate.privacy)
- }
- }
-
- if (attributes.originallyPublishedAt !== undefined) {
- req.field('originallyPublishedAt', attributes.originallyPublishedAt)
- }
-
- return req.attach('videofile', buildAbsoluteFixturePath(attributes.fixture))
- .expect(specialStatus)
-}
-
-function updateVideo (url: string, accessToken: string, id: number | string, attributes: VideoAttributes, statusCodeExpected = 204) {
- const path = '/api/v1/videos/' + id
- const body = {}
-
- if (attributes.name) body['name'] = attributes.name
- if (attributes.category) body['category'] = attributes.category
- if (attributes.licence) body['licence'] = attributes.licence
- if (attributes.language) body['language'] = attributes.language
- if (attributes.nsfw !== undefined) body['nsfw'] = JSON.stringify(attributes.nsfw)
- if (attributes.commentsEnabled !== undefined) body['commentsEnabled'] = JSON.stringify(attributes.commentsEnabled)
- if (attributes.downloadEnabled !== undefined) body['downloadEnabled'] = JSON.stringify(attributes.downloadEnabled)
- if (attributes.originallyPublishedAt !== undefined) body['originallyPublishedAt'] = attributes.originallyPublishedAt
- if (attributes.description) body['description'] = attributes.description
- if (attributes.tags) body['tags'] = attributes.tags
- if (attributes.privacy) body['privacy'] = attributes.privacy
- if (attributes.channelId) body['channelId'] = attributes.channelId
- if (attributes.scheduleUpdate) body['scheduleUpdate'] = attributes.scheduleUpdate
-
- // Upload request
- if (attributes.thumbnailfile || attributes.previewfile) {
- const attaches: any = {}
- if (attributes.thumbnailfile) attaches.thumbnailfile = attributes.thumbnailfile
- if (attributes.previewfile) attaches.previewfile = attributes.previewfile
-
- return makeUploadRequest({
- url,
- method: 'PUT',
- path,
- token: accessToken,
- fields: body,
- attaches,
- statusCodeExpected
- })
- }
-
- return makePutBodyRequest({
- url,
- path,
- fields: body,
- token: accessToken,
- statusCodeExpected
- })
-}
-
-function rateVideo (url: string, accessToken: string, id: number, rating: string, specialStatus = 204) {
- const path = '/api/v1/videos/' + id + '/rate'
-
- return request(url)
- .put(path)
- .set('Accept', 'application/json')
- .set('Authorization', 'Bearer ' + accessToken)
- .send({ rating })
- .expect(specialStatus)
-}
-
-function parseTorrentVideo (server: ServerInfo, videoUUID: string, resolution: number) {
- return new Promise<any>((res, rej) => {
- const torrentName = videoUUID + '-' + resolution + '.torrent'
- const torrentPath = join(root(), 'test' + server.serverNumber, 'torrents', torrentName)
- readFile(torrentPath, (err, data) => {
- if (err) return rej(err)
-
- return res(parseTorrent(data))
- })
- })
-}
-
-async function completeVideoCheck (
- url: string,
- video: any,
- attributes: {
- name: string
- category: number
- licence: number
- language: string
- nsfw: boolean
- commentsEnabled: boolean
- downloadEnabled: boolean
- description: string
- publishedAt?: string
- support: string
- originallyPublishedAt?: string,
- account: {
- name: string
- host: string
- }
- isLocal: boolean
- tags: string[]
- privacy: number
- likes?: number
- dislikes?: number
- duration: number
- channel: {
- displayName: string
- name: string
- description
- isLocal: boolean
- }
- fixture: string
- files: {
- resolution: number
- size: number
- }[],
- thumbnailfile?: string
- previewfile?: string
- }
-) {
- if (!attributes.likes) attributes.likes = 0
- if (!attributes.dislikes) attributes.dislikes = 0
-
- expect(video.name).to.equal(attributes.name)
- expect(video.category.id).to.equal(attributes.category)
- expect(video.category.label).to.equal(attributes.category !== null ? VIDEO_CATEGORIES[attributes.category] : 'Misc')
- expect(video.licence.id).to.equal(attributes.licence)
- expect(video.licence.label).to.equal(attributes.licence !== null ? VIDEO_LICENCES[attributes.licence] : 'Unknown')
- expect(video.language.id).to.equal(attributes.language)
- expect(video.language.label).to.equal(attributes.language !== null ? VIDEO_LANGUAGES[attributes.language] : 'Unknown')
- expect(video.privacy.id).to.deep.equal(attributes.privacy)
- expect(video.privacy.label).to.deep.equal(VIDEO_PRIVACIES[attributes.privacy])
- expect(video.nsfw).to.equal(attributes.nsfw)
- expect(video.description).to.equal(attributes.description)
- expect(video.account.id).to.be.a('number')
- expect(video.account.uuid).to.be.a('string')
- expect(video.account.host).to.equal(attributes.account.host)
- expect(video.account.name).to.equal(attributes.account.name)
- expect(video.channel.displayName).to.equal(attributes.channel.displayName)
- expect(video.channel.name).to.equal(attributes.channel.name)
- expect(video.likes).to.equal(attributes.likes)
- expect(video.dislikes).to.equal(attributes.dislikes)
- expect(video.isLocal).to.equal(attributes.isLocal)
- expect(video.duration).to.equal(attributes.duration)
- expect(dateIsValid(video.createdAt)).to.be.true
- expect(dateIsValid(video.publishedAt)).to.be.true
- expect(dateIsValid(video.updatedAt)).to.be.true
-
- if (attributes.publishedAt) {
- expect(video.publishedAt).to.equal(attributes.publishedAt)
- }
-
- if (attributes.originallyPublishedAt) {
- expect(video.originallyPublishedAt).to.equal(attributes.originallyPublishedAt)
- } else {
- expect(video.originallyPublishedAt).to.be.null
- }
-
- const res = await getVideo(url, video.uuid)
- const videoDetails: VideoDetails = res.body
-
- expect(videoDetails.files).to.have.lengthOf(attributes.files.length)
- expect(videoDetails.tags).to.deep.equal(attributes.tags)
- expect(videoDetails.account.name).to.equal(attributes.account.name)
- expect(videoDetails.account.host).to.equal(attributes.account.host)
- expect(video.channel.displayName).to.equal(attributes.channel.displayName)
- expect(video.channel.name).to.equal(attributes.channel.name)
- expect(videoDetails.channel.host).to.equal(attributes.account.host)
- expect(videoDetails.channel.isLocal).to.equal(attributes.channel.isLocal)
- expect(dateIsValid(videoDetails.channel.createdAt.toString())).to.be.true
- expect(dateIsValid(videoDetails.channel.updatedAt.toString())).to.be.true
- expect(videoDetails.commentsEnabled).to.equal(attributes.commentsEnabled)
- expect(videoDetails.downloadEnabled).to.equal(attributes.downloadEnabled)
-
- for (const attributeFile of attributes.files) {
- const file = videoDetails.files.find(f => f.resolution.id === attributeFile.resolution)
- expect(file).not.to.be.undefined
-
- let extension = extname(attributes.fixture)
- // Transcoding enabled on server 2, extension will always be .mp4
- if (attributes.account.host === 'localhost:9002') extension = '.mp4'
-
- const magnetUri = file.magnetUri
- expect(file.magnetUri).to.have.lengthOf.above(2)
- expect(file.torrentUrl).to.equal(`http://${attributes.account.host}/static/torrents/${videoDetails.uuid}-${file.resolution.id}.torrent`)
- expect(file.fileUrl).to.equal(`http://${attributes.account.host}/static/webseed/${videoDetails.uuid}-${file.resolution.id}${extension}`)
- expect(file.resolution.id).to.equal(attributeFile.resolution)
- expect(file.resolution.label).to.equal(attributeFile.resolution + 'p')
-
- const minSize = attributeFile.size - ((10 * attributeFile.size) / 100)
- const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
- expect(file.size,
- 'File size for resolution ' + file.resolution.label + ' outside confidence interval (' + minSize + '> size <' + maxSize + ')')
- .to.be.above(minSize).and.below(maxSize)
-
- {
- await testImage(url, attributes.thumbnailfile || attributes.fixture, videoDetails.thumbnailPath)
- }
-
- if (attributes.previewfile) {
- await testImage(url, attributes.previewfile, videoDetails.previewPath)
- }
-
- const torrent = await webtorrentAdd(magnetUri, true)
- expect(torrent.files).to.be.an('array')
- expect(torrent.files.length).to.equal(1)
- expect(torrent.files[0].path).to.exist.and.to.not.equal('')
- }
-}
-
-async function videoUUIDToId (url: string, id: number | string) {
- if (validator.isUUID('' + id) === false) return id
-
- const res = await getVideo(url, id)
- return res.body.id
-}
-
-async function uploadVideoAndGetId (options: { server: ServerInfo, videoName: string, nsfw?: boolean, token?: string }) {
- const videoAttrs: any = { name: options.videoName }
- if (options.nsfw) videoAttrs.nsfw = options.nsfw
-
- const res = await uploadVideo(options.server.url, options.token || options.server.accessToken, videoAttrs)
-
- return { id: res.body.video.id, uuid: res.body.video.uuid }
-}
-
-// ---------------------------------------------------------------------------
-
-export {
- getVideoDescription,
- getVideoCategories,
- getVideoLicences,
- videoUUIDToId,
- getVideoPrivacies,
- getVideoLanguages,
- getMyVideos,
- getAccountVideos,
- getVideoChannelVideos,
- getVideo,
- getVideoWithToken,
- getVideosList,
- getVideosListPagination,
- getVideosListSort,
- removeVideo,
- getVideosListWithToken,
- uploadVideo,
- getVideosWithFilters,
- updateVideo,
- rateVideo,
- viewVideo,
- parseTorrentVideo,
- getLocalVideos,
- completeVideoCheck,
- checkVideoFilesWereRemoved,
- getPlaylistVideos,
- uploadVideoAndGetId
-}