aboutsummaryrefslogtreecommitdiffhomepage
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/benchmark.ts101
-rwxr-xr-xscripts/parse-log.ts23
-rwxr-xr-xscripts/prune-storage.ts10
-rw-r--r--scripts/regenerate-thumbnails.ts68
-rwxr-xr-xscripts/upgrade.sh3
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'
15program 15program
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
20const options = program.opts() 22const 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
146function 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
11import * as Bluebird from 'bluebird' 11import * as Bluebird from 'bluebird'
12import { getUUIDFromFilename } from '../server/helpers/utils' 12import { getUUIDFromFilename } from '../server/helpers/utils'
13import { ThumbnailModel } from '../server/models/video/thumbnail' 13import { ThumbnailModel } from '../server/models/video/thumbnail'
14import { AvatarModel } from '../server/models/avatar/avatar' 14import { ActorImageModel } from '../server/models/account/actor-image'
15import { uniq, values } from 'lodash' 15import { uniq, values } from 'lodash'
16import { ThumbnailType } from '@shared/models' 16import { 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
110async function doesAvatarExist (file: string) { 110async 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
116async function doesRedundancyExist (file: string) { 116async 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 @@
1import { registerTSPaths } from '../server/helpers/register-ts-paths'
2registerTSPaths()
3
4import * as Bluebird from 'bluebird'
5import * as program from 'commander'
6import { pathExists, remove } from 'fs-extra'
7import { generateImageFilename, processImage } from '@server/helpers/image-utils'
8import { THUMBNAILS_SIZE } from '@server/initializers/constants'
9import { VideoModel } from '@server/models/video/video'
10import { MVideo } from '@server/types/models'
11import { initDatabaseModels } from '@server/initializers/database'
12
13program
14 .description('Regenerate local thumbnails using preview files')
15 .parse(process.argv)
16
17run()
18 .then(() => process.exit(0))
19 .catch(err => console.error(err))
20
21async 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
32async 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"
44else 45else
45 echo "pg_dump not found. Cannot make a SQL backup!" 46 echo "pg_dump not found. Cannot make a SQL backup!"
46fi 47fi