diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts index 2700e8dc7..145764d35 100644 --- a/server/controllers/api/video-playlist.ts +++ b/server/controllers/api/video-playlist.ts @@ -13,7 +13,7 @@ import { import { VideoChannelModel } from '../../models/video/video-channel' import { videoPlaylistsSortValidator } from '../../middlewares/validators' import { buildNSFWFilter, createReqFiles, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' -import { CONFIG, MIMETYPES, sequelizeTypescript, THUMBNAILS_SIZE } from '../../initializers' +import { CONFIG, MIMETYPES, sequelizeTypescript, THUMBNAILS_SIZE, VIDEO_PLAYLIST_PRIVACIES } from '../../initializers' import { logger } from '../../helpers/logger' import { resetSequelizeInstance } from '../../helpers/database-utils' import { VideoPlaylistModel } from '../../models/video/video-playlist' @@ -46,6 +46,8 @@ const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIM const videoPlaylistRouter = express.Router() +videoPlaylistRouter.get('/privacies', listVideoPlaylistPrivacies) + videoPlaylistRouter.get('/', paginationValidator, videoPlaylistsSortValidator, @@ -121,6 +123,10 @@ export { // --------------------------------------------------------------------------- +function listVideoPlaylistPrivacies (req: express.Request, res: express.Response) { + res.json(VIDEO_PLAYLIST_PRIVACIES) +} + async function listVideoPlaylists (req: express.Request, res: express.Response) { const serverActor = await getServerActor() const resultList = await VideoPlaylistModel.listForApi({ @@ -153,7 +159,7 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) { videoPlaylist.url = getVideoPlaylistActivityPubUrl(videoPlaylist) // We use the UUID, so set the URL after building the object - if (videoPlaylistInfo.videoChannelId !== undefined) { + if (videoPlaylistInfo.videoChannelId) { const videoChannel = res.locals.videoChannel as VideoChannelModel videoPlaylist.videoChannelId = videoChannel.id diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index cabb0681a..4cbb87ab5 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts @@ -18,7 +18,7 @@ let config: IConfig = require('config') // --------------------------------------------------------------------------- -const LAST_MIGRATION_VERSION = 340 +const LAST_MIGRATION_VERSION = 345 // --------------------------------------------------------------------------- diff --git a/server/initializers/migrations/0345-video-playlists.ts b/server/initializers/migrations/0345-video-playlists.ts new file mode 100644 index 000000000..11670b11d --- /dev/null +++ b/server/initializers/migrations/0345-video-playlists.ts @@ -0,0 +1,86 @@ +import * as Sequelize from 'sequelize' +import { CONFIG } from '../constants' +import { VideoPlaylistPrivacy, VideoPlaylistType } from '../../../shared/models/videos' +import * as uuidv4 from 'uuid/v4' + +async function up (utils: { + transaction: Sequelize.Transaction, + queryInterface: Sequelize.QueryInterface, + sequelize: Sequelize.Sequelize +}): Promise { + const transaction = utils.transaction + + { + const query = ` +CREATE TABLE IF NOT EXISTS "videoPlaylist" +( + "id" SERIAL, + "name" VARCHAR(255) NOT NULL, + "description" VARCHAR(255), + "privacy" INTEGER NOT NULL, + "url" VARCHAR(2000) NOT NULL, + "uuid" UUID NOT NULL, + "type" INTEGER NOT NULL DEFAULT 1, + "ownerAccountId" INTEGER NOT NULL REFERENCES "account" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + "videoChannelId" INTEGER REFERENCES "videoChannel" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, + "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, + PRIMARY KEY ("id") +);` + await utils.sequelize.query(query, { transaction }) + } + + { + const query = ` +CREATE TABLE IF NOT EXISTS "videoPlaylistElement" +( + "id" SERIAL, + "url" VARCHAR(2000) NOT NULL, + "position" INTEGER NOT NULL DEFAULT 1, + "startTimestamp" INTEGER, + "stopTimestamp" INTEGER, + "videoPlaylistId" INTEGER NOT NULL REFERENCES "videoPlaylist" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + "videoId" INTEGER NOT NULL REFERENCES "video" ("id") ON DELETE CASCADE ON UPDATE CASCADE, + "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, + "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, + PRIMARY KEY ("id") +);` + + await utils.sequelize.query(query, { transaction }) + } + + { + const userQuery = 'SELECT "username" FROM "user";' + const userResult = await utils.sequelize.query(userQuery, { transaction, type: Sequelize.QueryTypes.SELECT }) + const usernames = userResult.map(r => r.username) + + for (const username of usernames) { + const uuid = uuidv4() + + const baseUrl = CONFIG.WEBSERVER.URL + '/video-playlists/' + uuid + const query = ` + INSERT INTO "videoPlaylist" ("url", "uuid", "name", "privacy", "type", "ownerAccountId", "createdAt", "updatedAt") + SELECT '${baseUrl}' AS "url", + '${uuid}' AS "uuid", + 'Watch later' AS "name", + ${VideoPlaylistPrivacy.PRIVATE} AS "privacy", + ${VideoPlaylistType.WATCH_LATER} AS "type", + "account"."id" AS "ownerAccountId", + NOW() as "createdAt", + NOW() as "updatedAt" + FROM "user" INNER JOIN "account" ON "user"."id" = "account"."userId" + WHERE "user"."username" = '${username}'` + + await utils.sequelize.query(query, { transaction }) + } + } +} + +function down (options) { + throw new Error('Not implemented.') +} + +export { + up, + down +} diff --git a/server/tests/api/videos/video-playlists.ts b/server/tests/api/videos/video-playlists.ts index 7dd1563fc..baa2b3b8c 100644 --- a/server/tests/api/videos/video-playlists.ts +++ b/server/tests/api/videos/video-playlists.ts @@ -18,6 +18,7 @@ import { getPlaylistVideos, getVideoChannelPlaylistsList, getVideoPlaylist, + getVideoPlaylistPrivacies, getVideoPlaylistsList, getVideoPlaylistWithToken, killallServers, @@ -95,6 +96,15 @@ describe('Test video playlists', function () { await waitJobs(servers) }) + it('Should list video playlist privacies', async function () { + const res = await getVideoPlaylistPrivacies(servers[0].url) + + const privacies = res.body + expect(Object.keys(privacies)).to.have.length.at.least(3) + + expect(privacies[3]).to.equal('Private') + }) + it('Should list watch later playlist', async function () { const url = servers[ 0 ].url const accessToken = servers[ 0 ].accessToken