Make federation of unlisted videos an instance-level server preference (#2802)
authorLevi Bard <taktaktaktaktaktaktaktaktaktak@gmail.com>
Wed, 3 Jun 2020 07:42:07 +0000 (09:42 +0200)
committerGitHub <noreply@github.com>
Wed, 3 Jun 2020 07:42:07 +0000 (09:42 +0200)
* Add preference for federating unlisted videos

* Connect unlisted video federation with new preference

* Apply pull request feedback

* Fix lint issues

* Remove preference for federating unlisted videos from web admin interface

config/default.yaml
config/production.yaml.example
server/helpers/video.ts
server/initializers/checker-before-init.ts
server/initializers/config.ts
server/models/video/video.ts
server/tests/api/videos/video-privacy.ts

index 34a0a146fa3687567f463744d7c146170e439298..f6e944298a5eb4a2b810b78727fe39ad9463844b 100644 (file)
@@ -178,6 +178,10 @@ plugins:
     check_latest_versions_interval: '12 hours' # How often you want to check new plugins/themes versions
     url: 'https://packages.joinpeertube.org'
 
+federation:
+  videos:
+    federate_unlisted: true
+
 cache:
   previews:
     size: 500 # Max number of previews you want to cache
index 0ac05c5153d14e44a530467dc90b746a04e1b798..e215288214ac16e7ebdcfa4e19c6a44283ad0bd9 100644 (file)
@@ -179,6 +179,10 @@ plugins:
     check_latest_versions_interval: '12 hours' # How often you want to check new plugins/themes versions
     url: 'https://packages.joinpeertube.org'
 
+federation:
+  videos:
+    federate_unlisted: true
+
 
 ###############################################################################
 #
index 6f76cbdfc755a23f14f882600b86a7366e93747d..4bcc6d0b9497e218dc0204aa2a80308cd622ff1f 100644 (file)
@@ -15,7 +15,8 @@ import {
 import { Response } from 'express'
 import { DEFAULT_AUDIO_RESOLUTION } from '@server/initializers/constants'
 import { JobQueue } from '@server/lib/job-queue'
-import { VideoTranscodingPayload } from '@shared/models'
+import { VideoPrivacy, VideoTranscodingPayload } from '@shared/models'
+import { CONFIG } from "@server/initializers/config"
 
 type VideoFetchType = 'all' | 'only-video' | 'only-video-with-rights' | 'id' | 'none' | 'only-immutable-attributes'
 
@@ -96,6 +97,19 @@ function extractVideo (videoOrPlaylist: MVideo | MStreamingPlaylistVideo) {
     : videoOrPlaylist
 }
 
+function isPrivacyForFederation (privacy: VideoPrivacy) {
+  const castedPrivacy = parseInt(privacy + '', 10)
+
+  return castedPrivacy === VideoPrivacy.PUBLIC ||
+    (CONFIG.FEDERATION.VIDEOS.FEDERATE_UNLISTED === true && castedPrivacy === VideoPrivacy.UNLISTED)
+}
+
+function getPrivaciesForFederation () {
+  return (CONFIG.FEDERATION.VIDEOS.FEDERATE_UNLISTED === true)
+    ? [ { privacy: VideoPrivacy.PUBLIC }, { privacy: VideoPrivacy.UNLISTED } ]
+    : [ { privacy: VideoPrivacy.PUBLIC } ]
+}
+
 export {
   VideoFetchType,
   VideoFetchByUrlType,
@@ -103,5 +117,7 @@ export {
   getVideoWithAttributes,
   fetchVideoByUrl,
   addOptimizeOrMergeAudioJob,
-  extractVideo
+  extractVideo,
+  isPrivacyForFederation,
+  getPrivaciesForFederation
 }
index 56f8156c67aa4db9c29032a89703564bcdab99e6..bd8f02bc02eb4f9b906a04fa33d576c45f580ff1 100644 (file)
@@ -34,7 +34,8 @@ function checkMissedConfig () {
     'history.videos.max_age', 'views.videos.remote.max_age',
     'rates_limit.login.window', 'rates_limit.login.max', 'rates_limit.ask_send_email.window', 'rates_limit.ask_send_email.max',
     'theme.default',
-    'remote_redundancy.videos.accept_from'
+    'remote_redundancy.videos.accept_from',
+    'federation.videos.federate_unlisted'
   ]
   const requiredAlternatives = [
     [ // set
index e2920ce9e07d7a41d054dad91defcb040fcde164..44fd9045b48a82117307ab4673f1a2b7855c3114 100644 (file)
@@ -155,6 +155,11 @@ const CONFIG = {
       URL: config.get<string>('plugins.index.url')
     }
   },
+  FEDERATION: {
+    VIDEOS: {
+      FEDERATE_UNLISTED: config.get<boolean>('federation.videos.federate_unlisted')
+    }
+  },
   ADMIN: {
     get EMAIL () { return config.get<string>('admin.email') }
   },
index f5194e2593706137f6cd96f1b3763732d4472385..02a4c851c2f28235cc455b9102b2fd10889bd62d 100644 (file)
@@ -126,6 +126,7 @@ import { ModelCache } from '@server/models/model-cache'
 import { buildListQuery, BuildVideosQueryOptions, wrapForAPIResults } from './video-query-builder'
 import { buildNSFWFilter } from '@server/helpers/express-utils'
 import { getServerActor } from '@server/models/application/application'
+import { getPrivaciesForFederation, isPrivacyForFederation } from "@server/helpers/video"
 
 export enum ScopeNames {
   AVAILABLE_FOR_LIST_IDS = 'AVAILABLE_FOR_LIST_IDS',
@@ -864,10 +865,7 @@ export class VideoModel extends Model<VideoModel> {
         id: {
           [Op.in]: Sequelize.literal('(' + rawQuery + ')')
         },
-        [Op.or]: [
-          { privacy: VideoPrivacy.PUBLIC },
-          { privacy: VideoPrivacy.UNLISTED }
-        ]
+        [Op.or]: getPrivaciesForFederation()
       },
       include: [
         {
@@ -1582,12 +1580,6 @@ export class VideoModel extends Model<VideoModel> {
     return videos
   }
 
-  private static isPrivacyForFederation (privacy: VideoPrivacy) {
-    const castedPrivacy = parseInt(privacy + '', 10)
-
-    return castedPrivacy === VideoPrivacy.PUBLIC || castedPrivacy === VideoPrivacy.UNLISTED
-  }
-
   static getCategoryLabel (id: number) {
     return VIDEO_CATEGORIES[id] || 'Misc'
   }
@@ -1813,11 +1805,11 @@ export class VideoModel extends Model<VideoModel> {
   }
 
   hasPrivacyForFederation () {
-    return VideoModel.isPrivacyForFederation(this.privacy)
+    return isPrivacyForFederation(this.privacy)
   }
 
   isNewVideo (newPrivacy: VideoPrivacy) {
-    return this.hasPrivacyForFederation() === false && VideoModel.isPrivacyForFederation(newPrivacy) === true
+    return this.hasPrivacyForFederation() === false && isPrivacyForFederation(newPrivacy) === true
   }
 
   setAsRefreshed () {
index 4bbbb90f3df9a63d00d937b60561055c5a5bab9e..38e93bbe631361d23c9a05f55e6e7636feee6672 100644 (file)
@@ -5,7 +5,7 @@ import 'mocha'
 import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
 import {
   cleanupTests,
-  flushAndRunMultipleServers,
+  flushAndRunServer,
   getVideosList,
   getVideosListWithToken,
   ServerInfo,
@@ -22,7 +22,7 @@ import { Video } from '@shared/models'
 const expect = chai.expect
 
 describe('Test video privacy', function () {
-  let servers: ServerInfo[] = []
+  const servers: ServerInfo[] = []
   let anotherUserToken: string
 
   let privateVideoId: number
@@ -32,14 +32,24 @@ describe('Test video privacy', function () {
   let internalVideoUUID: string
 
   let unlistedVideoUUID: string
+  let nonFederatedUnlistedVideoUUID: string
 
   let now: number
 
+  const dontFederateUnlistedConfig = {
+    federation: {
+      videos: {
+        federate_unlisted: false
+      }
+    }
+  }
+
   before(async function () {
     this.timeout(50000)
 
     // Run servers
-    servers = await flushAndRunMultipleServers(2)
+    servers.push(await flushAndRunServer(1, dontFederateUnlistedConfig))
+    servers.push(await flushAndRunServer(2))
 
     // Get the access tokens
     await setAccessTokensToServers(servers)
@@ -164,6 +174,37 @@ describe('Test video privacy', function () {
     }
   })
 
+  it('Should upload a non-federating unlisted video to server 1', async function () {
+    this.timeout(30000)
+
+    const attributes = {
+      name: 'unlisted video',
+      privacy: VideoPrivacy.UNLISTED
+    }
+    await uploadVideo(servers[0].url, servers[0].accessToken, attributes)
+
+    await waitJobs(servers)
+  })
+
+  it('Should list my new unlisted video', async function () {
+    const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 3)
+
+    expect(res.body.total).to.equal(3)
+    expect(res.body.data).to.have.lengthOf(3)
+
+    nonFederatedUnlistedVideoUUID = res.body.data[0].uuid
+  })
+
+  it('Should be able to get non-federated unlisted video from origin', async function () {
+    const res = await getVideo(servers[0].url, nonFederatedUnlistedVideoUUID)
+
+    expect(res.body.name).to.equal('unlisted video')
+  })
+
+  it('Should not be able to get non-federated unlisted video from federated server', async function () {
+    await getVideo(servers[1].url, nonFederatedUnlistedVideoUUID, 404)
+  })
+
   it('Should update the private and internal videos to public on server 1', async function () {
     this.timeout(10000)
 
@@ -230,8 +271,8 @@ describe('Test video privacy', function () {
       const res = await getMyVideos(servers[0].url, servers[0].accessToken, 0, 5)
       const videos = res.body.data
 
-      expect(res.body.total).to.equal(2)
-      expect(videos).to.have.lengthOf(2)
+      expect(res.body.total).to.equal(3)
+      expect(videos).to.have.lengthOf(3)
 
       const privateVideo = videos.find(v => v.name === 'private video becomes public')
       const internalVideo = videos.find(v => v.name === 'internal video becomes public')