diff options
-rw-r--r-- | server/controllers/api/video-playlist.ts | 10 | ||||
-rw-r--r-- | server/initializers/constants.ts | 2 | ||||
-rw-r--r-- | server/initializers/migrations/0345-video-playlists.ts | 86 | ||||
-rw-r--r-- | server/tests/api/videos/video-playlists.ts | 10 |
4 files changed, 105 insertions, 3 deletions
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 { | |||
13 | import { VideoChannelModel } from '../../models/video/video-channel' | 13 | import { VideoChannelModel } from '../../models/video/video-channel' |
14 | import { videoPlaylistsSortValidator } from '../../middlewares/validators' | 14 | import { videoPlaylistsSortValidator } from '../../middlewares/validators' |
15 | import { buildNSFWFilter, createReqFiles, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' | 15 | import { buildNSFWFilter, createReqFiles, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils' |
16 | import { CONFIG, MIMETYPES, sequelizeTypescript, THUMBNAILS_SIZE } from '../../initializers' | 16 | import { CONFIG, MIMETYPES, sequelizeTypescript, THUMBNAILS_SIZE, VIDEO_PLAYLIST_PRIVACIES } from '../../initializers' |
17 | import { logger } from '../../helpers/logger' | 17 | import { logger } from '../../helpers/logger' |
18 | import { resetSequelizeInstance } from '../../helpers/database-utils' | 18 | import { resetSequelizeInstance } from '../../helpers/database-utils' |
19 | import { VideoPlaylistModel } from '../../models/video/video-playlist' | 19 | import { VideoPlaylistModel } from '../../models/video/video-playlist' |
@@ -46,6 +46,8 @@ const reqThumbnailFile = createReqFiles([ 'thumbnailfile' ], MIMETYPES.IMAGE.MIM | |||
46 | 46 | ||
47 | const videoPlaylistRouter = express.Router() | 47 | const videoPlaylistRouter = express.Router() |
48 | 48 | ||
49 | videoPlaylistRouter.get('/privacies', listVideoPlaylistPrivacies) | ||
50 | |||
49 | videoPlaylistRouter.get('/', | 51 | videoPlaylistRouter.get('/', |
50 | paginationValidator, | 52 | paginationValidator, |
51 | videoPlaylistsSortValidator, | 53 | videoPlaylistsSortValidator, |
@@ -121,6 +123,10 @@ export { | |||
121 | 123 | ||
122 | // --------------------------------------------------------------------------- | 124 | // --------------------------------------------------------------------------- |
123 | 125 | ||
126 | function listVideoPlaylistPrivacies (req: express.Request, res: express.Response) { | ||
127 | res.json(VIDEO_PLAYLIST_PRIVACIES) | ||
128 | } | ||
129 | |||
124 | async function listVideoPlaylists (req: express.Request, res: express.Response) { | 130 | async function listVideoPlaylists (req: express.Request, res: express.Response) { |
125 | const serverActor = await getServerActor() | 131 | const serverActor = await getServerActor() |
126 | const resultList = await VideoPlaylistModel.listForApi({ | 132 | const resultList = await VideoPlaylistModel.listForApi({ |
@@ -153,7 +159,7 @@ async function addVideoPlaylist (req: express.Request, res: express.Response) { | |||
153 | 159 | ||
154 | videoPlaylist.url = getVideoPlaylistActivityPubUrl(videoPlaylist) // We use the UUID, so set the URL after building the object | 160 | videoPlaylist.url = getVideoPlaylistActivityPubUrl(videoPlaylist) // We use the UUID, so set the URL after building the object |
155 | 161 | ||
156 | if (videoPlaylistInfo.videoChannelId !== undefined) { | 162 | if (videoPlaylistInfo.videoChannelId) { |
157 | const videoChannel = res.locals.videoChannel as VideoChannelModel | 163 | const videoChannel = res.locals.videoChannel as VideoChannelModel |
158 | 164 | ||
159 | videoPlaylist.videoChannelId = videoChannel.id | 165 | 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') | |||
18 | 18 | ||
19 | // --------------------------------------------------------------------------- | 19 | // --------------------------------------------------------------------------- |
20 | 20 | ||
21 | const LAST_MIGRATION_VERSION = 340 | 21 | const LAST_MIGRATION_VERSION = 345 |
22 | 22 | ||
23 | // --------------------------------------------------------------------------- | 23 | // --------------------------------------------------------------------------- |
24 | 24 | ||
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 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | import { CONFIG } from '../constants' | ||
3 | import { VideoPlaylistPrivacy, VideoPlaylistType } from '../../../shared/models/videos' | ||
4 | import * as uuidv4 from 'uuid/v4' | ||
5 | |||
6 | async function up (utils: { | ||
7 | transaction: Sequelize.Transaction, | ||
8 | queryInterface: Sequelize.QueryInterface, | ||
9 | sequelize: Sequelize.Sequelize | ||
10 | }): Promise<void> { | ||
11 | const transaction = utils.transaction | ||
12 | |||
13 | { | ||
14 | const query = ` | ||
15 | CREATE TABLE IF NOT EXISTS "videoPlaylist" | ||
16 | ( | ||
17 | "id" SERIAL, | ||
18 | "name" VARCHAR(255) NOT NULL, | ||
19 | "description" VARCHAR(255), | ||
20 | "privacy" INTEGER NOT NULL, | ||
21 | "url" VARCHAR(2000) NOT NULL, | ||
22 | "uuid" UUID NOT NULL, | ||
23 | "type" INTEGER NOT NULL DEFAULT 1, | ||
24 | "ownerAccountId" INTEGER NOT NULL REFERENCES "account" ("id") ON DELETE CASCADE ON UPDATE CASCADE, | ||
25 | "videoChannelId" INTEGER REFERENCES "videoChannel" ("id") ON DELETE CASCADE ON UPDATE CASCADE, | ||
26 | "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, | ||
27 | "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, | ||
28 | PRIMARY KEY ("id") | ||
29 | );` | ||
30 | await utils.sequelize.query(query, { transaction }) | ||
31 | } | ||
32 | |||
33 | { | ||
34 | const query = ` | ||
35 | CREATE TABLE IF NOT EXISTS "videoPlaylistElement" | ||
36 | ( | ||
37 | "id" SERIAL, | ||
38 | "url" VARCHAR(2000) NOT NULL, | ||
39 | "position" INTEGER NOT NULL DEFAULT 1, | ||
40 | "startTimestamp" INTEGER, | ||
41 | "stopTimestamp" INTEGER, | ||
42 | "videoPlaylistId" INTEGER NOT NULL REFERENCES "videoPlaylist" ("id") ON DELETE CASCADE ON UPDATE CASCADE, | ||
43 | "videoId" INTEGER NOT NULL REFERENCES "video" ("id") ON DELETE CASCADE ON UPDATE CASCADE, | ||
44 | "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, | ||
45 | "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, | ||
46 | PRIMARY KEY ("id") | ||
47 | );` | ||
48 | |||
49 | await utils.sequelize.query(query, { transaction }) | ||
50 | } | ||
51 | |||
52 | { | ||
53 | const userQuery = 'SELECT "username" FROM "user";' | ||
54 | const userResult = await utils.sequelize.query(userQuery, { transaction, type: Sequelize.QueryTypes.SELECT }) | ||
55 | const usernames = userResult.map(r => r.username) | ||
56 | |||
57 | for (const username of usernames) { | ||
58 | const uuid = uuidv4() | ||
59 | |||
60 | const baseUrl = CONFIG.WEBSERVER.URL + '/video-playlists/' + uuid | ||
61 | const query = ` | ||
62 | INSERT INTO "videoPlaylist" ("url", "uuid", "name", "privacy", "type", "ownerAccountId", "createdAt", "updatedAt") | ||
63 | SELECT '${baseUrl}' AS "url", | ||
64 | '${uuid}' AS "uuid", | ||
65 | 'Watch later' AS "name", | ||
66 | ${VideoPlaylistPrivacy.PRIVATE} AS "privacy", | ||
67 | ${VideoPlaylistType.WATCH_LATER} AS "type", | ||
68 | "account"."id" AS "ownerAccountId", | ||
69 | NOW() as "createdAt", | ||
70 | NOW() as "updatedAt" | ||
71 | FROM "user" INNER JOIN "account" ON "user"."id" = "account"."userId" | ||
72 | WHERE "user"."username" = '${username}'` | ||
73 | |||
74 | await utils.sequelize.query(query, { transaction }) | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | function down (options) { | ||
80 | throw new Error('Not implemented.') | ||
81 | } | ||
82 | |||
83 | export { | ||
84 | up, | ||
85 | down | ||
86 | } | ||
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 { | |||
18 | getPlaylistVideos, | 18 | getPlaylistVideos, |
19 | getVideoChannelPlaylistsList, | 19 | getVideoChannelPlaylistsList, |
20 | getVideoPlaylist, | 20 | getVideoPlaylist, |
21 | getVideoPlaylistPrivacies, | ||
21 | getVideoPlaylistsList, | 22 | getVideoPlaylistsList, |
22 | getVideoPlaylistWithToken, | 23 | getVideoPlaylistWithToken, |
23 | killallServers, | 24 | killallServers, |
@@ -95,6 +96,15 @@ describe('Test video playlists', function () { | |||
95 | await waitJobs(servers) | 96 | await waitJobs(servers) |
96 | }) | 97 | }) |
97 | 98 | ||
99 | it('Should list video playlist privacies', async function () { | ||
100 | const res = await getVideoPlaylistPrivacies(servers[0].url) | ||
101 | |||
102 | const privacies = res.body | ||
103 | expect(Object.keys(privacies)).to.have.length.at.least(3) | ||
104 | |||
105 | expect(privacies[3]).to.equal('Private') | ||
106 | }) | ||
107 | |||
98 | it('Should list watch later playlist', async function () { | 108 | it('Should list watch later playlist', async function () { |
99 | const url = servers[ 0 ].url | 109 | const url = servers[ 0 ].url |
100 | const accessToken = servers[ 0 ].accessToken | 110 | const accessToken = servers[ 0 ].accessToken |