feat(p2p): p2p torrents replication

This commit is contained in:
Alexey Kasyanchuk
2018-03-12 15:55:30 +03:00
parent 223fe8c1ab
commit db8a8cbd36
5 changed files with 152 additions and 68 deletions

View File

@ -11,7 +11,7 @@ import Slider from 'material-ui/Slider'
import fs from 'fs' import fs from 'fs'
const {dialog} = require('electron').remote const {dialog} = require('electron').remote
export default class AdminPage extends Page { export default class ConfigPage extends Page {
constructor(props) { constructor(props) {
super(props) super(props)
this.setTitle('Rats settings'); this.setTitle('Rats settings');
@ -196,6 +196,18 @@ export default class AdminPage extends Page {
/> />
</div> </div>
</div> </div>
<div className='column w100p'>
<Toggle
style={{marginTop: '10px'}}
label="P2P torrents replication"
toggled={this.options.p2pReplication}
onToggle={(e, checked) => {
this.options.p2pReplication = checked
this.forceUpdate()
}}
/>
<div className='fs0-75' style={{color: 'grey'}}>* Enable torrents replication from another rats clients. Dont recomended if torrent scanner works correct.</div>
</div>
<div style={{marginTop: 10}}>Torrent network scanner settings:</div> <div style={{marginTop: 10}}>Torrent network scanner settings:</div>

View File

@ -4,7 +4,7 @@ import PagesPie from './pages-pie.js';
import IndexPage from './index-page.js' import IndexPage from './index-page.js'
import TorrentPage from './torrent-page.js' import TorrentPage from './torrent-page.js'
import DMCAPage from './dmca-page.js' import DMCAPage from './dmca-page.js'
import AdminPage from './admin-page.js' import ConfigPage from './config-page.js'
import TopPage from './top-page.js' import TopPage from './top-page.js'
import DownloadPage from './download-page.js' import DownloadPage from './download-page.js'
import ChangelogPage from './changelog-page.js' import ChangelogPage from './changelog-page.js'
@ -72,7 +72,7 @@ router('/DMCA', () => {
router('/config', () => { router('/config', () => {
//singleton //singleton
PagesPie.instance().open(AdminPage, {replace: 'all'}); PagesPie.instance().open(ConfigPage, {replace: 'all'});
}); });
router('/top', () => { router('/top', () => {

View File

@ -13,6 +13,7 @@ let config = {
p2p: true, p2p: true,
p2pConnections: 10, p2pConnections: 10,
p2pBootstrap: true, p2pBootstrap: true,
p2pReplication: false,
upnp: true, upnp: true,

View File

@ -64,6 +64,7 @@ class p2p {
// new peer with peer exchange // new peer with peer exchange
this.on('peer', (peer) => { this.on('peer', (peer) => {
console.log('got peer exchange', peer)
this.add(peer) this.add(peer)
}) })

View File

@ -411,6 +411,66 @@ if(config.p2pBootstrap)
onTorrent(hash, options, (data) => callback(data)) onTorrent(hash, options, (data) => callback(data))
}) })
if(config.p2pReplication)
{
console.log('p2p replication enabled')
p2p.on('randomTorrents', (nil, callback) => {
if(typeof callback != 'function')
return;
sphinx.query('SELECT * FROM `torrents` ORDER BY rand() limit 5', (error, torrents) => {
if(!torrents || torrents.length == 0) {
callback(undefined)
return;
}
let hashes = {}
for(const torrent of torrents)
{
delete torrent.id
hashes[torrent.hash] = torrent
}
const inSql = Object.keys(hashes).map(hash => sphinx.escape(hash)).join(',');
sphinx.query(`SELECT * FROM files WHERE hash IN(${inSql}) limit 50000`, (error, files) => {
if(!files)
{
files = []
}
files.forEach((file) => {
if(!hashes[file.hash].filesList)
hashes[file.hash].filesList = []
hashes[file.hash].filesList.push(file)
})
callback(Object.values(hashes))
})
})
});
const getReplicationTorrents = (nextTimeout = 5000) => {
console.log('call replication')
let gotTorrents = 0
p2p.emit('randomTorrents', null, (torrents) => {
if(!torrents || torrents.length == 0)
return
gotTorrents += torrents.length
torrents.forEach((torrent) => {
console.log('replicate remote torrent', torrent)
insertTorrentToDB(torrent)
})
})
setTimeout(() => getReplicationTorrents(gotTorrents > 8 ? gotTorrents * 600 : 10000), nextTimeout)
}
// start
getReplicationTorrents()
}
const searchTorrentCall = function(text, navigation, callback) const searchTorrentCall = function(text, navigation, callback)
{ {
if(typeof callback != 'function') if(typeof callback != 'function')
@ -985,8 +1045,79 @@ const cleanupTorrents = (cleanTorrents = 1) => {
*/ */
} }
const insertTorrentToDB = (torrent) => {
if(!torrent)
return
const { filesList } = torrent
delete torrent.filesList;
mysqlSingle.query("SELECT id FROM torrents WHERE hash = ?", torrent.hash, (err, single) => {
if(!single)
{
console.log(err)
return
}
if(single.length > 0)
{
return
}
mysqlSingle.insertValues('torrents', torrent, function(err, result) {
if(result) {
send('newTorrent', {
hash: torrent.hash,
name: torrent.name,
size: torrent.size,
files: torrent.files,
piecelength: torrent.piecelength,
contentType: torrent.contentType,
contentCategory: torrent.contentCategory,
});
updateTorrentTrackers(torrent.hash);
}
else
{
console.log(torrent);
console.error(err);
}
});
})
let filesToAdd = filesList.length;
mysqlSingle.query('SELECT count(*) as files_count FROM files WHERE hash = ?', [torrent.hash], function(err, rows) {
if(!rows)
return
const db_files = rows[0]['files_count'];
if(db_files !== torrent.files)
{
mysqlSingle.query('DELETE FROM files WHERE hash = ?', torrent.hash, function (err, result) {
if(err)
{
return;
}
filesList.forEach((file) => {
mysqlSingle.insertValues('files', file, function(err, result) {
if(!result) {
console.log(file);
console.error(err);
return
}
if(--filesToAdd === 0) {
send('filesReady', torrent.hash);
}
});
});
})
}
})
}
const updateTorrent = (metadata, infohash, rinfo) => { const updateTorrent = (metadata, infohash, rinfo) => {
console.log('writing torrent', metadata.info.name, 'to database'); console.log('finded torrent', metadata.info.name, ' and add to database');
const hash = infohash.toString('hex'); const hash = infohash.toString('hex');
let size = metadata.info.length ? metadata.info.length : 0; let size = metadata.info.length ? metadata.info.length : 0;
@ -1019,37 +1150,7 @@ const updateTorrent = (metadata, infohash, rinfo) => {
filesAdd(metadata.info.name, size) filesAdd(metadata.info.name, size)
} }
let filesToAdd = filesArray.length; const torrentQ = {
mysqlSingle.query('SELECT count(*) as files_count FROM files WHERE hash = ?', [hash], function(err, rows) {
if(!rows)
return
const db_files = rows[0]['files_count'];
if(db_files !== filesCount)
{
mysqlSingle.query('DELETE FROM files WHERE hash = ?', hash, function (err, result) {
if(err)
{
return;
}
filesArray.forEach((file) => {
mysqlSingle.insertValues('files', file, function(err, result) {
if(!result) {
console.log(file);
console.error(err);
return
}
if(--filesToAdd === 0) {
send('filesReady', hash);
}
});
});
})
}
})
var torrentQ = {
id: torrentsId++, id: torrentsId++,
hash: hash, hash: hash,
name: metadata.info.name, name: metadata.info.name,
@ -1063,39 +1164,8 @@ const updateTorrent = (metadata, infohash, rinfo) => {
}; };
torrentTypeDetect(torrentQ, filesArray); torrentTypeDetect(torrentQ, filesArray);
torrentQ.filesList = filesArray;
mysqlSingle.query("SELECT id FROM torrents WHERE hash = ?", hash, (err, single) => { insertTorrentToDB(torrentQ)
if(!single)
{
console.log(err)
return
}
if(single.length > 0)
{
return
}
mysqlSingle.insertValues('torrents', torrentQ, function(err, result) {
if(result) {
send('newTorrent', {
hash: hash,
name: metadata.info.name,
size: size,
files: filesCount,
piecelength: metadata.info['piece length'],
contentType: torrentQ.contentType,
contentCategory: torrentQ.contentCategory,
});
updateTorrentTrackers(hash);
}
else
{
console.log(torrentQ);
console.error(err);
}
});
})
} }
client.on('complete', function (metadata, infohash, rinfo) { client.on('complete', function (metadata, infohash, rinfo) {