Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
cf843c3f12 | ||
![]() |
6966f37c4b | ||
![]() |
4fd8d34175 | ||
![]() |
c6047e1573 | ||
![]() |
f51bafb3fa | ||
![]() |
f9e710e7d4 | ||
![]() |
7a953a6b2f |
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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 []
|
||||||
}
|
}
|
||||||
|
|
|
@ -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' } })
|
||||||
|
|
|
@ -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 ])
|
||||||
})
|
})
|
||||||
|
|
|
@ -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' }
|
||||||
|
])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.')
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 })
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue
Block a user