Compare commits

..

7 Commits

Author SHA1 Message Date
Alejandro
cf843c3f12 Text corrections 2023-12-06 08:45:06 +01:00
Alejandro
6966f37c4b Corrected reference to production.yaml 2023-12-06 08:45:06 +01:00
Chocobozzz
4fd8d34175
Ensure user is owned by plugin before updating it 2023-12-06 08:43:19 +01:00
Chocobozzz
c6047e1573
Add missing logger tags 2023-11-29 16:31:29 +01:00
Chocobozzz
f51bafb3fa
Fix chapters tests 2023-11-29 15:01:46 +01:00
Chocobozzz
f9e710e7d4
Fix chapters import 2023-11-29 14:12:13 +01:00
Julien Rabier
7a953a6b2f Fix upgrade.sh when Peertube is installed outside the standard path (fixes #6063) 2023-11-29 11:03:44 +01:00
13 changed files with 71 additions and 26 deletions

View File

@ -8,7 +8,7 @@
* We've made some modifications in v6.0.0 IMPORTANT NOTES, so if you upgrade from PeerTube v6.0.0: * We've made some modifications in v6.0.0 IMPORTANT NOTES, so if you upgrade from PeerTube v6.0.0:
* Ensure `location = /api/v1/videos/upload-resumable {` has been replaced by `location ~ ^/api/v1/videos/(upload-resumable|([^/]+/source/replace-resumable))$ {` in your nginx configuration * Ensure `location = /api/v1/videos/upload-resumable {` has been replaced by `location ~ ^/api/v1/videos/(upload-resumable|([^/]+/source/replace-resumable))$ {` in your nginx configuration
* Ensure you updated `storage.web_videos` configuration value to use `web-videos/` directory name * Ensure you updated `storage.web_videos` configuration value to use `web-videos/` directory name
* Ensure your directory name on filesystem is the same as `storage.web_videos` configuration * Ensure your directory name on filesystem is the same as `storage.web_videos` configuration value: directory on filesystem must be renamed from `videos/` to `web-videos/` to represent the value of `storage.web_videos`
### Bug fixes ### Bug fixes
@ -43,7 +43,7 @@ We have many important notes in this release. We know it's a pain for sysadmin,
* Directory on filesystem must be **renamed** from `videos/` to `web-videos/` to represent the value of `storage.web_videos` * Directory on filesystem must be **renamed** from `videos/` to `web-videos/` to represent the value of `storage.web_videos`
* Classic installation: `sudo -u peertube mv '/var/www/peertube/storage/videos/' '/var/www/peertube/storage/web-videos/'` * Classic installation: `sudo -u peertube mv '/var/www/peertube/storage/videos/' '/var/www/peertube/storage/web-videos/'`
* Docker installation: `mv '/path-to-docker-installation/docker-volume/data/videos/' '/path-to-docker-installation/docker-volume/data/web-videos/'` * Docker installation: `mv '/path-to-docker-installation/docker-volume/data/videos/' '/path-to-docker-installation/docker-volume/data/web-videos/'`
* `transcoding.webtorrent` must be **renamed** to `transcoding.web_videos`: https://github.com/Chocobozzz/PeerTube/blob/develop/config/production.yaml.example#L522 * `transcoding.webtorrent` must be **renamed** to `transcoding.web_videos`: https://github.com/Chocobozzz/PeerTube/blob/develop/config/production.yaml.example#L532
* `object_storage.videos` must be **renamed** to `object_storage.web_videos`. The value of `object_storage.web_videos.bucket_name` doesn't need to be changed: https://github.com/Chocobozzz/PeerTube/blob/develop/config/production.yaml.example#L223 * `object_storage.videos` must be **renamed** to `object_storage.web_videos`. The value of `object_storage.web_videos.bucket_name` doesn't need to be changed: https://github.com/Chocobozzz/PeerTube/blob/develop/config/production.yaml.example#L223
* `storage.storyboards` must be **added**: https://github.com/Chocobozzz/PeerTube/blob/develop/config/production.yaml.example#L157 * `storage.storyboards` must be **added**: https://github.com/Chocobozzz/PeerTube/blob/develop/config/production.yaml.example#L157
@ -61,7 +61,7 @@ We have many important notes in this release. We know it's a pain for sysadmin,
* `location ~ ^(/static/(webseed|streaming-playlists)/private/)|^/download {` must be updated to `location ~ ^(/static/(webseed|web-videos|streaming-playlists)/private/)|^/download {` * `location ~ ^(/static/(webseed|streaming-playlists)/private/)|^/download {` must be updated to `location ~ ^(/static/(webseed|web-videos|streaming-playlists)/private/)|^/download {`
* `location ~ ^/static/(webseed|redundancy|streaming-playlists)/ {` must be updated to `location ~ ^/static/(webseed|web-videos|redundancy|streaming-playlists)/ {` * `location ~ ^/static/(webseed|redundancy|streaming-playlists)/ {` must be updated to `location ~ ^/static/(webseed|web-videos|redundancy|streaming-playlists)/ {`
* Tracing requires `--experimental-loader=@opentelemetry/instrumentation/hook.mjs` node option: https://github.com/Chocobozzz/PeerTube/blob/develop/config/production.yaml.example#L263 * Tracing requires `--experimental-loader=@opentelemetry/instrumentation/hook.mjs` node option: https://github.com/Chocobozzz/PeerTube/blob/develop/config/production.yaml.example#L264
#### Developers important notes #### Developers important notes

View File

@ -2,7 +2,7 @@ import { timeToInt, timecodeRegexString } from '../common/date.js'
const timecodeRegex = new RegExp(`^(${timecodeRegexString})\\s`) const timecodeRegex = new RegExp(`^(${timecodeRegexString})\\s`)
export function parseChapters (text: string) { export function parseChapters (text: string, maxTitleLength: number) {
if (!text) return [] if (!text) return []
const lines = text.split(/\r?\n|\r|\n/g) const lines = text.split(/\r?\n|\r|\n/g)
@ -25,8 +25,11 @@ export function parseChapters (text: string) {
const timecode = timeToInt(timecodeText) const timecode = timeToInt(timecodeText)
const title = line.replace(matched[0], '') const title = line.replace(matched[0], '')
chapters.push({ timecode, title }) chapters.push({ timecode, title: title.slice(0, maxTitleLength) })
} }
return chapters // Only consider chapters if there are more than one
if (chapters.length > 1) return chapters
return []
} }

View File

@ -178,13 +178,13 @@ describe('Test video chapters', function () {
checkChapters(chapters) checkChapters(chapters)
} }
await servers[0].videos.update({ id: video.uuid, attributes: { description: '00:01 chapter 1' } }) await servers[0].videos.update({ id: video.uuid, attributes: { description: '00:01 chapter 1\n00:03 chapter 2' } })
await waitJobs(servers) await waitJobs(servers)
for (const server of servers) { for (const server of servers) {
const { chapters } = await server.chapters.list({ videoId: video.uuid }) const { chapters } = await server.chapters.list({ videoId: video.uuid })
expect(chapters).to.deep.equal([ { timecode: 1, title: 'chapter 1' } ]) expect(chapters).to.deep.equal([ { timecode: 1, title: 'chapter 1' }, { timecode: 3, title: 'chapter 2' } ])
} }
await servers[0].videos.update({ id: video.uuid, attributes: { description: 'null description' } }) await servers[0].videos.update({ id: video.uuid, attributes: { description: 'null description' } })

View File

@ -242,6 +242,29 @@ describe('Test id and pass auth plugins', function () {
expect(laguna.pluginAuth).to.equal('peertube-plugin-test-id-pass-auth-two') expect(laguna.pluginAuth).to.equal('peertube-plugin-test-id-pass-auth-two')
}) })
it('Should not update a user if not owned by the plugin auth', async function () {
{
await server.users.update({ userId: lagunaId, videoQuota: 43000, password: 'coucou', pluginAuth: null })
const body = await server.users.get({ userId: lagunaId })
expect(body.videoQuota).to.equal(43000)
expect(body.pluginAuth).to.be.null
}
{
await server.login.login({
user: { username: 'laguna', password: 'laguna password' },
expectedStatus: HttpStatusCode.BAD_REQUEST_400
})
}
{
const body = await server.users.get({ userId: lagunaId })
expect(body.videoQuota).to.equal(43000)
expect(body.pluginAuth).to.be.null
}
})
after(async function () { after(async function () {
await cleanupTests([ server ]) await cleanupTests([ server ])
}) })

View File

@ -221,25 +221,38 @@ describe('Parse semantic version string', function () {
describe('Extract chapters', function () { describe('Extract chapters', function () {
it('Should not extract chapters', function () { it('Should not extract chapters', function () {
expect(parseChapters('my super description\nno?')).to.deep.equal([]) expect(parseChapters('my super description\nno?', 100)).to.deep.equal([])
expect(parseChapters('m00:00 super description\nno?')).to.deep.equal([]) expect(parseChapters('m00:00 super description\nno?', 100)).to.deep.equal([])
expect(parseChapters('00:00super description\nno?')).to.deep.equal([]) expect(parseChapters('00:00super description\nno?', 100)).to.deep.equal([])
expect(parseChapters('my super description\n'.repeat(10) + ' * list1\n * list 2\n * list 3')).to.deep.equal([]) expect(parseChapters('my super description\n'.repeat(10) + ' * list1\n * list 2\n * list 3', 100)).to.deep.equal([])
expect(parseChapters('3 Hello coucou', 100)).to.deep.equal([])
expect(parseChapters('00:00 coucou', 100)).to.deep.equal([])
}) })
it('Should extract chapters', function () { it('Should extract chapters', function () {
expect(parseChapters('00:00 coucou')).to.deep.equal([ { timecode: 0, title: 'coucou' } ]) expect(parseChapters('00:00 coucou\n00:05 hello', 100)).to.deep.equal([
expect(parseChapters('my super description\n\n00:01:30 chapter 1\n00:01:35 chapter 2')).to.deep.equal([ { timecode: 0, title: 'coucou' },
{ timecode: 5, title: 'hello' }
])
expect(parseChapters('my super description\n\n00:01:30 chapter 1\n00:01:35 chapter 2', 100)).to.deep.equal([
{ timecode: 90, title: 'chapter 1' }, { timecode: 90, title: 'chapter 1' },
{ timecode: 95, title: 'chapter 2' } { timecode: 95, title: 'chapter 2' }
]) ])
expect(parseChapters('hi\n\n00:01:30 chapter 1\n00:01:35 chapter 2\nhi')).to.deep.equal([ expect(parseChapters('hi\n\n00:01:30 chapter 1\n00:01:35 chapter 2\nhi', 100)).to.deep.equal([
{ timecode: 90, title: 'chapter 1' }, { timecode: 90, title: 'chapter 1' },
{ timecode: 95, title: 'chapter 2' } { timecode: 95, title: 'chapter 2' }
]) ])
expect(parseChapters('hi\n\n00:01:30 chapter 1\n00:01:35 chapter 2\nhi\n00:01:40 chapter 3')).to.deep.equal([ expect(parseChapters('hi\n\n00:01:30 chapter 1\n00:01:35 chapter 2\nhi\n00:01:40 chapter 3', 100)).to.deep.equal([
{ timecode: 90, title: 'chapter 1' }, { timecode: 90, title: 'chapter 1' },
{ timecode: 95, title: 'chapter 2' } { timecode: 95, title: 'chapter 2' }
]) ])
}) })
it('Should respect the max length option', function () {
expect(parseChapters('my super description\n\n00:01:30 chapter 1\n00:01:35 chapter 2', 3)).to.deep.equal([
{ timecode: 90, title: 'cha' },
{ timecode: 95, title: 'cha' }
])
})
}) })

View File

@ -4,4 +4,4 @@ set -eu
# Backward path compatibility now upgrade.sh is in dist/scripts since v6 # Backward path compatibility now upgrade.sh is in dist/scripts since v6
/bin/sh ../dist/scripts/upgrade.sh /bin/sh ../dist/scripts/upgrade.sh $1

View File

@ -89,8 +89,11 @@ async function getUser (usernameOrEmail?: string, password?: string, bypassLogin
let user = await UserModel.loadByEmail(bypassLogin.user.email) let user = await UserModel.loadByEmail(bypassLogin.user.email)
if (!user) user = await createUserFromExternal(bypassLogin.pluginName, bypassLogin.user) if (!user) {
else user = await updateUserFromExternal(user, bypassLogin.user, bypassLogin.userUpdater) user = await createUserFromExternal(bypassLogin.pluginName, bypassLogin.user)
} else if (user.pluginAuth === bypassLogin.pluginName) {
user = await updateUserFromExternal(user, bypassLogin.user, bypassLogin.userUpdater)
}
// Cannot create a user // Cannot create a user
if (!user) throw new AccessDeniedError('Cannot create such user: an actor with that name already exists.') if (!user) throw new AccessDeniedError('Cannot create such user: an actor with that name already exists.')

View File

@ -38,7 +38,7 @@ export async function moveToJob (options: {
} }
if (video.VideoStreamingPlaylists) { if (video.VideoStreamingPlaylists) {
logger.debug('Moving HLS playlist of %s.', video.uuid) logger.debug('Moving HLS playlist of %s.', video.uuid, lTags)
await moveHLSFiles(video) await moveHLSFiles(video)
} }

View File

@ -5,6 +5,7 @@ import { VideoChapterModel } from '@server/models/video/video-chapter.js'
import { MVideoImmutable } from '@server/types/models/index.js' import { MVideoImmutable } from '@server/types/models/index.js'
import { Transaction } from 'sequelize' import { Transaction } from 'sequelize'
import { InternalEventEmitter } from './internal-event-emitter.js' import { InternalEventEmitter } from './internal-event-emitter.js'
import { CONSTRAINTS_FIELDS } from '@server/initializers/constants.js'
const lTags = loggerTagsFactory('video', 'chapters') const lTags = loggerTagsFactory('video', 'chapters')
@ -44,7 +45,7 @@ export async function replaceChaptersFromDescriptionIfNeeded (options: {
}) { }) {
const { transaction, video, newDescription, oldDescription = '' } = options const { transaction, video, newDescription, oldDescription = '' } = options
const chaptersFromOldDescription = sortBy(parseChapters(oldDescription), 'timecode') const chaptersFromOldDescription = sortBy(parseChapters(oldDescription, CONSTRAINTS_FIELDS.VIDEO_CHAPTERS.TITLE.max), 'timecode')
const existingChapters = await VideoChapterModel.listChaptersOfVideo(video.id, transaction) const existingChapters = await VideoChapterModel.listChaptersOfVideo(video.id, transaction)
logger.debug( logger.debug(
@ -54,7 +55,7 @@ export async function replaceChaptersFromDescriptionIfNeeded (options: {
// Then we can update chapters from the new description // Then we can update chapters from the new description
if (areSameChapters(chaptersFromOldDescription, existingChapters)) { if (areSameChapters(chaptersFromOldDescription, existingChapters)) {
const chaptersFromNewDescription = sortBy(parseChapters(newDescription), 'timecode') const chaptersFromNewDescription = sortBy(parseChapters(newDescription, CONSTRAINTS_FIELDS.VIDEO_CHAPTERS.TITLE.max), 'timecode')
if (chaptersFromOldDescription.length === 0 && chaptersFromNewDescription.length === 0) return false if (chaptersFromOldDescription.length === 0 && chaptersFromNewDescription.length === 0) return false
await replaceChapters({ video, chapters: chaptersFromNewDescription, transaction }) await replaceChapters({ video, chapters: chaptersFromNewDescription, transaction })

View File

@ -873,6 +873,8 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
} }
isPasswordMatch (password: string) { isPasswordMatch (password: string) {
if (!password || !this.password) return false
return comparePassword(password, this.password) return comparePassword(password, this.password)
} }

View File

@ -9766,10 +9766,10 @@ components:
description: P2P peers connected (doesn't include WebSeed peers) description: P2P peers connected (doesn't include WebSeed peers)
resolutionChanges: resolutionChanges:
type: number type: number
description: How many resolution changes occured since the last metric creation description: How many resolution changes occurred since the last metric creation
errors: errors:
type: number type: number
description: How many errors occured since the last metric creation description: How many errors occurred since the last metric creation
downloadedBytesP2P: downloadedBytesP2P:
type: number type: number
description: How many bytes were downloaded with P2P since the last metric creation description: How many bytes were downloaded with P2P since the last metric creation

View File

@ -870,7 +870,7 @@ function register ({ registerClientRoute }) {
} }
``` ```
You can then access the page on `/p/my-super/route` (please note the additionnal `/p/` in the path). You can then access the page on `/p/my-super/route` (please note the additional `/p/` in the path).
### Publishing ### Publishing

View File

@ -185,7 +185,7 @@ peertube-runner list-registered
## Server tools ## Server tools
Server tools are scripts that interect directly with the code of your PeerTube instance. Server tools are scripts that interact directly with the code of your PeerTube instance.
They must be run on the server, in `peertube-latest` directory. They must be run on the server, in `peertube-latest` directory.
### Parse logs ### Parse logs