mirror of https://github.com/Chocobozzz/PeerTube
Add ability to disable tracker
parent
22834691ab
commit
31b6ddf866
|
@ -105,6 +105,9 @@ export class ServerService {
|
||||||
enabled: false
|
enabled: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
tracker: {
|
||||||
|
enabled: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private videoCategories: Array<VideoConstant<number>> = []
|
private videoCategories: Array<VideoConstant<number>> = []
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Component, OnInit } from '@angular/core'
|
import { Component, OnInit } from '@angular/core'
|
||||||
import { ServerService } from '@app/core'
|
import { ServerService } from '@app/core'
|
||||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||||
import { ServerConfig } from '../../../../../shared'
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-instance-features-table',
|
selector: 'my-instance-features-table',
|
||||||
|
@ -65,6 +64,10 @@ export class InstanceFeaturesTableComponent implements OnInit {
|
||||||
{
|
{
|
||||||
label: this.i18n('Torrent import'),
|
label: this.i18n('Torrent import'),
|
||||||
value: config.import.videos.torrent.enabled
|
value: config.import.videos.torrent.enabled
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: this.i18n('P2P enabled'),
|
||||||
|
value: config.tracker.enabled
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -427,6 +427,7 @@ my-video-comments {
|
||||||
// If the view is not expanded, take into account the menu
|
// If the view is not expanded, take into account the menu
|
||||||
.privacy-concerns {
|
.privacy-concerns {
|
||||||
width: calc(100% - #{$menu-width});
|
width: calc(100% - #{$menu-width});
|
||||||
|
margin-left: -15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: $small-view) {
|
@media screen and (max-width: $small-view) {
|
||||||
|
|
|
@ -29,6 +29,7 @@ import { VideoPlaylist } from '@app/shared/video-playlist/video-playlist.model'
|
||||||
import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
|
import { VideoPlaylistService } from '@app/shared/video-playlist/video-playlist.service'
|
||||||
import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
|
import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
|
||||||
import { Video } from '@app/shared/video/video.model'
|
import { Video } from '@app/shared/video/video.model'
|
||||||
|
import { isWebRTCDisabled } from '../../../assets/player/utils'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-video-watch',
|
selector: 'my-video-watch',
|
||||||
|
@ -71,6 +72,7 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
private currentTime: number
|
private currentTime: number
|
||||||
private paramsSub: Subscription
|
private paramsSub: Subscription
|
||||||
private queryParamsSub: Subscription
|
private queryParamsSub: Subscription
|
||||||
|
private configSub: Subscription
|
||||||
|
|
||||||
constructor (
|
constructor (
|
||||||
private elementRef: ElementRef,
|
private elementRef: ElementRef,
|
||||||
|
@ -100,12 +102,16 @@ export class VideoWatchComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit () {
|
ngOnInit () {
|
||||||
if (
|
this.configSub = this.serverService.configLoaded
|
||||||
!!((window as any).RTCPeerConnection || (window as any).mozRTCPeerConnection || (window as any).webkitRTCPeerConnection) === false ||
|
.subscribe(() => {
|
||||||
peertubeLocalStorage.getItem(VideoWatchComponent.LOCAL_STORAGE_PRIVACY_CONCERN_KEY) === 'true'
|
if (
|
||||||
) {
|
isWebRTCDisabled() ||
|
||||||
this.hasAlreadyAcceptedPrivacyConcern = true
|
this.serverService.getConfig().tracker.enabled === false ||
|
||||||
}
|
peertubeLocalStorage.getItem(VideoWatchComponent.LOCAL_STORAGE_PRIVACY_CONCERN_KEY) === 'true'
|
||||||
|
) {
|
||||||
|
this.hasAlreadyAcceptedPrivacyConcern = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
this.paramsSub = this.route.params.subscribe(routeParams => {
|
this.paramsSub = this.route.params.subscribe(routeParams => {
|
||||||
const videoId = routeParams[ 'videoId' ]
|
const videoId = routeParams[ 'videoId' ]
|
||||||
|
|
|
@ -4,6 +4,10 @@ function toTitleCase (str: string) {
|
||||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isWebRTCDisabled () {
|
||||||
|
return !!((window as any).RTCPeerConnection || (window as any).mozRTCPeerConnection || (window as any).webkitRTCPeerConnection) === false
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/danrevah/ngx-pipes/blob/master/src/pipes/math/bytes.ts
|
// https://github.com/danrevah/ngx-pipes/blob/master/src/pipes/math/bytes.ts
|
||||||
// Don't import all Angular stuff, just copy the code with shame
|
// Don't import all Angular stuff, just copy the code with shame
|
||||||
const dictionaryBytes: Array<{max: number, type: string}> = [
|
const dictionaryBytes: Array<{max: number, type: string}> = [
|
||||||
|
@ -141,6 +145,7 @@ export {
|
||||||
toTitleCase,
|
toTitleCase,
|
||||||
timeToInt,
|
timeToInt,
|
||||||
secondsToTime,
|
secondsToTime,
|
||||||
|
isWebRTCDisabled,
|
||||||
buildVideoLink,
|
buildVideoLink,
|
||||||
buildVideoEmbed,
|
buildVideoEmbed,
|
||||||
videoFileMaxByResolution,
|
videoFileMaxByResolution,
|
||||||
|
|
|
@ -21,16 +21,16 @@
|
||||||
.vjs-dock-description {
|
.vjs-dock-description {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
|
||||||
&::before, &::after {
|
.text::before, .text::after {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
content: '\1F308';
|
content: '\1F308';
|
||||||
}
|
}
|
||||||
|
|
||||||
&::before {
|
.text::before {
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&::after {
|
.text::after {
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
transform: scale(-1, 1);
|
transform: scale(-1, 1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import './embed.scss'
|
||||||
|
|
||||||
import * as Channel from 'jschannel'
|
import * as Channel from 'jschannel'
|
||||||
|
|
||||||
import { peertubeTranslate, ResultList, VideoDetails } from '../../../../shared'
|
import { peertubeTranslate, ResultList, ServerConfig, VideoDetails } from '../../../../shared'
|
||||||
import { PeerTubeResolution } from '../player/definitions'
|
import { PeerTubeResolution } from '../player/definitions'
|
||||||
import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
|
import { VideoJSCaption } from '../../assets/player/peertube-videojs-typings'
|
||||||
import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model'
|
import { VideoCaption } from '../../../../shared/models/videos/caption/video-caption.model'
|
||||||
|
@ -177,6 +177,10 @@ class PeerTubeEmbed {
|
||||||
return fetch(this.getVideoUrl(videoId) + '/captions')
|
return fetch(this.getVideoUrl(videoId) + '/captions')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadConfig (): Promise<Response> {
|
||||||
|
return fetch('/api/v1/config')
|
||||||
|
}
|
||||||
|
|
||||||
removeElement (element: HTMLElement) {
|
removeElement (element: HTMLElement) {
|
||||||
element.parentElement.removeChild(element)
|
element.parentElement.removeChild(element)
|
||||||
}
|
}
|
||||||
|
@ -237,10 +241,10 @@ class PeerTubeEmbed {
|
||||||
try {
|
try {
|
||||||
const params = new URL(window.location.toString()).searchParams
|
const params = new URL(window.location.toString()).searchParams
|
||||||
|
|
||||||
this.autoplay = this.getParamToggle(params, 'autoplay')
|
this.autoplay = this.getParamToggle(params, 'autoplay', false)
|
||||||
this.controls = this.getParamToggle(params, 'controls')
|
this.controls = this.getParamToggle(params, 'controls', true)
|
||||||
this.muted = this.getParamToggle(params, 'muted')
|
this.muted = this.getParamToggle(params, 'muted', false)
|
||||||
this.loop = this.getParamToggle(params, 'loop')
|
this.loop = this.getParamToggle(params, 'loop', false)
|
||||||
this.enableApi = this.getParamToggle(params, 'api', this.enableApi)
|
this.enableApi = this.getParamToggle(params, 'api', this.enableApi)
|
||||||
|
|
||||||
this.scope = this.getParamString(params, 'scope', this.scope)
|
this.scope = this.getParamString(params, 'scope', this.scope)
|
||||||
|
@ -258,10 +262,11 @@ class PeerTubeEmbed {
|
||||||
const urlParts = window.location.pathname.split('/')
|
const urlParts = window.location.pathname.split('/')
|
||||||
const videoId = urlParts[ urlParts.length - 1 ]
|
const videoId = urlParts[ urlParts.length - 1 ]
|
||||||
|
|
||||||
const [ serverTranslations, videoResponse, captionsResponse ] = await Promise.all([
|
const [ serverTranslations, videoResponse, captionsResponse, configResponse ] = await Promise.all([
|
||||||
PeertubePlayerManager.getServerTranslations(window.location.origin, navigator.language),
|
PeertubePlayerManager.getServerTranslations(window.location.origin, navigator.language),
|
||||||
this.loadVideoInfo(videoId),
|
this.loadVideoInfo(videoId),
|
||||||
this.loadVideoCaptions(videoId)
|
this.loadVideoCaptions(videoId),
|
||||||
|
this.loadConfig()
|
||||||
])
|
])
|
||||||
|
|
||||||
if (!videoResponse.ok) {
|
if (!videoResponse.ok) {
|
||||||
|
@ -338,9 +343,14 @@ class PeerTubeEmbed {
|
||||||
window[ 'videojsPlayer' ] = this.player
|
window[ 'videojsPlayer' ] = this.player
|
||||||
|
|
||||||
if (this.controls) {
|
if (this.controls) {
|
||||||
|
const config: ServerConfig = await configResponse.json()
|
||||||
|
const description = config.tracker.enabled
|
||||||
|
? '<span class="text">' + this.player.localize('Uses P2P, others may know your IP is downloading this video.') + '</span>'
|
||||||
|
: undefined
|
||||||
|
|
||||||
this.player.dock({
|
this.player.dock({
|
||||||
title: videoInfo.name,
|
title: videoInfo.name,
|
||||||
description: this.player.localize('Uses P2P, others may know your IP is downloading this video.')
|
description
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,16 @@ csp:
|
||||||
report_only: true # CSP directives are still being tested, so disable the report only mode at your own risk!
|
report_only: true # CSP directives are still being tested, so disable the report only mode at your own risk!
|
||||||
report_uri:
|
report_uri:
|
||||||
|
|
||||||
|
tracker:
|
||||||
|
# If you disable the tracker, you disable the P2P aspect of PeerTube
|
||||||
|
enabled: true
|
||||||
|
# Only handle requests on your videos.
|
||||||
|
# If you set this to false it means you have a public tracker.
|
||||||
|
# Then, it is possible that clients overload your instance with external torrents
|
||||||
|
private: true
|
||||||
|
# Reject peers that do a lot of announces (could improve privacy of TCP/UDP peers)
|
||||||
|
reject_too_many_announces: false
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
previews:
|
previews:
|
||||||
size: 500 # Max number of previews you want to cache
|
size: 500 # Max number of previews you want to cache
|
||||||
|
|
|
@ -102,6 +102,16 @@ csp:
|
||||||
report_only: true # CSP directives are still being tested, so disable the report only mode at your own risk!
|
report_only: true # CSP directives are still being tested, so disable the report only mode at your own risk!
|
||||||
report_uri:
|
report_uri:
|
||||||
|
|
||||||
|
tracker:
|
||||||
|
# If you disable the tracker, you disable the P2P aspect of PeerTube
|
||||||
|
enabled: true
|
||||||
|
# Only handle requests on your videos.
|
||||||
|
# If you set this to false it means you have a public tracker.
|
||||||
|
# Then, it is possible that clients overload your instance with external torrents
|
||||||
|
private: true
|
||||||
|
# Reject peers that do a lot of announces (could improve privacy of TCP/UDP peers)
|
||||||
|
reject_too_many_announces: false
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
#
|
#
|
||||||
|
|
|
@ -136,6 +136,9 @@ async function getConfig (req: express.Request, res: express.Response) {
|
||||||
videos: {
|
videos: {
|
||||||
intervalDays: CONFIG.TRENDING.VIDEOS.INTERVAL_DAYS
|
intervalDays: CONFIG.TRENDING.VIDEOS.INTERVAL_DAYS
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
tracker: {
|
||||||
|
enabled: CONFIG.TRACKER.ENABLED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,10 @@ const trackerServer = new TrackerServer({
|
||||||
ws: false,
|
ws: false,
|
||||||
dht: false,
|
dht: false,
|
||||||
filter: async function (infoHash, params, cb) {
|
filter: async function (infoHash, params, cb) {
|
||||||
|
if (CONFIG.TRACKER.ENABLED === false) {
|
||||||
|
return cb(new Error('Tracker is disabled on this instance.'))
|
||||||
|
}
|
||||||
|
|
||||||
let ip: string
|
let ip: string
|
||||||
|
|
||||||
if (params.type === 'ws') {
|
if (params.type === 'ws') {
|
||||||
|
@ -36,11 +40,13 @@ const trackerServer = new TrackerServer({
|
||||||
peersIps[ ip ] = peersIps[ ip ] ? peersIps[ ip ] + 1 : 1
|
peersIps[ ip ] = peersIps[ ip ] ? peersIps[ ip ] + 1 : 1
|
||||||
peersIpInfoHash[ key ] = peersIpInfoHash[ key ] ? peersIpInfoHash[ key ] + 1 : 1
|
peersIpInfoHash[ key ] = peersIpInfoHash[ key ] ? peersIpInfoHash[ key ] + 1 : 1
|
||||||
|
|
||||||
if (peersIpInfoHash[ key ] > TRACKER_RATE_LIMITS.ANNOUNCES_PER_IP_PER_INFOHASH) {
|
if (CONFIG.TRACKER.REJECT_TOO_MANY_ANNOUNCES && peersIpInfoHash[ key ] > TRACKER_RATE_LIMITS.ANNOUNCES_PER_IP_PER_INFOHASH) {
|
||||||
return cb(new Error(`Too many requests (${peersIpInfoHash[ key ]} of ip ${ip} for torrent ${infoHash}`))
|
return cb(new Error(`Too many requests (${peersIpInfoHash[ key ]} of ip ${ip} for torrent ${infoHash}`))
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (CONFIG.TRACKER.PRIVATE === false) return cb()
|
||||||
|
|
||||||
const videoFileExists = await VideoFileModel.doesInfohashExist(infoHash)
|
const videoFileExists = await VideoFileModel.doesInfohashExist(infoHash)
|
||||||
if (videoFileExists === true) return cb()
|
if (videoFileExists === true) return cb()
|
||||||
|
|
||||||
|
@ -55,13 +61,16 @@ const trackerServer = new TrackerServer({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
trackerServer.on('error', function (err) {
|
if (CONFIG.TRACKER.ENABLED !== false) {
|
||||||
logger.error('Error in tracker.', { err })
|
|
||||||
})
|
|
||||||
|
|
||||||
trackerServer.on('warning', function (err) {
|
trackerServer.on('error', function (err) {
|
||||||
logger.warn('Warning in tracker.', { err })
|
logger.error('Error in tracker.', { err })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
trackerServer.on('warning', function (err) {
|
||||||
|
logger.warn('Warning in tracker.', { err })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const onHttpRequest = trackerServer.onHttpRequest.bind(trackerServer)
|
const onHttpRequest = trackerServer.onHttpRequest.bind(trackerServer)
|
||||||
trackerRouter.get('/tracker/announce', (req, res) => onHttpRequest(req, res, { action: 'announce' }))
|
trackerRouter.get('/tracker/announce', (req, res) => onHttpRequest(req, res, { action: 'announce' }))
|
||||||
|
|
|
@ -25,7 +25,8 @@ function checkMissedConfig () {
|
||||||
'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route',
|
'instance.name', 'instance.short_description', 'instance.description', 'instance.terms', 'instance.default_client_route',
|
||||||
'instance.is_nsfw', 'instance.default_nsfw_policy', 'instance.robots', 'instance.securitytxt',
|
'instance.is_nsfw', 'instance.default_nsfw_policy', 'instance.robots', 'instance.securitytxt',
|
||||||
'services.twitter.username', 'services.twitter.whitelisted',
|
'services.twitter.username', 'services.twitter.whitelisted',
|
||||||
'followers.instance.enabled', 'followers.instance.manual_approval'
|
'followers.instance.enabled', 'followers.instance.manual_approval',
|
||||||
|
'tracker.enabled', 'tracker.private', 'tracker.reject_too_many_announces'
|
||||||
]
|
]
|
||||||
const requiredAlternatives = [
|
const requiredAlternatives = [
|
||||||
[ // set
|
[ // set
|
||||||
|
|
|
@ -243,6 +243,11 @@ const CONFIG = {
|
||||||
REPORT_ONLY: config.get<boolean>('csp.report_only'),
|
REPORT_ONLY: config.get<boolean>('csp.report_only'),
|
||||||
REPORT_URI: config.get<boolean>('csp.report_uri')
|
REPORT_URI: config.get<boolean>('csp.report_uri')
|
||||||
},
|
},
|
||||||
|
TRACKER: {
|
||||||
|
ENABLED: config.get<boolean>('tracker.enabled'),
|
||||||
|
PRIVATE: config.get<boolean>('tracker.private'),
|
||||||
|
REJECT_TOO_MANY_ANNOUNCES: config.get<boolean>('tracker.reject_too_many_announces')
|
||||||
|
},
|
||||||
ADMIN: {
|
ADMIN: {
|
||||||
get EMAIL () { return config.get<string>('admin.email') }
|
get EMAIL () { return config.get<string>('admin.email') }
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import * as magnetUtil from 'magnet-uri'
|
import * as magnetUtil from 'magnet-uri'
|
||||||
import 'mocha'
|
import 'mocha'
|
||||||
import { getVideo, killallServers, runServer, ServerInfo, uploadVideo } from '../../../../shared/utils'
|
import { getVideo, killallServers, reRunServer, runServer, ServerInfo, uploadVideo } from '../../../../shared/utils'
|
||||||
import { flushTests, setAccessTokensToServers } from '../../../../shared/utils/index'
|
import { flushTests, setAccessTokensToServers } from '../../../../shared/utils/index'
|
||||||
import { VideoDetails } from '../../../../shared/models/videos'
|
import { VideoDetails } from '../../../../shared/models/videos'
|
||||||
import * as WebTorrent from 'webtorrent'
|
import * as WebTorrent from 'webtorrent'
|
||||||
|
@ -34,7 +34,7 @@ describe('Test tracker', function () {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should return an error when adding an incorrect infohash', done => {
|
it('Should return an error when adding an incorrect infohash', function (done) {
|
||||||
this.timeout(10000)
|
this.timeout(10000)
|
||||||
const webtorrent = new WebTorrent()
|
const webtorrent = new WebTorrent()
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ describe('Test tracker', function () {
|
||||||
torrent.on('done', () => done(new Error('No error on infohash')))
|
torrent.on('done', () => done(new Error('No error on infohash')))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should succeed with the correct infohash', done => {
|
it('Should succeed with the correct infohash', function (done) {
|
||||||
this.timeout(10000)
|
this.timeout(10000)
|
||||||
const webtorrent = new WebTorrent()
|
const webtorrent = new WebTorrent()
|
||||||
|
|
||||||
|
@ -64,6 +64,26 @@ describe('Test tracker', function () {
|
||||||
torrent.on('done', done)
|
torrent.on('done', done)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should disable the tracker', function (done) {
|
||||||
|
this.timeout(20000)
|
||||||
|
|
||||||
|
killallServers([ server ])
|
||||||
|
reRunServer(server, { tracker: { enabled: false } })
|
||||||
|
.then(() => {
|
||||||
|
const webtorrent = new WebTorrent()
|
||||||
|
|
||||||
|
const torrent = webtorrent.add(goodMagnet)
|
||||||
|
|
||||||
|
torrent.on('error', done)
|
||||||
|
torrent.on('warning', warn => {
|
||||||
|
const message = typeof warn === 'string' ? warn : warn.message
|
||||||
|
if (message.indexOf('disabled ') !== -1) return done()
|
||||||
|
})
|
||||||
|
|
||||||
|
torrent.on('done', () => done(new Error('Tracker is enabled')))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
after(async function () {
|
after(async function () {
|
||||||
killallServers([ server ])
|
killallServers([ server ])
|
||||||
})
|
})
|
||||||
|
|
|
@ -97,4 +97,8 @@ export interface ServerConfig {
|
||||||
intervalDays: number
|
intervalDays: number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracker: {
|
||||||
|
enabled: boolean
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,5 +46,9 @@ storage:
|
||||||
log:
|
log:
|
||||||
level: 'info' # debug/info/warning/error
|
level: 'info' # debug/info/warning/error
|
||||||
|
|
||||||
|
tracker:
|
||||||
|
enabled: true
|
||||||
|
reject_too_many_announces: false # false because we have issues with traefik and ws ip/port forwarding
|
||||||
|
|
||||||
admin:
|
admin:
|
||||||
email: null
|
email: null
|
||||||
|
|
Loading…
Reference in New Issue