diff options
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/benchmark.ts | 101 | ||||
-rwxr-xr-x | scripts/parse-log.ts | 23 | ||||
-rwxr-xr-x | scripts/prune-storage.ts | 10 | ||||
-rw-r--r-- | scripts/regenerate-thumbnails.ts | 68 | ||||
-rwxr-xr-x | scripts/upgrade.sh | 3 |
5 files changed, 138 insertions, 67 deletions
diff --git a/scripts/benchmark.ts b/scripts/benchmark.ts index 45b2a7a79..0cadb36d9 100644 --- a/scripts/benchmark.ts +++ b/scripts/benchmark.ts | |||
@@ -50,126 +50,102 @@ async function run () { | |||
50 | title: 'AP - account peertube', | 50 | title: 'AP - account peertube', |
51 | path: '/accounts/peertube', | 51 | path: '/accounts/peertube', |
52 | headers: buildAPHeader(), | 52 | headers: buildAPHeader(), |
53 | expecter: (client, statusCode) => { | 53 | expecter: (body, status) => { |
54 | const body = client.resData[0].body | 54 | return status === 200 && body.startsWith('{"type":') |
55 | |||
56 | return statusCode === 200 && body.startsWith('{"type":') | ||
57 | } | 55 | } |
58 | }, | 56 | }, |
59 | { | 57 | { |
60 | title: 'AP - video', | 58 | title: 'AP - video', |
61 | path: '/videos/watch/' + video.uuid, | 59 | path: '/videos/watch/' + video.uuid, |
62 | headers: buildAPHeader(), | 60 | headers: buildAPHeader(), |
63 | expecter: (client, statusCode) => { | 61 | expecter: (body, status) => { |
64 | const body = client.resData[0].body | 62 | return status === 200 && body.startsWith('{"type":"Video"') |
65 | |||
66 | return statusCode === 200 && body.startsWith('{"type":"Video"') | ||
67 | } | 63 | } |
68 | }, | 64 | }, |
69 | { | 65 | { |
70 | title: 'Misc - webfinger peertube', | 66 | title: 'Misc - webfinger peertube', |
71 | path: '/.well-known/webfinger?resource=acct:peertube@' + server.host, | 67 | path: '/.well-known/webfinger?resource=acct:peertube@' + server.host, |
72 | expecter: (client, statusCode) => { | 68 | expecter: (body, status) => { |
73 | const body = client.resData[0].body | 69 | return status === 200 && body.startsWith('{"subject":') |
74 | |||
75 | return statusCode === 200 && body.startsWith('{"subject":') | ||
76 | } | 70 | } |
77 | }, | 71 | }, |
78 | { | 72 | { |
79 | title: 'API - unread notifications', | 73 | title: 'API - unread notifications', |
80 | path: '/api/v1/users/me/notifications?start=0&count=0&unread=true', | 74 | path: '/api/v1/users/me/notifications?start=0&count=0&unread=true', |
81 | headers: buildAuthorizationHeader(), | 75 | headers: buildAuthorizationHeader(), |
82 | expecter: (_client, statusCode) => { | 76 | expecter: (_body, status) => { |
83 | return statusCode === 200 | 77 | return status === 200 |
84 | } | 78 | } |
85 | }, | 79 | }, |
86 | { | 80 | { |
87 | title: 'API - me', | 81 | title: 'API - me', |
88 | path: '/api/v1/users/me', | 82 | path: '/api/v1/users/me', |
89 | headers: buildAuthorizationHeader(), | 83 | headers: buildAuthorizationHeader(), |
90 | expecter: (client, statusCode) => { | 84 | expecter: (body, status) => { |
91 | const body = client.resData[0].body | 85 | return status === 200 && body.startsWith('{"id":') |
92 | |||
93 | return statusCode === 200 && body.startsWith('{"id":') | ||
94 | } | 86 | } |
95 | }, | 87 | }, |
96 | { | 88 | { |
97 | title: 'API - videos list', | 89 | title: 'API - videos list', |
98 | path: '/api/v1/videos', | 90 | path: '/api/v1/videos', |
99 | expecter: (client, statusCode) => { | 91 | expecter: (body, status) => { |
100 | const body = client.resData[0].body | 92 | return status === 200 && body.startsWith('{"total":10') |
101 | |||
102 | return statusCode === 200 && body.startsWith('{"total":10') | ||
103 | } | 93 | } |
104 | }, | 94 | }, |
105 | { | 95 | { |
106 | title: 'API - video get', | 96 | title: 'API - video get', |
107 | path: '/api/v1/videos/' + video.uuid, | 97 | path: '/api/v1/videos/' + video.uuid, |
108 | expecter: (client, statusCode) => { | 98 | expecter: (body, status) => { |
109 | const body = client.resData[0].body | 99 | return status === 200 && body.startsWith('{"id":') |
110 | |||
111 | return statusCode === 200 && body.startsWith('{"id":') | ||
112 | } | 100 | } |
113 | }, | 101 | }, |
114 | { | 102 | { |
115 | title: 'API - video captions', | 103 | title: 'API - video captions', |
116 | path: '/api/v1/videos/' + video.uuid + '/captions', | 104 | path: '/api/v1/videos/' + video.uuid + '/captions', |
117 | expecter: (client, statusCode) => { | 105 | expecter: (body, status) => { |
118 | const body = client.resData[0].body | 106 | return status === 200 && body.startsWith('{"total":4') |
119 | |||
120 | return statusCode === 200 && body.startsWith('{"total":4') | ||
121 | } | 107 | } |
122 | }, | 108 | }, |
123 | { | 109 | { |
124 | title: 'API - video threads', | 110 | title: 'API - video threads', |
125 | path: '/api/v1/videos/' + video.uuid + '/comment-threads', | 111 | path: '/api/v1/videos/' + video.uuid + '/comment-threads', |
126 | expecter: (client, statusCode) => { | 112 | expecter: (body, status) => { |
127 | const body = client.resData[0].body | 113 | return status === 200 && body.startsWith('{"total":10') |
128 | |||
129 | return statusCode === 200 && body.startsWith('{"total":10') | ||
130 | } | 114 | } |
131 | }, | 115 | }, |
132 | { | 116 | { |
133 | title: 'API - video replies', | 117 | title: 'API - video replies', |
134 | path: '/api/v1/videos/' + video.uuid + '/comment-threads/' + threadId, | 118 | path: '/api/v1/videos/' + video.uuid + '/comment-threads/' + threadId, |
135 | expecter: (client, statusCode) => { | 119 | expecter: (body, status) => { |
136 | const body = client.resData[0].body | 120 | return status === 200 && body.startsWith('{"comment":{') |
137 | |||
138 | return statusCode === 200 && body.startsWith('{"comment":{') | ||
139 | } | 121 | } |
140 | }, | 122 | }, |
141 | { | 123 | { |
142 | title: 'HTML - video watch', | 124 | title: 'HTML - video watch', |
143 | path: '/videos/watch/' + video.uuid, | 125 | path: '/videos/watch/' + video.uuid, |
144 | expecter: (client, statusCode) => { | 126 | expecter: (body, status) => { |
145 | const body = client.resData[0].body | 127 | return status === 200 && body.includes('<title>my super') |
146 | |||
147 | return statusCode === 200 && body.includes('<title>my super') | ||
148 | } | 128 | } |
149 | }, | 129 | }, |
150 | { | 130 | { |
151 | title: 'HTML - video embed', | 131 | title: 'HTML - video embed', |
152 | path: '/videos/embed/' + video.uuid, | 132 | path: '/videos/embed/' + video.uuid, |
153 | expecter: (client, statusCode) => { | 133 | expecter: (body, status) => { |
154 | const body = client.resData[0].body | 134 | return status === 200 && body.includes('embed') |
155 | |||
156 | return statusCode === 200 && body.includes('embed') | ||
157 | } | 135 | } |
158 | }, | 136 | }, |
159 | { | 137 | { |
160 | title: 'HTML - homepage', | 138 | title: 'HTML - homepage', |
161 | path: '/', | 139 | path: '/', |
162 | expecter: (_client, statusCode) => { | 140 | expecter: (_body, status) => { |
163 | return statusCode === 200 | 141 | return status === 200 |
164 | } | 142 | } |
165 | }, | 143 | }, |
166 | { | 144 | { |
167 | title: 'API - config', | 145 | title: 'API - config', |
168 | path: '/api/v1/config', | 146 | path: '/api/v1/config', |
169 | expecter: (client, statusCode) => { | 147 | expecter: (body, status) => { |
170 | const body = client.resData[0].body | 148 | return status === 200 && body.startsWith('{"instance":') |
171 | |||
172 | return statusCode === 200 && body.startsWith('{"instance":') | ||
173 | } | 149 | } |
174 | } | 150 | } |
175 | ] | 151 | ] |
@@ -197,24 +173,27 @@ function runBenchmark (options: { | |||
197 | const { path, expecter, headers } = options | 173 | const { path, expecter, headers } = options |
198 | 174 | ||
199 | return new Promise((res, rej) => { | 175 | return new Promise((res, rej) => { |
200 | const instance = autocannon({ | 176 | autocannon({ |
201 | url: server.url + path, | 177 | url: server.url + path, |
202 | connections: 20, | 178 | connections: 20, |
203 | headers, | 179 | headers, |
204 | pipelining: 1, | 180 | pipelining: 1, |
205 | duration: 10 | 181 | duration: 10, |
182 | requests: [ | ||
183 | { | ||
184 | onResponse: (status, body) => { | ||
185 | if (expecter(body, status) !== true) { | ||
186 | console.error('Expected result failed.', { body, status }) | ||
187 | throw new Error('Invalid expectation') | ||
188 | } | ||
189 | } | ||
190 | } | ||
191 | ] | ||
206 | }, (err, result) => { | 192 | }, (err, result) => { |
207 | if (err) return rej(err) | 193 | if (err) return rej(err) |
208 | 194 | ||
209 | return res(result) | 195 | return res(result) |
210 | }) | 196 | }) |
211 | |||
212 | instance.on('response', (client, statusCode) => { | ||
213 | if (expecter(client, statusCode) !== true) { | ||
214 | console.error('Expected result failed.', { data: client.resData }) | ||
215 | process.exit(-1) | ||
216 | } | ||
217 | }) | ||
218 | }) | 197 | }) |
219 | } | 198 | } |
220 | 199 | ||
diff --git a/scripts/parse-log.ts b/scripts/parse-log.ts index 3679dab74..5f4480c88 100755 --- a/scripts/parse-log.ts +++ b/scripts/parse-log.ts | |||
@@ -15,6 +15,8 @@ import { format as sqlFormat } from 'sql-formatter' | |||
15 | program | 15 | program |
16 | .option('-l, --level [level]', 'Level log (debug/info/warn/error)') | 16 | .option('-l, --level [level]', 'Level log (debug/info/warn/error)') |
17 | .option('-f, --files [file...]', 'Files to parse. If not provided, the script will parse the latest log file from config)') | 17 | .option('-f, --files [file...]', 'Files to parse. If not provided, the script will parse the latest log file from config)') |
18 | .option('-t, --tags [tags...]', 'Display only lines with these tags') | ||
19 | .option('-nt, --not-tags [tags...]', 'Donrt display lines containing these tags') | ||
18 | .parse(process.argv) | 20 | .parse(process.argv) |
19 | 21 | ||
20 | const options = program.opts() | 22 | const options = program.opts() |
@@ -24,6 +26,7 @@ const excludedKeys = { | |||
24 | message: true, | 26 | message: true, |
25 | splat: true, | 27 | splat: true, |
26 | timestamp: true, | 28 | timestamp: true, |
29 | tags: true, | ||
27 | label: true, | 30 | label: true, |
28 | sql: true | 31 | sql: true |
29 | } | 32 | } |
@@ -93,6 +96,14 @@ function run () { | |||
93 | rl.on('line', line => { | 96 | rl.on('line', line => { |
94 | try { | 97 | try { |
95 | const log = JSON.parse(line) | 98 | const log = JSON.parse(line) |
99 | if (options.tags && !containsTags(log.tags, options.tags)) { | ||
100 | return | ||
101 | } | ||
102 | |||
103 | if (options.notTags && containsTags(log.tags, options.notTags)) { | ||
104 | return | ||
105 | } | ||
106 | |||
96 | // Don't know why but loggerFormat does not remove splat key | 107 | // Don't know why but loggerFormat does not remove splat key |
97 | Object.assign(log, { splat: undefined }) | 108 | Object.assign(log, { splat: undefined }) |
98 | 109 | ||
@@ -131,3 +142,15 @@ function toTimeFormat (time: string) { | |||
131 | 142 | ||
132 | return new Date(timestamp).toISOString() | 143 | return new Date(timestamp).toISOString() |
133 | } | 144 | } |
145 | |||
146 | function containsTags (loggerTags: string[], optionsTags: string[]) { | ||
147 | if (!loggerTags) return false | ||
148 | |||
149 | for (const lt of loggerTags) { | ||
150 | for (const ot of optionsTags) { | ||
151 | if (lt === ot) return true | ||
152 | } | ||
153 | } | ||
154 | |||
155 | return false | ||
156 | } | ||
diff --git a/scripts/prune-storage.ts b/scripts/prune-storage.ts index dcb1fcf90..bdfb335c6 100755 --- a/scripts/prune-storage.ts +++ b/scripts/prune-storage.ts | |||
@@ -11,7 +11,7 @@ import { VideoRedundancyModel } from '../server/models/redundancy/video-redundan | |||
11 | import * as Bluebird from 'bluebird' | 11 | import * as Bluebird from 'bluebird' |
12 | import { getUUIDFromFilename } from '../server/helpers/utils' | 12 | import { getUUIDFromFilename } from '../server/helpers/utils' |
13 | import { ThumbnailModel } from '../server/models/video/thumbnail' | 13 | import { ThumbnailModel } from '../server/models/video/thumbnail' |
14 | import { AvatarModel } from '../server/models/avatar/avatar' | 14 | import { ActorImageModel } from '../server/models/account/actor-image' |
15 | import { uniq, values } from 'lodash' | 15 | import { uniq, values } from 'lodash' |
16 | import { ThumbnailType } from '@shared/models' | 16 | import { ThumbnailType } from '@shared/models' |
17 | 17 | ||
@@ -43,7 +43,7 @@ async function run () { | |||
43 | await pruneDirectory(CONFIG.STORAGE.PREVIEWS_DIR, doesThumbnailExist(true, ThumbnailType.PREVIEW)), | 43 | await pruneDirectory(CONFIG.STORAGE.PREVIEWS_DIR, doesThumbnailExist(true, ThumbnailType.PREVIEW)), |
44 | await pruneDirectory(CONFIG.STORAGE.THUMBNAILS_DIR, doesThumbnailExist(false, ThumbnailType.MINIATURE)), | 44 | await pruneDirectory(CONFIG.STORAGE.THUMBNAILS_DIR, doesThumbnailExist(false, ThumbnailType.MINIATURE)), |
45 | 45 | ||
46 | await pruneDirectory(CONFIG.STORAGE.AVATARS_DIR, doesAvatarExist) | 46 | await pruneDirectory(CONFIG.STORAGE.ACTOR_IMAGES, doesActorImageExist) |
47 | ) | 47 | ) |
48 | 48 | ||
49 | const tmpFiles = await readdir(CONFIG.STORAGE.TMP_DIR) | 49 | const tmpFiles = await readdir(CONFIG.STORAGE.TMP_DIR) |
@@ -107,10 +107,10 @@ function doesThumbnailExist (keepOnlyOwned: boolean, type: ThumbnailType) { | |||
107 | } | 107 | } |
108 | } | 108 | } |
109 | 109 | ||
110 | async function doesAvatarExist (file: string) { | 110 | async function doesActorImageExist (file: string) { |
111 | const avatar = await AvatarModel.loadByName(file) | 111 | const image = await ActorImageModel.loadByName(file) |
112 | 112 | ||
113 | return !!avatar | 113 | return !!image |
114 | } | 114 | } |
115 | 115 | ||
116 | async function doesRedundancyExist (file: string) { | 116 | async function doesRedundancyExist (file: string) { |
diff --git a/scripts/regenerate-thumbnails.ts b/scripts/regenerate-thumbnails.ts new file mode 100644 index 000000000..b95343c0b --- /dev/null +++ b/scripts/regenerate-thumbnails.ts | |||
@@ -0,0 +1,68 @@ | |||
1 | import { registerTSPaths } from '../server/helpers/register-ts-paths' | ||
2 | registerTSPaths() | ||
3 | |||
4 | import * as Bluebird from 'bluebird' | ||
5 | import * as program from 'commander' | ||
6 | import { pathExists, remove } from 'fs-extra' | ||
7 | import { generateImageFilename, processImage } from '@server/helpers/image-utils' | ||
8 | import { THUMBNAILS_SIZE } from '@server/initializers/constants' | ||
9 | import { VideoModel } from '@server/models/video/video' | ||
10 | import { MVideo } from '@server/types/models' | ||
11 | import { initDatabaseModels } from '@server/initializers/database' | ||
12 | |||
13 | program | ||
14 | .description('Regenerate local thumbnails using preview files') | ||
15 | .parse(process.argv) | ||
16 | |||
17 | run() | ||
18 | .then(() => process.exit(0)) | ||
19 | .catch(err => console.error(err)) | ||
20 | |||
21 | async function run () { | ||
22 | await initDatabaseModels(true) | ||
23 | |||
24 | const videos = await VideoModel.listLocal() | ||
25 | |||
26 | await Bluebird.map(videos, v => { | ||
27 | return processVideo(v) | ||
28 | .catch(err => console.error('Cannot process video %s.', v.url, err)) | ||
29 | }, { concurrency: 20 }) | ||
30 | } | ||
31 | |||
32 | async function processVideo (videoArg: MVideo) { | ||
33 | const video = await VideoModel.loadWithFiles(videoArg.id) | ||
34 | |||
35 | console.log('Processing video %s.', video.name) | ||
36 | |||
37 | const thumbnail = video.getMiniature() | ||
38 | const preview = video.getPreview() | ||
39 | |||
40 | const previewPath = preview.getPath() | ||
41 | |||
42 | if (!await pathExists(previewPath)) { | ||
43 | throw new Error(`Preview ${previewPath} does not exist on disk`) | ||
44 | } | ||
45 | |||
46 | const size = { | ||
47 | width: THUMBNAILS_SIZE.width, | ||
48 | height: THUMBNAILS_SIZE.height | ||
49 | } | ||
50 | |||
51 | const oldPath = thumbnail.getPath() | ||
52 | |||
53 | // Update thumbnail | ||
54 | thumbnail.filename = generateImageFilename() | ||
55 | thumbnail.width = size.width | ||
56 | thumbnail.height = size.height | ||
57 | |||
58 | const thumbnailPath = thumbnail.getPath() | ||
59 | await processImage(previewPath, thumbnailPath, size, true) | ||
60 | |||
61 | // Save new attributes | ||
62 | await thumbnail.save() | ||
63 | |||
64 | // Remove old thumbnail | ||
65 | await remove(oldPath) | ||
66 | |||
67 | // Don't federate, remote instances will refresh the thumbnails after a while | ||
68 | } | ||
diff --git a/scripts/upgrade.sh b/scripts/upgrade.sh index f5f3219af..64a7b18fd 100755 --- a/scripts/upgrade.sh +++ b/scripts/upgrade.sh | |||
@@ -39,8 +39,9 @@ then | |||
39 | DB_PASS=$(node -e "console.log(require('js-yaml').load(fs.readFileSync('$PEERTUBE_PATH/config/production.yaml', 'utf8'))['database']['password'])") | 39 | DB_PASS=$(node -e "console.log(require('js-yaml').load(fs.readFileSync('$PEERTUBE_PATH/config/production.yaml', 'utf8'))['database']['password'])") |
40 | DB_HOST=$(node -e "console.log(require('js-yaml').load(fs.readFileSync('$PEERTUBE_PATH/config/production.yaml', 'utf8'))['database']['hostname'])") | 40 | DB_HOST=$(node -e "console.log(require('js-yaml').load(fs.readFileSync('$PEERTUBE_PATH/config/production.yaml', 'utf8'))['database']['hostname'])") |
41 | DB_SUFFIX=$(node -e "console.log(require('js-yaml').load(fs.readFileSync('$PEERTUBE_PATH/config/production.yaml', 'utf8'))['database']['suffix'])") | 41 | DB_SUFFIX=$(node -e "console.log(require('js-yaml').load(fs.readFileSync('$PEERTUBE_PATH/config/production.yaml', 'utf8'))['database']['suffix'])") |
42 | DB_NAME=$(node -e "console.log(require('js-yaml').load(fs.readFileSync('$PEERTUBE_PATH/config/production.yaml', 'utf8'))['database']['name'] || '')") | ||
42 | mkdir -p $PEERTUBE_PATH/backup | 43 | mkdir -p $PEERTUBE_PATH/backup |
43 | PGPASSWORD=$DB_PASS pg_dump -U $DB_USER -h $DB_HOST -F c "peertube${DB_SUFFIX}" -f "$SQL_BACKUP_PATH" | 44 | PGPASSWORD=$DB_PASS pg_dump -U $DB_USER -h $DB_HOST -F c "${DB_NAME:-'peertube${DB_SUFFIX}'}" -f "$SQL_BACKUP_PATH" |
44 | else | 45 | else |
45 | echo "pg_dump not found. Cannot make a SQL backup!" | 46 | echo "pg_dump not found. Cannot make a SQL backup!" |
46 | fi | 47 | fi |