From 97a5d4b408679a2c690708bc5583e92a33d07187 Mon Sep 17 00:00:00 2001 From: Alecks Gates Date: Tue, 23 May 2023 23:11:10 -0500 Subject: [PATCH 1/6] Initial WIP commit to add tag to Podcast RSS feeds --- package-lock.json | 55 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + server/main.ts | 31 +++++++++++++++++++++++++- 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index ee89dee5..851da2d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "validate-color": "^2.2.1" }, "devDependencies": { + "@peertube/feed": "^5.1.0", "@peertube/peertube-types": "^5.1.0", "@tsconfig/node12": "^1.0.9", "@types/async": "^3.2.9", @@ -2557,6 +2558,18 @@ "node": ">=14" } }, + "node_modules/@peertube/feed": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@peertube/feed/-/feed-5.1.0.tgz", + "integrity": "sha512-ggwIbjxh4oc1aAGYV7ZxtIpiEIGq3Rkg6FxvOSrk/EPZ76rExoIJCjKeSyd4zb/sGkyKldy+bGs1OUUVidWWTQ==", + "dev": true, + "dependencies": { + "xml-js": "^1.6.11" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/@peertube/peertube-types": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@peertube/peertube-types/-/peertube-types-5.1.0.tgz", @@ -8804,6 +8817,12 @@ "node": ">=8.9.0" } }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, "node_modules/semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -10441,6 +10460,18 @@ } } }, + "node_modules/xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "dev": true, + "dependencies": { + "sax": "^1.2.4" + }, + "bin": { + "xml-js": "bin/cli.js" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -12611,6 +12642,15 @@ "integrity": "sha512-hO+bdeGOlJwqowUBoZF5LyP3ORUFOP1G0GRv8N45W/cztXbT2ZEXaAzfokRS9Xc9FWmYrDj32mF6SzH6wuoIyA==", "dev": true }, + "@peertube/feed": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@peertube/feed/-/feed-5.1.0.tgz", + "integrity": "sha512-ggwIbjxh4oc1aAGYV7ZxtIpiEIGq3Rkg6FxvOSrk/EPZ76rExoIJCjKeSyd4zb/sGkyKldy+bGs1OUUVidWWTQ==", + "dev": true, + "requires": { + "xml-js": "^1.6.11" + } + }, "@peertube/peertube-types": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@peertube/peertube-types/-/peertube-types-5.1.0.tgz", @@ -17285,6 +17325,12 @@ "chokidar": ">=3.0.0 <4.0.0" } }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -18531,6 +18577,15 @@ "dev": true, "requires": {} }, + "xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "dev": true, + "requires": { + "sax": "^1.2.4" + } + }, "xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index c65b7ea9..cb9c628c 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "validate-color": "^2.2.1" }, "devDependencies": { + "@peertube/feed": "^5.1.0", "@peertube/peertube-types": "^5.1.0", "@tsconfig/node12": "^1.0.9", "@types/async": "^3.2.9", diff --git a/server/main.ts b/server/main.ts index beb1e95d..a2c6e5fa 100644 --- a/server/main.ts +++ b/server/main.ts @@ -1,4 +1,4 @@ -import type { RegisterServerOptions } from '@peertube/peertube-types' +import type { RegisterServerOptions, Video } from '@peertube/peertube-types' import { migrateSettings } from './lib/migration/settings' import { initSettings } from './lib/settings' import { initCustomFields } from './lib/custom-fields' @@ -8,6 +8,8 @@ import { prepareProsody, ensureProsodyRunning, ensureProsodyNotRunning } from '. import { unloadDebugMode } from './lib/debug' import { loadLoc } from './lib/loc' import decache from 'decache' +import { CustomTag } from '@peertube/feed/lib/typings' +import { URL } from 'url' // FIXME: Peertube unregister don't have any parameter. // Using this global variable to fix this, so we can use helpers to unregister. @@ -31,6 +33,33 @@ async function register (options: RegisterServerOptions): Promise { await initRouters(options) await initFederation(options) + options.registerHook({ + // @ts-expect-error Type doesn't exist for peertube 5.1 yet + target: 'filter:feed.podcast.video.create-custom-tags.result', + handler: (result: CustomTag[], { video, liveItem }: { video: Video, liveItem: boolean }) => { + if (!liveItem) { + return result + } + + const webserverUrl = options.peertubeHelpers.config.getWebserverUrl() + const hostname = (new URL(webserverUrl)).hostname + const embedUrl = `${webserverUrl}/plugins/livechat/router/webchat/room/${encodeURIComponent(video.uuid)}` + const xmppHostname = `room.${hostname}` + + return result.concat([ + { + name: 'podcast:chat', + attributes: { + server: xmppHostname, + protocol: 'xmpp', + space: `${video.uuid}@${xmppHostname}`, + embedUrl: embedUrl + } + } + ]) + } + }) + try { await prepareProsody(options) await ensureProsodyRunning(options) From e4d6626d6e4c689b8dc839b1aef76dadb050d53a Mon Sep 17 00:00:00 2001 From: Alecks Gates Date: Thu, 25 May 2023 22:08:30 -0500 Subject: [PATCH 2/6] Remove "room." from from podcast:chat server --- server/main.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/main.ts b/server/main.ts index a2c6e5fa..75aff65d 100644 --- a/server/main.ts +++ b/server/main.ts @@ -44,15 +44,15 @@ async function register (options: RegisterServerOptions): Promise { const webserverUrl = options.peertubeHelpers.config.getWebserverUrl() const hostname = (new URL(webserverUrl)).hostname const embedUrl = `${webserverUrl}/plugins/livechat/router/webchat/room/${encodeURIComponent(video.uuid)}` - const xmppHostname = `room.${hostname}` + const xmppRoom = `room.${hostname}` return result.concat([ { name: 'podcast:chat', attributes: { - server: xmppHostname, + server: hostname, protocol: 'xmpp', - space: `${video.uuid}@${xmppHostname}`, + space: `${video.uuid}@${xmppRoom}`, embedUrl: embedUrl } } From 2e98d930d3beae6a4571a3ba1e150214c3cd928e Mon Sep 17 00:00:00 2001 From: Alecks Gates Date: Sun, 4 Jun 2023 01:03:51 -0500 Subject: [PATCH 3/6] Set minimum PeerTube version to 5.2.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cb9c628c..80c78bc6 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,7 @@ "yaml": "^2.2.1" }, "engine": { - "peertube": ">=4.2.0" + "peertube": ">=5.2.0" }, "engines": { "npm": ">=7" From 995dfa4dffec92a580f393d1cbec64832bbeb06f Mon Sep 17 00:00:00 2001 From: John Livingston Date: Wed, 5 Jul 2023 18:33:30 +0200 Subject: [PATCH 4/6] Some refactoring. --- CHANGELOG.md | 1 + package-lock.json | 88 ++++++++++--------- package.json | 4 +- server/lib/rss/init.ts | 87 ++++++++++++++++++ server/lib/uri/webchat.ts | 5 +- server/main.ts | 33 +------ .../content/technical/thirdparty/_index.en.md | 2 +- 7 files changed, 141 insertions(+), 79 deletions(-) create mode 100644 server/lib/rss/init.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 07943869..7eb64835 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ## New Features * Implementing the [FEP-1970](https://codeberg.org/fediverse/fep/src/branch/main/fep/1970/fep-1970.md) draft for ActivityPub chat declaration. +* Podcast RSS feed support (thanks to [Alecks Gates](https://github.com/agates)). ## 7.1.0 diff --git a/package-lock.json b/package-lock.json index 851da2d2..839ed707 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ }, "devDependencies": { "@peertube/feed": "^5.1.0", - "@peertube/peertube-types": "^5.1.0", + "@peertube/peertube-types": "^5.2.0", "@tsconfig/node12": "^1.0.9", "@types/async": "^3.2.9", "@types/express": "^4.17.13", @@ -2571,15 +2571,16 @@ } }, "node_modules/@peertube/peertube-types": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@peertube/peertube-types/-/peertube-types-5.1.0.tgz", - "integrity": "sha512-n0FMlKzHae/HuBXXeUd5nWUmBN+BMiyRkwftRquFqyQObwTwltqUooL+DBcjetFsRmPTObvF4kPccQ7LTLqkXQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@peertube/peertube-types/-/peertube-types-5.2.0.tgz", + "integrity": "sha512-t5o/W4cF+E8FJXvFKBuGuCGU01Ad7jodyO7go//UYzgTde4CpxVJIE4G/8fKB30Kl0GCtqm2gncj31gilxZJ0g==", "dev": true, "dependencies": { "@aws-sdk/client-s3": "^3.190.0", "@node-oauth/oauth2-server": "^4.2.0", "@opentelemetry/api": "^1.1.0", "@opentelemetry/sdk-metrics": "^1.8.0", + "@peertube/feed": "^5.1.0", "@types/bluebird": "^3.5.33", "@types/express": "4.17.9", "@types/fluent-ffmpeg": "^2.1.16", @@ -2594,25 +2595,25 @@ "bullmq": "^3.6.6", "execa": "^5.1.1", "express": "^4.18.1", - "express-validator": "^6.4.0", + "express-validator": "^7.0.1", "fluent-ffmpeg": "^2.1.0", "fs-extra": "^11.1.0", "got": "^11.8.2", "hpagent": "^1.0.0", "ioredis": "^5.2.3", - "lru-cache": "^7.13.0", + "lru-cache": "^9.1.1", "memoizee": "^0.4.14", "multer": "^1.4.5-lts.1", - "parse-torrent": "^9.1.0", + "parse-torrent": "^9", "reflect-metadata": "^0.1.12", - "sequelize": "6.29.0", + "sequelize": "6.31.1", "sequelize-typescript": "^2.0.0-beta.1", "short-uuid": "^4.2.0", "socket.io": "^4.5.4", "winston": "3.8.2" }, "engines": { - "node": ">=12.x", + "node": ">=16.x", "yarn": ">=1.x" } }, @@ -2635,12 +2636,12 @@ "dev": true }, "node_modules/@peertube/peertube-types/node_modules/lru-cache": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz", - "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz", + "integrity": "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==", "dev": true, "engines": { - "node": ">=12" + "node": "14 || >=16.14" } }, "node_modules/@sindresorhus/is": { @@ -5749,13 +5750,13 @@ } }, "node_modules/express-validator": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.14.0.tgz", - "integrity": "sha512-ZWHJfnRgePp3FKRSKMtnZVnD1s8ZchWD+jSl7UMseGIqhweCo1Z9916/xXBbJAa6PrA3pUZfkOvIsHZG4ZtIMw==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.0.1.tgz", + "integrity": "sha512-oB+z9QOzQIE8FnlINqyIFA8eIckahC6qc8KtqLdLJcU3/phVyuhXH3bA4qzcrhme+1RYaCSwrq+TlZ/kAKIARA==", "dev": true, "dependencies": { "lodash": "^4.17.21", - "validator": "^13.7.0" + "validator": "^13.9.0" }, "engines": { "node": ">= 8.0.0" @@ -8863,9 +8864,9 @@ "dev": true }, "node_modules/sequelize": { - "version": "6.29.0", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.29.0.tgz", - "integrity": "sha512-m8Wi90rs3NZP9coXE52c7PL4Q078nwYZXqt1IxPvgki7nOFn0p/F0eKsYDBXCPw9G8/BCEa6zZNk0DQUAT4ypA==", + "version": "6.31.1", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.31.1.tgz", + "integrity": "sha512-cahWtRrYLjqoZP/aurGBoaxn29qQCF4bxkAUPEQ/ozjJjt6mtL4Q113S3N39mQRmX5fgxRbli+bzZARP/N51eg==", "dev": true, "funding": [ { @@ -10295,9 +10296,9 @@ } }, "node_modules/validator": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", + "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==", "dev": true, "engines": { "node": ">= 0.10" @@ -12652,15 +12653,16 @@ } }, "@peertube/peertube-types": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@peertube/peertube-types/-/peertube-types-5.1.0.tgz", - "integrity": "sha512-n0FMlKzHae/HuBXXeUd5nWUmBN+BMiyRkwftRquFqyQObwTwltqUooL+DBcjetFsRmPTObvF4kPccQ7LTLqkXQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@peertube/peertube-types/-/peertube-types-5.2.0.tgz", + "integrity": "sha512-t5o/W4cF+E8FJXvFKBuGuCGU01Ad7jodyO7go//UYzgTde4CpxVJIE4G/8fKB30Kl0GCtqm2gncj31gilxZJ0g==", "dev": true, "requires": { "@aws-sdk/client-s3": "^3.190.0", "@node-oauth/oauth2-server": "^4.2.0", "@opentelemetry/api": "^1.1.0", "@opentelemetry/sdk-metrics": "^1.8.0", + "@peertube/feed": "^5.1.0", "@types/bluebird": "^3.5.33", "@types/express": "4.17.9", "@types/fluent-ffmpeg": "^2.1.16", @@ -12675,18 +12677,18 @@ "bullmq": "^3.6.6", "execa": "^5.1.1", "express": "^4.18.1", - "express-validator": "^6.4.0", + "express-validator": "^7.0.1", "fluent-ffmpeg": "^2.1.0", "fs-extra": "^11.1.0", "got": "^11.8.2", "hpagent": "^1.0.0", "ioredis": "^5.2.3", - "lru-cache": "^7.13.0", + "lru-cache": "^9.1.1", "memoizee": "^0.4.14", "multer": "^1.4.5-lts.1", - "parse-torrent": "^9.1.0", + "parse-torrent": "^9", "reflect-metadata": "^0.1.12", - "sequelize": "6.29.0", + "sequelize": "6.31.1", "sequelize-typescript": "^2.0.0-beta.1", "short-uuid": "^4.2.0", "socket.io": "^4.5.4", @@ -12712,9 +12714,9 @@ "dev": true }, "lru-cache": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz", - "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz", + "integrity": "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==", "dev": true } } @@ -15093,13 +15095,13 @@ } }, "express-validator": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.14.0.tgz", - "integrity": "sha512-ZWHJfnRgePp3FKRSKMtnZVnD1s8ZchWD+jSl7UMseGIqhweCo1Z9916/xXBbJAa6PrA3pUZfkOvIsHZG4ZtIMw==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.0.1.tgz", + "integrity": "sha512-oB+z9QOzQIE8FnlINqyIFA8eIckahC6qc8KtqLdLJcU3/phVyuhXH3bA4qzcrhme+1RYaCSwrq+TlZ/kAKIARA==", "dev": true, "requires": { "lodash": "^4.17.21", - "validator": "^13.7.0" + "validator": "^13.9.0" } }, "ext": { @@ -17367,9 +17369,9 @@ } }, "sequelize": { - "version": "6.29.0", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.29.0.tgz", - "integrity": "sha512-m8Wi90rs3NZP9coXE52c7PL4Q078nwYZXqt1IxPvgki7nOFn0p/F0eKsYDBXCPw9G8/BCEa6zZNk0DQUAT4ypA==", + "version": "6.31.1", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.31.1.tgz", + "integrity": "sha512-cahWtRrYLjqoZP/aurGBoaxn29qQCF4bxkAUPEQ/ozjJjt6mtL4Q113S3N39mQRmX5fgxRbli+bzZARP/N51eg==", "dev": true, "requires": { "@types/debug": "^4.1.7", @@ -18449,9 +18451,9 @@ } }, "validator": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", - "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "version": "13.9.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.9.0.tgz", + "integrity": "sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA==", "dev": true }, "vary": { diff --git a/package.json b/package.json index 80c78bc6..2cd2ec19 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ }, "devDependencies": { "@peertube/feed": "^5.1.0", - "@peertube/peertube-types": "^5.1.0", + "@peertube/peertube-types": "^5.2.0", "@tsconfig/node12": "^1.0.9", "@types/async": "^3.2.9", "@types/express": "^4.17.13", @@ -70,7 +70,7 @@ "yaml": "^2.2.1" }, "engine": { - "peertube": ">=5.2.0" + "peertube": ">=4.2.0" }, "engines": { "npm": ">=7" diff --git a/server/lib/rss/init.ts b/server/lib/rss/init.ts new file mode 100644 index 00000000..037b3fe1 --- /dev/null +++ b/server/lib/rss/init.ts @@ -0,0 +1,87 @@ +import type { RegisterServerOptions, Video } from '@peertube/peertube-types' +import type { CustomTag } from '@peertube/feed/lib/typings' +import { videoHasWebchat } from '../../../shared/lib/video' +import { fillVideoCustomFields } from '../custom-fields' +import { getProsodyDomain } from '../prosody/config/domain' +import { getPublicChatUri } from '../uri/webchat' + +async function initRSS (options: RegisterServerOptions): Promise { + const logger = options.peertubeHelpers.logger + const registerHook = options.registerHook + logger.info('Registring RSS hooks...') + + registerHook({ + target: 'filter:feed.podcast.video.create-custom-tags.result', + handler: async ( + result: CustomTag[], { video, liveItem }: { video: Video, liveItem: boolean } + ): Promise => { + if (!liveItem) { + // Note: the Podcast RSS feed specification does not handle chats for non-live. + // So we just return here. + return result + } + + // FIXME: calling getSettings for each RSS entry is not optimal. + // Settings should be cached somewhere on the plugin level. + // (i already have some plans to do something for this) + const settings = await options.settingsManager.getSettings([ + 'chat-per-live-video', + 'chat-all-lives', + 'chat-all-non-lives', + 'chat-videos-list', + 'prosody-room-type', + 'federation-dont-publish-remotely', + 'prosody-room-allow-s2s', + 'prosody-s2s-port' + ]) + + if (settings['federation-dont-publish-remotely']) { + // Chat must not be published to the outer world. + return result + } + + await fillVideoCustomFields(options, video) + const hasChat = await videoHasWebchat({ + 'chat-per-live-video': !!settings['chat-per-live-video'], + 'chat-all-lives': !!settings['chat-all-lives'], + 'chat-all-non-lives': !!settings['chat-all-non-lives'], + 'chat-videos-list': settings['chat-videos-list'] as string + }, video) + + if (!hasChat) { + logger.debug(`Video uuid=${video.uuid} has not livechat, no need to add podcast:chat tag.`) + return result + } + + const prosodyDomain = await getProsodyDomain(options) + const podcastChat: any = { + name: 'podcast:chat', + attributes: { + server: prosodyDomain, + protocol: 'xmpp', + // space: will be added only if external XMPP connections are available + embedUrl: getPublicChatUri(options, video) + } + } + + // In order to connect to the chat using standard xmpp, it requires these settings: + // - prosody-room-allow-s2s + // - prosody-s2s-port + if (settings['prosody-room-allow-s2s'] && settings['prosody-s2s-port']) { + let roomJID: string + if (settings['prosody-room-type'] === 'channel') { + roomJID = `channel.${video.channel.id}@room.${prosodyDomain}` + } else { + roomJID = `${video.uuid}@room.${prosodyDomain}` + } + podcastChat.attributes.space = roomJID + } + + return result.concat([podcastChat]) + } + }) +} + +export { + initRSS +} diff --git a/server/lib/uri/webchat.ts b/server/lib/uri/webchat.ts index 7121bc1a..6aebe66e 100644 --- a/server/lib/uri/webchat.ts +++ b/server/lib/uri/webchat.ts @@ -1,5 +1,4 @@ - -import type { RegisterServerOptions, VideoObject } from '@peertube/peertube-types' +import type { RegisterServerOptions, VideoObject, Video } from '@peertube/peertube-types' import { getBaseRouterRoute, getBaseWebSocketRoute } from '../helpers' import { canonicalizePluginUri } from './canonicalize' @@ -19,7 +18,7 @@ export function getWSS2SUri (options: RegisterServerOptions): string | undefined return base + 'xmpp-websocket-s2s' } -export function getPublicChatUri (options: RegisterServerOptions, video: VideoObject): string { +export function getPublicChatUri (options: RegisterServerOptions, video: VideoObject | Video): string { const url = getBaseRouterRoute(options) + 'webchat/room/' + encodeURIComponent(video.uuid) return canonicalizePluginUri(options, url, { removePluginVersion: true diff --git a/server/main.ts b/server/main.ts index 75aff65d..c044cfc7 100644 --- a/server/main.ts +++ b/server/main.ts @@ -1,15 +1,14 @@ -import type { RegisterServerOptions, Video } from '@peertube/peertube-types' +import type { RegisterServerOptions } from '@peertube/peertube-types' import { migrateSettings } from './lib/migration/settings' import { initSettings } from './lib/settings' import { initCustomFields } from './lib/custom-fields' import { initRouters } from './lib/routers/index' import { initFederation } from './lib/federation/init' +import { initRSS } from './lib/rss/init' import { prepareProsody, ensureProsodyRunning, ensureProsodyNotRunning } from './lib/prosody/ctl' import { unloadDebugMode } from './lib/debug' import { loadLoc } from './lib/loc' import decache from 'decache' -import { CustomTag } from '@peertube/feed/lib/typings' -import { URL } from 'url' // FIXME: Peertube unregister don't have any parameter. // Using this global variable to fix this, so we can use helpers to unregister. @@ -32,33 +31,7 @@ async function register (options: RegisterServerOptions): Promise { await initCustomFields(options) await initRouters(options) await initFederation(options) - - options.registerHook({ - // @ts-expect-error Type doesn't exist for peertube 5.1 yet - target: 'filter:feed.podcast.video.create-custom-tags.result', - handler: (result: CustomTag[], { video, liveItem }: { video: Video, liveItem: boolean }) => { - if (!liveItem) { - return result - } - - const webserverUrl = options.peertubeHelpers.config.getWebserverUrl() - const hostname = (new URL(webserverUrl)).hostname - const embedUrl = `${webserverUrl}/plugins/livechat/router/webchat/room/${encodeURIComponent(video.uuid)}` - const xmppRoom = `room.${hostname}` - - return result.concat([ - { - name: 'podcast:chat', - attributes: { - server: hostname, - protocol: 'xmpp', - space: `${video.uuid}@${xmppRoom}`, - embedUrl: embedUrl - } - } - ]) - } - }) + await initRSS(options) try { await prepareProsody(options) diff --git a/support/documentation/content/technical/thirdparty/_index.en.md b/support/documentation/content/technical/thirdparty/_index.en.md index 82fdaa41..5c35cd2b 100644 --- a/support/documentation/content/technical/thirdparty/_index.en.md +++ b/support/documentation/content/technical/thirdparty/_index.en.md @@ -159,7 +159,7 @@ In the ActivityPub object, there is also a `peertubeLiveChat` entry. Don't use the content of this entry. This is specific to the livechat plugin, and can be changed or removed in a near future. It is currently required for some endpoint discovery. -### Using RSS +### Using Podcast RSS feed {{% notice warning %}} This part is not implemented yet, but should be available for v7.2.0 release. From bf5ae504434cb8a1317ceeb6421a3789735ecd86 Mon Sep 17 00:00:00 2001 From: Alecks Gates Date: Thu, 6 Jul 2023 01:42:25 -0500 Subject: [PATCH 5/6] Podcast RSS Documentation --- .../content/technical/thirdparty/_index.en.md | 97 ++++++++++++++++++- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/support/documentation/content/technical/thirdparty/_index.en.md b/support/documentation/content/technical/thirdparty/_index.en.md index 5c35cd2b..2b6bcc09 100644 --- a/support/documentation/content/technical/thirdparty/_index.en.md +++ b/support/documentation/content/technical/thirdparty/_index.en.md @@ -141,7 +141,7 @@ If you want to display the chat in a web page or in an iframe, here is what you * get the Video ActivityPub object, * if there is no `attachment` key, stop. -* loop through the `attachment` values (if `attachment is not an array, just iterate on this single value) +* loop through the `attachment` values (if `attachment` is not an array, just iterate on this single value) * search for an entry with `rel` === `discussion`, and with `href` using the `https` scheme (that begins with `https://`) * if found, open this href @@ -149,7 +149,7 @@ If you want to open the chat room using the XMPP protocol: * get the Video ActivityPub object, * if there is no `attachment` key, stop. -* loop through the `attachment` values (if `attachment is not an array, just iterate on this single value) +* loop through the `attachment` values (if `attachment` is not an array, just iterate on this single value) * search for an entry with `rel` === `discussion`, and with `href` using the `xmpp` scheme (that begins with `xmpp://`) * if found, open this xmpp uri with your client, or connect to the XMPP room at that address @@ -161,6 +161,8 @@ It is currently required for some endpoint discovery. ### Using Podcast RSS feed +The livechat plugin adds some data in Podcast RSS feeds under the [``](https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#live-item), so that the chat can be discovered for live streams. + {{% notice warning %}} This part is not implemented yet, but should be available for v7.2.0 release. {{% /notice %}} @@ -168,3 +170,94 @@ This part is not implemented yet, but should be available for v7.2.0 release. {{% notice info %}} This requires Peertube >= 5.2 {{% /notice %}} + +{{% notice info %}} +The `` element is currently only supported for live streams. +{{% /notice %}} + +This follows the [``](https://github.com/Podcastindex-org/podcast-namespace/discussions/502) proposal. + +{{% notice warning %}} +At the time of the writing, this proposal is in draft status, and the livechat plugin is a Proof-of-concept. +Until the proposal is adopted, the specification can change, and the livechat plugin will be adapted accordingly. +{{% /notice %}} + +Basically, the chat will be declared as tag under on the `` element. + +By default, here is an example of what you will get: + +```xml + + The video title + e32b4890-983b-4ce5-8b46-f2d6bc1d8819_2023-07-06T18:00:00.000Z + https://yourinstance.tld/videos/watch/8df24108-6e70-4fc8-b1cc-f2db7fcdd535 + + + + + + + + +``` + +In case the instance has activated the + [external XMPP clients connection](/peertube-plugin-livechat/documentation/admin/advanced/xmpp_clients/) feature: + +```xml + + The video title + e32b4890-983b-4ce5-8b46-f2d6bc1d8819_2023-07-06T18:00:00.000Z + https://yourinstance.tld/videos/watch/8df24108-6e70-4fc8-b1cc-f2db7fcdd535 + + + + + + + + +``` + +#### Algorithm + +If you want to display the chat in a web page or in an iframe, here is what you should do: + +* get the Podcast RSS feed for the channel, +* if there is no `` element under the ``, stop. +* find the `` you are looking for + * `` can be used to cross-reference the items with ActivityPub +* if there is no `` element under the ``, stop. +* loop through the `` values (if `` is not an array, just iterate on this single value) + * there should only be one, but you should expect to handle several just in case +* search for the first entry `protocol` === `xmpp` and an `embedUrl` attribute +* if found, open this embedUrl + +If you want to open the chat room using the XMPP protocol: + +* get the Podcast RSS feed for the channel, +* if there is no `` element under the ``, stop. +* find the `` you are looking for + * `` can be used to cross-reference the items with ActivityPub +* loop through the `` values (if `` is not an array, just iterate on this single value) + * there should only be one, but you should expect to handle several just in case +* search for the first entry `protocol` === `xmpp` and a `space` attribute + * space should be an XMPP JID for a MUC +* if found, open this XMPP JID with your client after converting it to a join URI, or connect to the XMPP room at that address From da21d36b871db81bea70db021892feb6674eba0e Mon Sep 17 00:00:00 2001 From: John Livingston Date: Thu, 6 Jul 2023 10:18:45 +0200 Subject: [PATCH 6/6] Fix typo --- support/documentation/content/contributing/develop/_index.de.md | 2 +- support/documentation/content/contributing/develop/_index.en.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/support/documentation/content/contributing/develop/_index.de.md b/support/documentation/content/contributing/develop/_index.de.md index 6259ca95..9a96eb63 100644 --- a/support/documentation/content/contributing/develop/_index.de.md +++ b/support/documentation/content/contributing/develop/_index.de.md @@ -78,7 +78,7 @@ To enable this mode, you juste have to create the The simple existence of this file is sufficient to trigger the debug mode. To make sure it's taken into account, you can restart your Peertube instance. -This file can contain some JSON to enable more advances options. +This file can contain some JSON to enable more advanced options. To have a list of existing parameters, check `server/lib/debug.ts`. Restart Peertube after each content modification. diff --git a/support/documentation/content/contributing/develop/_index.en.md b/support/documentation/content/contributing/develop/_index.en.md index 503317d8..3a79c942 100644 --- a/support/documentation/content/contributing/develop/_index.en.md +++ b/support/documentation/content/contributing/develop/_index.en.md @@ -80,7 +80,7 @@ To enable this mode, you juste have to create the The simple existence of this file is sufficient to trigger the debug mode. To make sure it's taken into account, you can restart your Peertube instance. -This file can contain some JSON to enable more advances options. +This file can contain some JSON to enable more advanced options. To have a list of existing parameters, check `server/lib/debug.ts`. Restart Peertube after each content modification.