fix eslint

This commit is contained in:
Alexey Kasyanchuk
2018-06-18 00:06:28 +03:00
parent 93b1b91f56
commit fd4ba2c392
61 changed files with 5528 additions and 5528 deletions

View File

@ -4,17 +4,17 @@ import Page from './page';
import RecentTorrents from './recent-torrents'
export default class ActivityPage extends Page {
constructor(props) {
super(props)
this.setTitle('Rats On The Boat - Content Search Engine');
}
render() {
return (
<div className='column center'>
<div className='column center w100p pad0-75'>
<RecentTorrents />
</div>
</div>
);
}
constructor(props) {
super(props)
this.setTitle('Rats On The Boat - Content Search Engine');
}
render() {
return (
<div className='column center'>
<div className='column center w100p pad0-75'>
<RecentTorrents />
</div>
</div>
);
}
}

View File

@ -20,52 +20,52 @@ if(typeof WEB !== 'undefined')
}
else
{
const { ipcRenderer, remote } = require('electron');
window.currentWindow = remote.getCurrentWindow()
const { ipcRenderer, remote } = require('electron');
window.currentWindow = remote.getCurrentWindow()
window.torrentSocket = {}
window.torrentSocket.callbacks = {}
window.torrentSocket.listeners = {}
window.torrentSocket.on = (name, func) => {
const newListener = (event, ...data) => {
func(...data)
}
window.torrentSocket.listeners[func] = newListener
ipcRenderer.on(name, newListener);
}
window.torrentSocket.off = (name, func) => {
if(!func)
ipcRenderer.removeAllListeners(name);
else
{
const realListener = window.torrentSocket.listeners[func]
if(realListener)
{
ipcRenderer.removeListener(name, realListener);
delete window.torrentSocket.listeners[func]
window.torrentSocket = {}
window.torrentSocket.callbacks = {}
window.torrentSocket.listeners = {}
window.torrentSocket.on = (name, func) => {
const newListener = (event, ...data) => {
func(...data)
}
}
}
window.torrentSocket.emit = (name, ...data) => {
if(typeof data[data.length - 1] === 'function')
{
const id = Math.random().toString(36).substring(5)
window.torrentSocket.callbacks[id] = data[data.length - 1];
data[data.length - 1] = {callback: id}
}
ipcRenderer.send(name, data)
}
ipcRenderer.on('callback', (event, id, data) => {
const callback = window.torrentSocket.callbacks[id]
if(callback)
callback(data)
delete window.torrentSocket.callbacks[id]
});
window.torrentSocket.listeners[func] = newListener
ipcRenderer.on(name, newListener);
}
window.torrentSocket.off = (name, func) => {
if(!func)
ipcRenderer.removeAllListeners(name);
else
{
const realListener = window.torrentSocket.listeners[func]
if(realListener)
{
ipcRenderer.removeListener(name, realListener);
delete window.torrentSocket.listeners[func]
}
}
}
window.torrentSocket.emit = (name, ...data) => {
if(typeof data[data.length - 1] === 'function')
{
const id = Math.random().toString(36).substring(5)
window.torrentSocket.callbacks[id] = data[data.length - 1];
data[data.length - 1] = {callback: id}
}
ipcRenderer.send(name, data)
}
ipcRenderer.on('callback', (event, id, data) => {
const callback = window.torrentSocket.callbacks[id]
if(callback)
callback(data)
delete window.torrentSocket.callbacks[id]
});
ipcRenderer.on('url', (event, url) => {
console.log('url', url)
router(url)
});
ipcRenderer.on('url', (event, url) => {
console.log('url', url)
router(url)
});
}
@ -164,8 +164,8 @@ class App extends Component {
<div>
{
checkNotModal
&&
<Header />
&&
<Header />
}
<PagesPie />
<Footer />

View File

@ -1,259 +1,259 @@
const XXX_BLOCK_WORDS = ['incestcash', 'asacp', 'xondemand', 'yankscash', 'klixxx', 'cybersitter', 'safesurf',
'surfwatch', 'netcash', 'watersport', 'fuck', 'threesome', 'tits',
'masturbating', 'incest', 'bestiality', 'analintercourse', 'analsex', 'animesex',
'anitablonde', 'autosex', 'blackass', 'blackasses', 'boner', 'boobcruise',
'boobies', 'bordello', 'braless', 'brothel', 'callgirl', 'callgirls',
'clit', 'clitoris', 'clits', 'cums', 'cybererotic', 'cybererotica',
'cybersex', 'cybersexx', 'ejaculation', 'erotica', 'eroticfilm', 'eroticfilms',
'eroticism', 'eroticphoto', 'eroticphotography', 'eroticphotos', 'erotics', 'eroticsex',
'eroticsexstories', 'eroticstories', 'eroticstory', 'erotik', 'erotika', 'antasysex',
'gaysex', 'gruppensex', 'horny', 'jackoff', 'lesbo', 'lesbos',
'makinglove', 'motherfucker', 'horsesex', 'dogsex', 'snakesex', 'sextracker',
'tranny', 'bdsm', 'porn', 'beastality', 'bizarre', 'blowjob',
'bondage', 'bondages', 'celebs', 'cum', 'cunt', 'transsexual',
'transsexuals', 'cumbath', 'cumshot', 'dildo', 'dildos', 'transvestites',
'transvestite', 'dominatrix', 'spank', 'dungeon', 'sexbilder', 'dungeons',
'pussie', 'ebony', 'fisting', 'gayshop', 'facesitting', 'sexshop',
'pussy', 'bukkake', 'escort', 'blowjobs', 'facial', 'facialized',
'facials', 'fetish', 'footfetish', 'fuckingpussy', 'fucking', 'hardcore',
'hentai', 'hustlerlatex', 'latex', 'lolita', 'lolitas', 'mlm',
'oralsex', 'peepshow', 'peepshows', 'phonesex', 'pornstars', 'preteen',
'preteens', 'pussies', 'shemale', 'shemales', 'sluts', 'whores',
'whore', 'spanking', 'strapon', 'twinks', 'upskirts', 'voyeur',
'whip', 'whipping', 'xxx', 'sexkey', 'femdom', 'gangbang',
'gloryhole', 'adultbuffet', 'adultcartoons', 'adultchat', 'adultchatnetwork', 'adultdvd',
'adultentertainment', 'adultentertainmenthouse', 'adulterotica', 'adultfilms', 'adultfree', 'adultfreepics',
'adultfreepix', 'adultfun', 'adulthardcore', 'adultimages', 'adultjpeg', 'adultlink',
'adultlinks', 'adultmovie', 'adultmovies', 'adultmpeg', 'adultmpegs', 'adultpass',
'adultpersonals', 'adultphotos', 'adultpic', 'adultpics', 'adultpicture', 'adultpictures',
'adultpix', 'adultporn', 'adultpornography', 'adultsearch', 'adultsex', 'adultsights',
'adultsites', 'adultsonly', 'adultstars', 'adultstories', 'adultvideo', 'adultvideos',
'adultweb', 'adultxxx', 'alt.sex', 'altsex', 'alt.sex.exhibitionism', 'alt.sex.pictures',
'altsexst', 'alt.sex.stories', 'alt.sex.voyeurism', 'amateurerotica', 'amateurfotos', 'amateurhardcore',
'amateurindex', 'amateurnaked', 'amateurnudes', 'amateurporn', 'amateurpussy', 'amateursex',
'amateursxxx', 'amatuerhardcore', 'amatuersex', 'ampland', 'analaction', 'analcum',
'analfuck', 'analfucking', 'analgallery', 'analingus', 'analpics', 'analpicture',
'analsex', 'analsexpics', 'analsexpictures', 'analxxx', 'animalbeastiality', 'animehentai',
'animeporno', 'animexxx', 'asiacarrera', 'assfuck', 'assfucking', 'asshole',
'assholes', 'asslick', 'asspics', 'assworship', 'autofellatio', 'babepix',
'badgirls', 'bakedchicks', 'baldcunt', 'baldpussies', 'baldpussy', 'barecelebs',
'barenaked', 'barepussy', 'beastiality', 'beaverboy', 'beavershots', 'bigballs',
'bigblackboobs', 'bigblackcocks', 'bigblackdicks', 'bigblacktits', 'bigboob', 'bigboobs',
'bigbreast', 'bigbreastlovers', 'bigbreasts', 'bigbutt', 'bigbutts', 'bigclit',
'bigclitoris', 'bigclits', 'bigcock', 'bigcocks', 'bigcunt', 'bigcunts',
'bigdick', 'bigdicks', 'biggestboobs', 'biggestdick', 'biggesttit', 'biggesttits',
'bighairyballs', 'bighardons', 'bighooters', 'bignipples', 'bigpussies', 'bigpussy',
'bigtit', 'bigtits', 'bigtitties', 'bigtitts', 'bizarresex', 'bizarrexxx',
'bizzaresex', 'blackbondage', 'blackboobs', 'blackbooty', 'blackbutt', 'blackcock',
'blackcocks', 'blackcum', 'blackcunt', 'blackcunts', 'blackdick', 'blackdicks',
'blackerotica', 'blackhardcore', 'blacknudes', 'blackonblondes', 'blackporn', 'blackporno',
'blackpornography', 'blackpussy', 'blacksex', 'blacksluts', 'blacksonblondes', 'blacktit',
'blacktits', 'blacktwat', 'blackxxx', 'blondepussy', 'blondsgay', 'bondagefree',
'bondagegallery', 'bondagelinks', 'bondagepics', 'bondagepictures', 'bondagesex', 'bondagestories',
'bondagestory', 'bukake', 'bustybabes', 'bustyblondes', 'bustyceleb', 'butt-fuck',
'buttfuck', 'buttfucker', 'buttfuckers', 'buttfucking', 'buttholes', 'buttman',
'buttpics', 'buttplug', 'buttthumbnails', 'cartoonporn', 'cartoonsex', 'cartoonsxxx',
'cartoonxxx', 'celebritiesnaked', 'celebritiesnude', 'celebritiesxxx', 'celebritybush', 'celebritybutts',
'celebritynude', 'celebritynudes', 'celebrityporn', 'celebritypussy', 'celebritysex', 'celebrityxxx',
'celebsxxx', 'celebxxx', 'centerfolds', 'chaseylain', 'chatsex', 'cheerleadersxxx',
'cheerleaderxxx', 'chickswithdicks', 'christycanyon', 'cicciolina', 'clitpics', 'clitpictures',
'closeuppussy', 'comixxx', 'crotchless', 'cruisingforsex', 'cumbaths', 'cumcoveredcunts',
'cumcunt', 'cumdrinker', 'cumdrinkers', 'cumdrinking', 'cumdrops', 'cumeater',
'cumeaters', 'cumeating', 'cumface', 'cumfaces', 'cumfacial', 'cumfacials',
'cumgallery', 'cumgargle', 'cumguzzlers', 'cumincunt', 'cumjunkies', 'cumlovers',
'cummpeg', 'cumpic', 'cumpics', 'cumpicture', 'cumpictures', 'cumpussy',
'cumsluts', 'cumsucker', 'cumsuckers', 'cumsucking', 'cumswallow', 'cumswallowers',
'cumswallowing', 'cumtasting', 'cumthumbnails', 'cunilingus', 'cunnilingus', 'cuntcum',
'cuntfuck', 'cuntfucking', 'cuntjuice', 'cuntlapper', 'cuntlick', 'cuntlickers',
'cuntlicking', 'cuntpics', 'cuntpictures', 'cunts', 'cuntstories', 'cuntsuckers',
'cuntz', 'cyberlust', 'cybernude', 'cyberporn', 'cyberpornlinks', 'cyberpornsexlinks',
'cyberslut', 'damateur', 'danniashe', 'dicksex', 'dicksuckers', 'dicksucking',
'digixxx', 'directporn', 'dirtypictures', 'dirtysex', 'doggiestyle', 'doggystyle',
'domatrix', 'dominatrixes', 'downblouse', 'drinkingcum', 'drippingcunt', 'drippingcunts',
'drippingpussy', 'easypic.com', 'eatcum', 'eatingcum', 'eatingpussy', 'eatpussy',
'ebony+ayres', 'ebonyporn', 'ebonypussy', 'ebonysex', 'ebonyxxx', 'enormoustits',
'erosvillage', 'eroticanime', 'eroticart', 'eroticas', 'eroticastories', 'eroticfiction',
'eroticlesbianstories', 'eroticmovie', 'eroticmovies', 'erotico', 'eroticos', 'eroticpics',
'eroticpicture', 'eroticpictures', 'eroticsites', 'eroticsounds', 'erotictales', 'erotictext',
'eroticvideo', 'eroticvideos', 'eroticwomen', 'eroticwriting', 'erotikchat', 'erotique',
'erotismo', 'escortservice', 'escortservices', 'eurosex', 'explicitsex', 'facecum',
'facesit', 'facesitters', 'fastporn', 'fatass', 'fatsex', 'feetfetish',
'feetsex', 'felatio', 'fellatio', 'fellations', 'fetishwear', 'fettegirls',
'fingerbang', 'fingerfuck', 'flesh4free', 'footjobs', 'footlicking', 'footworship',
'fornication', 'freeanal', 'freeanalsex', 'freeass', 'freebigboobs', 'freebigtit',
'freebigtits', 'freeblackcunt', 'freeblackpussy', 'freeblowjob', 'freeblowjobs', 'freebondage',
'freeboobs', 'freecum', 'freecumshot', 'freecumshots', 'freecunt', 'freecunts',
'freedick', 'freeerotic', 'freeerotica', 'freeeroticstories', 'freefuck', 'freefucking',
'freefuckpics', 'freegay', 'freegaypics', 'freehardcore', 'freehardcorepics', 'freehardcorepictures',
'freehardcoreporn', 'freehardcoresex', 'freehardcoresexpics', 'freehentai', 'freehooters', 'freelargehooters',
'freelesbian', 'freelesbianporn', 'freelesbiansex', 'freenakedpic', 'freenakedpics', 'freenakedpictures',
'freenakedwomen', 'freenude', 'freenudecelebs', 'freenudephotos', 'freenudepics', 'freenudes',
'freeporn', 'freepornmovies', 'freeporno', 'freepornography', 'freepornopics', 'freepornopictures',
'freepornos', 'freepornpic', 'freepornpics', 'freepornpictures', 'freeporns', 'freepornsite',
'freepornsites', 'freepussy', 'freepussypic', 'freepussypics', 'freepussypictures', 'freesex',
'freesexchat', 'freesexmovies', 'freesexnet', 'freesexphotos', 'freesexpic', 'freesexpics',
'freesexpicture', 'freesexpictures', 'freesexsites', 'freesexstories', 'freesexvideos', 'freesexycam',
'freeshemale', 'freesmut', 'freetit', 'freetits', 'freevoyeur', 'freexxx',
'freexxxmovie', 'freexxxmovies', 'freexxxmpeg', 'freexxxphotos', 'freexxxpic', 'freexxxpics',
'freexxxpicture', 'freexxxpictures', 'freexxxstories', 'freexxxvideo', 'freexxxx', 'fuking',
'gangbangs', 'gratishardcoregalerie', 'hardcorecelebs', 'hardcorefisting', 'hardcorefree', 'hardcorefucking',
'hardcorehooters', 'hardcorejunkie', 'hardcorejunky', 'hardcoremovie', 'hardcoremovies', 'hardcorepic',
'hardcorepics', 'hardcorepictures', 'hardcorepix', 'hardcoreporn', 'hardcoreporno', 'hardcorepornography',
'hardcorepornos', 'hardcorepussy', 'hardcoresamples', 'hardcoresex', 'hardcoresexpictures', 'hardcorestories',
'hardcorethumbnails', 'hardcorevideo', 'hardcorevideos', 'hardcorexxx', 'harddicks', 'hardnipples',
'hardons', 'hardporn', 'indiasex', 'interacialhardcore', 'interacialsex', 'interacialxxx',
'intercoursepositions', 'internetsex', 'interracialfucking', 'interracialhardcore', 'interracialporn', 'interracialpornography',
'interracialsex', 'interracialsexstories', 'interracialxxx', 'intimatesex', 'ittybittytitty', 'japaneseporn',
'japanesesex', 'japansex', 'japanxxx', 'jennacam.com', 'jennajameson', 'jennicam',
'jerkoff', 'jism', 'jiz', 'jizz', 'juicycunts', 'juicypussy',
'justhardcore', 'karasamateurs', 'karasxxx', 'kascha', 'kaylakleevage', 'kobetai',
'koreasex', 'lapdance', 'largeclits', 'largecunts', 'largedicks', 'largehooters',
'largepussy', 'largetit', 'largetits', 'latinapussy', 'latinasex', 'latinosex',
'latinoxxx', 'latinxxx', 'legsex', 'lesbianerotica', 'lesbianhardcore', 'lesbianorgies',
'lesbianorgy', 'lesbianpics', 'lesbianpink', 'lesbianporn', 'lesbian-porno', 'lesbianporno',
'lesbianpornography', 'lesbianpornos', 'lesbianpussy', 'lesbiansex', 'lesbiansexpics', 'lesbiansexstories',
'lesbianxxx', 'lickadick', 'lickingpussy', 'lindalovelace', 'lingeriexxx', 'literotica',
'littleboobs', 'littlepussy', 'littletits', 'livefucking', 'liveporn', 'livesex',
'livesexcam', 'livesexcams', 'longdicks', 'lovedoll', 'lovedolls', 'magazinessex',
'makinglove', 'malaysex', 'malenudes', 'malesex', 'mangaporno', 'mangax',
'mangaxxx', 'manpics', 'mansex', 'marilynchambers', 'massivecocks', 'masterbating',
'masturbation', 'masturbationvideos', 'maturefucking', 'maturesex', 'maturexxx', 'megaboobs',
'megaporno', 'megapussy', 'megatits', 'mensdicks', 'mensex', 'm.i.l.f.',
'milf', 'milfhunterpic', 'milfmpegsample', 'modelsex', 'mondoporn', 'monstercocks',
'monsterdicks', 'motherfucker', 'moviepost.com', 'mpegsex', 'mpegxxx', 'muffdiving',
'myslutwife', 'nacktfotos', 'nakedbabes', 'nakedblackwomen', 'nakedcelebrity', 'nakedcelebs',
'nakedcheerleader', 'nakedchicks', 'nakedgirls', 'nakedguys', 'nakedladies', 'nakedlady',
'nakedman', 'nakedmen', 'nakedness', 'nakedphotographs', 'nakedphotography', 'nakedphotos',
'nakedpic', 'nakedpics', 'nakedpicture', 'nakedpictures', 'nakedpussy', 'nakedstars',
'nakedwife', 'nakedwoman', 'nakedwomen', 'nastychat', 'nastypussy', 'nastysex',
'nastythumbs', 'naturaltits', 'naughty.com', 'naughtylinks', 'naughtylinx', 'naughtylynx',
'naughtynurses', 'netsex', 'niceass', 'nicetits', 'nikkinova', 'nikkityler',
'nipples', 'nookie', 'nookies', 'nudeactress', 'nudeactresses', 'nudeamateur',
'nudeamateurs', 'nudeasianwomen', 'nudebabes', 'nudebigboobs', 'nudeblack', 'nudeblackwomen',
'nudeblondes', 'nudeceleb', 'nudeceleberties', 'nudecelebraties', 'nudecelebrites', 'nudecelebrities',
'nudecelebrity', 'nudecelebs', 'nudecollegegirls', 'nudefemale', 'nudefemales', 'nudefree',
'nudegay', 'nudeimages', 'nudeladies', 'nudelesbians', 'nudemale', 'nudemales',
'nudeman', 'nudemen', 'nudepic', 'nudepics', 'nudepicture', 'nudepictures',
'nudepornography', 'nudepussy', 'nuderaider', 'nuderedheads', 'nudes', 'nudesex',
'nudestar', 'nudestars', 'nudevideoconferencing', 'nudewoman', 'nudewomen', 'nudism',
'nudist', 'nudists', 'nudity', 'nylonfetish', 'nylonsex', 'nympho',
'nymphos', 'olderbabes', 'oldersex', 'oldersluts', 'oldpussy', 'oldsex',
'oldsluts', 'openlegs', 'openpussy', 'oral4free', 'oral-sex', 'oralsexpictures',
'orgie', 'orgies', 'orgypics', 'orgys', 'pantyhosefetish', 'pantyhosesex',
'peepcam', 'persiankitty', 'persiankitty.com', 'perverted', 'picsxxx', 'picturesex',
'picturesofsex', 'picturessex', 'pimpserver', 'pimpserver.com', 'pinkpussy', 'pissing',
'pixxx', 'poontang', 'pornagraphy', 'porncast', 'porncity', 'porndirectory',
'porne', 'pornfree', 'pornmovies', 'pornno', 'pornnude', 'porno',
'pornoadult', 'pornocartoons', 'pornochat', 'pornodeluxe', 'pornoe', 'pornofilms',
'pornofree', 'pornogame', 'pornografi', 'pornografia', 'pornografie', 'pornografy',
'pornograph', 'pornographi', 'pornographia', 'pornographic', 'pornographicpictures', 'pornographics',
'pornographicvideo', 'pornographicvideos', 'pornographie', 'pornography', 'pornographyfree', 'pornographyphotos',
'pornographypictures', 'pornography-sex', 'pornographysex', 'pornoland', 'pornolinks', 'pornolynx',
'pornomagazines', 'pornomovies', 'pornompeg', 'pornophotos', 'pornopic', 'pornopics',
'pornopicture', 'pornopictures', 'pornos', 'pornosex', 'pornosite', 'pornosites',
'pornostar', 'pornostories', 'pornovideo', 'pornovideos', 'pornoxxx', 'pornphotos',
'pornpics', 'pornpictures', 'pornpix', 'pornpost', 'pornqueens', 'pornrated',
'porns', 'pornsex', 'pornstar', 'pornstories', 'porntrack', 'pornvideo',
'pornvideos', 'pornypics', 'porstars', 'privatesex', 'privatex', 'privatexx',
'privatexxx', 'prono', 'pronography', 'publicnudity', 'puffynipples', 'purple+passion',
'pussyboard', 'pussycam', 'pussycams', 'pussycloseup', 'pussycloseups', 'pussycum',
'pussycunt', 'pussyeater', 'pussyeaters', 'pussyeating', 'pussyfuck', 'pussyfucking',
'pussyhair', 'pussyheaven', 'pussyjuice', 'pussylickers', 'pussylicking', 'pussylink',
'pussylips', 'pussyphotos', 'pussyporno', 'pussys', 'pussysex', 'pussyshots',
'pussythumbnails', 'pussytits', 'pusy', 'racqueldarrian', 'rape', 'rawlinks',
'rawpussy', 'rawsex', 'realhardcore', 'realsex', 'redneckporn', 'redpussy',
'rubberfetish', 'russianxxx', 'scat', 'schiffernude', 'seka', 'seniorsex',
'sex4free', 'sex66', 'sexacts', 'sexadult', 'sexaids', 'sexamateur',
'sexanal', 'sexandpictures', 'sexaphone', 'sexaudio', 'sexavi', 'sexbondage',
'sexboner', 'sexbuttfucker', 'sexcam', 'sexcams', 'sexchat', 'sexchatrooms',
'sexcites', 'sexclub', 'sexclubs', 'sex.com', 'sexdating', 'sexe',
'sexfantasies', 'sexfantasy', 'sexfilm', 'sexgallery', 'sexgame', 'sexgames',
'sexgirl', 'sexgirls', 'sexgroup', 'sexguide', 'sexhardcore', 'sexhphoto',
'sexhungry', 'sexhungryjoe', 'sexhungryjoes', 'sexi', 'seximages', 'sexis',
'sexjapan', 'sex.jpg', 'sexlinda', 'sexlinks', 'sexlinx', 'sexlive',
'sexmagazine', 'sexmagazines', 'sexmature', 'sexmelayu', 'sexmodels', 'sexmovie',
'sexmovies', 'sexmpeg', 'sexnude', 'sexnudity', 'sexo', 'sexoanal',
'sexole', 'sexontheinternet', 'sexoral', 'sexpasswords', 'sexphoto', 'sexphotos',
'sexpic', 'sexpicnet', 'sexpics', 'sexpicture', 'sexpictures', 'sexpicturesfree',
'sexpix', 'sexplaza', 'sexporn', 'sexporno', 'sexpositions', 'sexpussy',
'sexroulette', 'sexs', 'sexsamples', 'sexsearch', 'sexsex', 'sexsexsex',
'sexshare', 'sexshops', 'sexshow', 'sexshows', 'sexsite', 'sexsites',
'sexslave', 'sexsound', 'sexsounds', 'sexsource', 'sexspaces', 'sexspaces.com',
'sexstories', 'sexstory', 'sexstorys', 'sextalk', 'sexteen', 'sexthumbnails',
'sex-toons', 'sextoons', 'sextour', 'sextoy', 'sextoys', 'sextpus',
'sextracker.com', 'sextropolis', 'sexualbondage', 'sexualfantasies', 'sexualfantasy', 'sexualintercourse',
'sexualpictures', 'sexualpleasure', 'sexualpositions', 'sexuncensored', 'sexvideo', 'sexvision',
'sexvote', 'sexwomen', 'sexworld', 'sexx', 'sexxx', 'sexxxx',
'sexxxxx', 'sexyamateurs', 'sexybabes', 'sexyblack', 'sexyboobs', 'sexybookmark',
'sexybookmarka', 'sexybookmarks', 'sexybutts', 'sexycunts', 'sexygirl', 'sexygirls',
'sexyladies', 'sexylady', 'sexylegs', 'sexylingerie', 'sexymen', 'sexyphotos',
'sexypics', 'sexypictures', 'sexypost', 'sexypussies', 'sexypussy', 'sexysites',
'sexystories', 'sexywoman', 'sexywomen', 'shavedcunt', 'shavedcunts', 'shavedpussies',
'shavedpussy', 'shavedpussypics', 'shavedsluts', 'shavedwomen', 'shavenpussies', 'shavenpussy',
'shavepussy', 'sheboy', 'she-male', 'shemale+video', 'shemaleyum', 'showcam',
'showcams', 'showercam', 'showercams', 'slavesex', 'smallboobs', 'smallbreasts',
'smalldicks', 'smallpussy', 'smalltits', 'smut', 'smut69', 'smutland',
'smutserver', 'smutshack', 'softcore', 'softporn', 'softpornography', 'spankingpage',
'spreadpussy', 'spycamadult', 'stockingsex', 'storieserotic', 'storiessex', 'straightsex',
'stretchedcunt', 'stripclub', 'stripclubs', 'stripshow', 'striptease', 'strokeit',
'strokeme', 'suckdick', 'sucksex', 'superchicken', 'supersex', 'supertits',
'swallowcum', 'swinger', 'swingerclub', 'swingers', 'swollenclits', 'syberporn',
'sylviasaint', 'teenanal', 'teenanalsex', 'teenhardcore', 'teenie', 'teenies',
'teennude', 'teenpics', 'teenporn', 'teenporno', 'teenpussy', 'teensex',
'teenslut', 'teensluts', 'teensuck', 'teenxxx', 'tgp', 'thaipussy',
'thaisex', 'thehun', 'threesomes', 'thumblords', 'thumbnailssex', 'thumbzilla',
'tiffanytowers', 'tightcunt', 'tightpussies', 'tightpussy', 'tinypussy', 'tinytits',
'tinytitties', 'tisandass', 'tit', 'titbondage', 'titfuck', 'titfucking',
'tities', 'titman', 'titsandass', 'titsass', 'titties', 'titts',
'titty', 'tittyfuck', 'tittyfucking', 'tittys', 'tokyotopless', 'tommysbookmark',
'toplesswomen', 'trannies', 'transexual', 'transsexuels', 'twat', 'twats',
'twink', 'ultradonkey', 'ultrahardcore', 'uncutcocks', 'vaginalintercourse', 'vaginapictures',
'videoporno', 'videosex', 'videoxxx', 'vintageporn', 'virtualsex', 'vividtv',
'wank', 'wankers', 'wanking', 'wendywhoppers', 'wetcunt', 'wetcunts',
'wetdick', 'wetpanties', 'wetpussies', 'wetpussy', 'wetsex', 'wetvagina',
'whitepussy', 'whitesex', 'whitetits', 'whitexxx', 'wifesharing', 'wifeswapping',
'wildpussy', 'wildsex', 'womensex', 'worldsex', 'worldsexguide', 'x-rated',
'xrated', 'x-ratedmovie', 'x-ratedmovies', 'x-ratedvideo', 'x-ratedvideos', 'xxx4free',
'xxxadult', 'xxxadultmovies', 'xxxadultvideo', 'xxxadultvideos', 'xxxbabes', 'xxxcam',
'xxxcelebrities', 'xxxfree', 'xxxfreepics', 'xxxgalleries', 'xxxhardcore', 'xxxjapan',
'xxxlinks', 'xxxmodels', 'xxxmovie', 'xxxmovies', 'xxxmpeg', 'xxxpassword',
'xxxphoto', 'xxxphotos', 'xxxpic', 'xxx-pics', 'xxxpics', 'xxxpicture',
'xxxpictures', 'xxxpicturesfree', 'xxxpicx', 'xxxpirtures', 'xxxporn', 'xxxporno',
'xxxpussy', 'xxx-rated', 'xxxrated', 'xxxsex', 'xxxsites', 'xxxsluts',
'xxxstories', 'xxxteen', 'xxxthumbnails', 'xxxthumbs', 'xxxtoons', 'xxxtoys',
'xxxvideo', 'xxxvideos', 'xxxwomen', 'xxxx', 'xxxxx', 'xxxxxx',
'filthyfarm', 'adultcheck', 'erotic', 'mistresses', 'naughty',
'adult', 'amateur', 'amateurs', 'anal', 'analau', 'poker',
'anally', 'asian', 'swinging', 'ass', 'orgy', 'sexvideos',
'babe', 'babes', 'bare', 'glamour', 'casino', 'bed',
'vibrator', 'bikini', 'bisexual', 'blonde', 'breasted', 'breasts',
'busty', 'butts', 'candy', 'caning', 'caps', 'catholic',
'chained', 'chested', 'chubby', 'close-ups', 'coupling',
'cucumber', 'doll', 'dolls', 'dolly', 'dominating', 'drinking',
'drunk', 'fat', 'feet', 'fingering', 'fishnet', 'flasher',
'footjob', 'foreplay', 'foreplaying', 'girlfriend', 'girlfriends', 'gymnast',
'hairy', 'holly', 'horse', 'housewife', 'houswife', 'insertion',
'insertions', 'interracial', 'jerka', 'juicy', 'kissing', 'kitty',
'lactating', 'ladyboy', 'lance', 'latina', 'latins', 'leather',
'legs', 'lesbian', 'lesbians', 'licking', 'lingerie', 'lubricants',
'mature', 'midget', 'minor', 'mistress', 'nude',
'older', 'olez', 'oral', 'panties', 'pantyhose', 'pedro',
'peeing', 'penetration', 'pigtailed', 'pleasure', 'plump',
'plumper', 'poser', 'posers', 'poses', 'posing', 'prosecuted',
'punishment', 'reproducing', 'sex', 'shaved', 'shock', 'slave',
'sleeping', 'speculums', 'spread', 'spreading', 'squirting', 'stripper',
'stripping', 'swapping', 'thong', 'topless', 'toying',
'trix', 'undressing', 'uniform', 'whipcream', 'brazzers', 'порно',
'порн', 'лесб', 'гей', 'геи', 'прон'
'surfwatch', 'netcash', 'watersport', 'fuck', 'threesome', 'tits',
'masturbating', 'incest', 'bestiality', 'analintercourse', 'analsex', 'animesex',
'anitablonde', 'autosex', 'blackass', 'blackasses', 'boner', 'boobcruise',
'boobies', 'bordello', 'braless', 'brothel', 'callgirl', 'callgirls',
'clit', 'clitoris', 'clits', 'cums', 'cybererotic', 'cybererotica',
'cybersex', 'cybersexx', 'ejaculation', 'erotica', 'eroticfilm', 'eroticfilms',
'eroticism', 'eroticphoto', 'eroticphotography', 'eroticphotos', 'erotics', 'eroticsex',
'eroticsexstories', 'eroticstories', 'eroticstory', 'erotik', 'erotika', 'antasysex',
'gaysex', 'gruppensex', 'horny', 'jackoff', 'lesbo', 'lesbos',
'makinglove', 'motherfucker', 'horsesex', 'dogsex', 'snakesex', 'sextracker',
'tranny', 'bdsm', 'porn', 'beastality', 'bizarre', 'blowjob',
'bondage', 'bondages', 'celebs', 'cum', 'cunt', 'transsexual',
'transsexuals', 'cumbath', 'cumshot', 'dildo', 'dildos', 'transvestites',
'transvestite', 'dominatrix', 'spank', 'dungeon', 'sexbilder', 'dungeons',
'pussie', 'ebony', 'fisting', 'gayshop', 'facesitting', 'sexshop',
'pussy', 'bukkake', 'escort', 'blowjobs', 'facial', 'facialized',
'facials', 'fetish', 'footfetish', 'fuckingpussy', 'fucking', 'hardcore',
'hentai', 'hustlerlatex', 'latex', 'lolita', 'lolitas', 'mlm',
'oralsex', 'peepshow', 'peepshows', 'phonesex', 'pornstars', 'preteen',
'preteens', 'pussies', 'shemale', 'shemales', 'sluts', 'whores',
'whore', 'spanking', 'strapon', 'twinks', 'upskirts', 'voyeur',
'whip', 'whipping', 'xxx', 'sexkey', 'femdom', 'gangbang',
'gloryhole', 'adultbuffet', 'adultcartoons', 'adultchat', 'adultchatnetwork', 'adultdvd',
'adultentertainment', 'adultentertainmenthouse', 'adulterotica', 'adultfilms', 'adultfree', 'adultfreepics',
'adultfreepix', 'adultfun', 'adulthardcore', 'adultimages', 'adultjpeg', 'adultlink',
'adultlinks', 'adultmovie', 'adultmovies', 'adultmpeg', 'adultmpegs', 'adultpass',
'adultpersonals', 'adultphotos', 'adultpic', 'adultpics', 'adultpicture', 'adultpictures',
'adultpix', 'adultporn', 'adultpornography', 'adultsearch', 'adultsex', 'adultsights',
'adultsites', 'adultsonly', 'adultstars', 'adultstories', 'adultvideo', 'adultvideos',
'adultweb', 'adultxxx', 'alt.sex', 'altsex', 'alt.sex.exhibitionism', 'alt.sex.pictures',
'altsexst', 'alt.sex.stories', 'alt.sex.voyeurism', 'amateurerotica', 'amateurfotos', 'amateurhardcore',
'amateurindex', 'amateurnaked', 'amateurnudes', 'amateurporn', 'amateurpussy', 'amateursex',
'amateursxxx', 'amatuerhardcore', 'amatuersex', 'ampland', 'analaction', 'analcum',
'analfuck', 'analfucking', 'analgallery', 'analingus', 'analpics', 'analpicture',
'analsex', 'analsexpics', 'analsexpictures', 'analxxx', 'animalbeastiality', 'animehentai',
'animeporno', 'animexxx', 'asiacarrera', 'assfuck', 'assfucking', 'asshole',
'assholes', 'asslick', 'asspics', 'assworship', 'autofellatio', 'babepix',
'badgirls', 'bakedchicks', 'baldcunt', 'baldpussies', 'baldpussy', 'barecelebs',
'barenaked', 'barepussy', 'beastiality', 'beaverboy', 'beavershots', 'bigballs',
'bigblackboobs', 'bigblackcocks', 'bigblackdicks', 'bigblacktits', 'bigboob', 'bigboobs',
'bigbreast', 'bigbreastlovers', 'bigbreasts', 'bigbutt', 'bigbutts', 'bigclit',
'bigclitoris', 'bigclits', 'bigcock', 'bigcocks', 'bigcunt', 'bigcunts',
'bigdick', 'bigdicks', 'biggestboobs', 'biggestdick', 'biggesttit', 'biggesttits',
'bighairyballs', 'bighardons', 'bighooters', 'bignipples', 'bigpussies', 'bigpussy',
'bigtit', 'bigtits', 'bigtitties', 'bigtitts', 'bizarresex', 'bizarrexxx',
'bizzaresex', 'blackbondage', 'blackboobs', 'blackbooty', 'blackbutt', 'blackcock',
'blackcocks', 'blackcum', 'blackcunt', 'blackcunts', 'blackdick', 'blackdicks',
'blackerotica', 'blackhardcore', 'blacknudes', 'blackonblondes', 'blackporn', 'blackporno',
'blackpornography', 'blackpussy', 'blacksex', 'blacksluts', 'blacksonblondes', 'blacktit',
'blacktits', 'blacktwat', 'blackxxx', 'blondepussy', 'blondsgay', 'bondagefree',
'bondagegallery', 'bondagelinks', 'bondagepics', 'bondagepictures', 'bondagesex', 'bondagestories',
'bondagestory', 'bukake', 'bustybabes', 'bustyblondes', 'bustyceleb', 'butt-fuck',
'buttfuck', 'buttfucker', 'buttfuckers', 'buttfucking', 'buttholes', 'buttman',
'buttpics', 'buttplug', 'buttthumbnails', 'cartoonporn', 'cartoonsex', 'cartoonsxxx',
'cartoonxxx', 'celebritiesnaked', 'celebritiesnude', 'celebritiesxxx', 'celebritybush', 'celebritybutts',
'celebritynude', 'celebritynudes', 'celebrityporn', 'celebritypussy', 'celebritysex', 'celebrityxxx',
'celebsxxx', 'celebxxx', 'centerfolds', 'chaseylain', 'chatsex', 'cheerleadersxxx',
'cheerleaderxxx', 'chickswithdicks', 'christycanyon', 'cicciolina', 'clitpics', 'clitpictures',
'closeuppussy', 'comixxx', 'crotchless', 'cruisingforsex', 'cumbaths', 'cumcoveredcunts',
'cumcunt', 'cumdrinker', 'cumdrinkers', 'cumdrinking', 'cumdrops', 'cumeater',
'cumeaters', 'cumeating', 'cumface', 'cumfaces', 'cumfacial', 'cumfacials',
'cumgallery', 'cumgargle', 'cumguzzlers', 'cumincunt', 'cumjunkies', 'cumlovers',
'cummpeg', 'cumpic', 'cumpics', 'cumpicture', 'cumpictures', 'cumpussy',
'cumsluts', 'cumsucker', 'cumsuckers', 'cumsucking', 'cumswallow', 'cumswallowers',
'cumswallowing', 'cumtasting', 'cumthumbnails', 'cunilingus', 'cunnilingus', 'cuntcum',
'cuntfuck', 'cuntfucking', 'cuntjuice', 'cuntlapper', 'cuntlick', 'cuntlickers',
'cuntlicking', 'cuntpics', 'cuntpictures', 'cunts', 'cuntstories', 'cuntsuckers',
'cuntz', 'cyberlust', 'cybernude', 'cyberporn', 'cyberpornlinks', 'cyberpornsexlinks',
'cyberslut', 'damateur', 'danniashe', 'dicksex', 'dicksuckers', 'dicksucking',
'digixxx', 'directporn', 'dirtypictures', 'dirtysex', 'doggiestyle', 'doggystyle',
'domatrix', 'dominatrixes', 'downblouse', 'drinkingcum', 'drippingcunt', 'drippingcunts',
'drippingpussy', 'easypic.com', 'eatcum', 'eatingcum', 'eatingpussy', 'eatpussy',
'ebony+ayres', 'ebonyporn', 'ebonypussy', 'ebonysex', 'ebonyxxx', 'enormoustits',
'erosvillage', 'eroticanime', 'eroticart', 'eroticas', 'eroticastories', 'eroticfiction',
'eroticlesbianstories', 'eroticmovie', 'eroticmovies', 'erotico', 'eroticos', 'eroticpics',
'eroticpicture', 'eroticpictures', 'eroticsites', 'eroticsounds', 'erotictales', 'erotictext',
'eroticvideo', 'eroticvideos', 'eroticwomen', 'eroticwriting', 'erotikchat', 'erotique',
'erotismo', 'escortservice', 'escortservices', 'eurosex', 'explicitsex', 'facecum',
'facesit', 'facesitters', 'fastporn', 'fatass', 'fatsex', 'feetfetish',
'feetsex', 'felatio', 'fellatio', 'fellations', 'fetishwear', 'fettegirls',
'fingerbang', 'fingerfuck', 'flesh4free', 'footjobs', 'footlicking', 'footworship',
'fornication', 'freeanal', 'freeanalsex', 'freeass', 'freebigboobs', 'freebigtit',
'freebigtits', 'freeblackcunt', 'freeblackpussy', 'freeblowjob', 'freeblowjobs', 'freebondage',
'freeboobs', 'freecum', 'freecumshot', 'freecumshots', 'freecunt', 'freecunts',
'freedick', 'freeerotic', 'freeerotica', 'freeeroticstories', 'freefuck', 'freefucking',
'freefuckpics', 'freegay', 'freegaypics', 'freehardcore', 'freehardcorepics', 'freehardcorepictures',
'freehardcoreporn', 'freehardcoresex', 'freehardcoresexpics', 'freehentai', 'freehooters', 'freelargehooters',
'freelesbian', 'freelesbianporn', 'freelesbiansex', 'freenakedpic', 'freenakedpics', 'freenakedpictures',
'freenakedwomen', 'freenude', 'freenudecelebs', 'freenudephotos', 'freenudepics', 'freenudes',
'freeporn', 'freepornmovies', 'freeporno', 'freepornography', 'freepornopics', 'freepornopictures',
'freepornos', 'freepornpic', 'freepornpics', 'freepornpictures', 'freeporns', 'freepornsite',
'freepornsites', 'freepussy', 'freepussypic', 'freepussypics', 'freepussypictures', 'freesex',
'freesexchat', 'freesexmovies', 'freesexnet', 'freesexphotos', 'freesexpic', 'freesexpics',
'freesexpicture', 'freesexpictures', 'freesexsites', 'freesexstories', 'freesexvideos', 'freesexycam',
'freeshemale', 'freesmut', 'freetit', 'freetits', 'freevoyeur', 'freexxx',
'freexxxmovie', 'freexxxmovies', 'freexxxmpeg', 'freexxxphotos', 'freexxxpic', 'freexxxpics',
'freexxxpicture', 'freexxxpictures', 'freexxxstories', 'freexxxvideo', 'freexxxx', 'fuking',
'gangbangs', 'gratishardcoregalerie', 'hardcorecelebs', 'hardcorefisting', 'hardcorefree', 'hardcorefucking',
'hardcorehooters', 'hardcorejunkie', 'hardcorejunky', 'hardcoremovie', 'hardcoremovies', 'hardcorepic',
'hardcorepics', 'hardcorepictures', 'hardcorepix', 'hardcoreporn', 'hardcoreporno', 'hardcorepornography',
'hardcorepornos', 'hardcorepussy', 'hardcoresamples', 'hardcoresex', 'hardcoresexpictures', 'hardcorestories',
'hardcorethumbnails', 'hardcorevideo', 'hardcorevideos', 'hardcorexxx', 'harddicks', 'hardnipples',
'hardons', 'hardporn', 'indiasex', 'interacialhardcore', 'interacialsex', 'interacialxxx',
'intercoursepositions', 'internetsex', 'interracialfucking', 'interracialhardcore', 'interracialporn', 'interracialpornography',
'interracialsex', 'interracialsexstories', 'interracialxxx', 'intimatesex', 'ittybittytitty', 'japaneseporn',
'japanesesex', 'japansex', 'japanxxx', 'jennacam.com', 'jennajameson', 'jennicam',
'jerkoff', 'jism', 'jiz', 'jizz', 'juicycunts', 'juicypussy',
'justhardcore', 'karasamateurs', 'karasxxx', 'kascha', 'kaylakleevage', 'kobetai',
'koreasex', 'lapdance', 'largeclits', 'largecunts', 'largedicks', 'largehooters',
'largepussy', 'largetit', 'largetits', 'latinapussy', 'latinasex', 'latinosex',
'latinoxxx', 'latinxxx', 'legsex', 'lesbianerotica', 'lesbianhardcore', 'lesbianorgies',
'lesbianorgy', 'lesbianpics', 'lesbianpink', 'lesbianporn', 'lesbian-porno', 'lesbianporno',
'lesbianpornography', 'lesbianpornos', 'lesbianpussy', 'lesbiansex', 'lesbiansexpics', 'lesbiansexstories',
'lesbianxxx', 'lickadick', 'lickingpussy', 'lindalovelace', 'lingeriexxx', 'literotica',
'littleboobs', 'littlepussy', 'littletits', 'livefucking', 'liveporn', 'livesex',
'livesexcam', 'livesexcams', 'longdicks', 'lovedoll', 'lovedolls', 'magazinessex',
'makinglove', 'malaysex', 'malenudes', 'malesex', 'mangaporno', 'mangax',
'mangaxxx', 'manpics', 'mansex', 'marilynchambers', 'massivecocks', 'masterbating',
'masturbation', 'masturbationvideos', 'maturefucking', 'maturesex', 'maturexxx', 'megaboobs',
'megaporno', 'megapussy', 'megatits', 'mensdicks', 'mensex', 'm.i.l.f.',
'milf', 'milfhunterpic', 'milfmpegsample', 'modelsex', 'mondoporn', 'monstercocks',
'monsterdicks', 'motherfucker', 'moviepost.com', 'mpegsex', 'mpegxxx', 'muffdiving',
'myslutwife', 'nacktfotos', 'nakedbabes', 'nakedblackwomen', 'nakedcelebrity', 'nakedcelebs',
'nakedcheerleader', 'nakedchicks', 'nakedgirls', 'nakedguys', 'nakedladies', 'nakedlady',
'nakedman', 'nakedmen', 'nakedness', 'nakedphotographs', 'nakedphotography', 'nakedphotos',
'nakedpic', 'nakedpics', 'nakedpicture', 'nakedpictures', 'nakedpussy', 'nakedstars',
'nakedwife', 'nakedwoman', 'nakedwomen', 'nastychat', 'nastypussy', 'nastysex',
'nastythumbs', 'naturaltits', 'naughty.com', 'naughtylinks', 'naughtylinx', 'naughtylynx',
'naughtynurses', 'netsex', 'niceass', 'nicetits', 'nikkinova', 'nikkityler',
'nipples', 'nookie', 'nookies', 'nudeactress', 'nudeactresses', 'nudeamateur',
'nudeamateurs', 'nudeasianwomen', 'nudebabes', 'nudebigboobs', 'nudeblack', 'nudeblackwomen',
'nudeblondes', 'nudeceleb', 'nudeceleberties', 'nudecelebraties', 'nudecelebrites', 'nudecelebrities',
'nudecelebrity', 'nudecelebs', 'nudecollegegirls', 'nudefemale', 'nudefemales', 'nudefree',
'nudegay', 'nudeimages', 'nudeladies', 'nudelesbians', 'nudemale', 'nudemales',
'nudeman', 'nudemen', 'nudepic', 'nudepics', 'nudepicture', 'nudepictures',
'nudepornography', 'nudepussy', 'nuderaider', 'nuderedheads', 'nudes', 'nudesex',
'nudestar', 'nudestars', 'nudevideoconferencing', 'nudewoman', 'nudewomen', 'nudism',
'nudist', 'nudists', 'nudity', 'nylonfetish', 'nylonsex', 'nympho',
'nymphos', 'olderbabes', 'oldersex', 'oldersluts', 'oldpussy', 'oldsex',
'oldsluts', 'openlegs', 'openpussy', 'oral4free', 'oral-sex', 'oralsexpictures',
'orgie', 'orgies', 'orgypics', 'orgys', 'pantyhosefetish', 'pantyhosesex',
'peepcam', 'persiankitty', 'persiankitty.com', 'perverted', 'picsxxx', 'picturesex',
'picturesofsex', 'picturessex', 'pimpserver', 'pimpserver.com', 'pinkpussy', 'pissing',
'pixxx', 'poontang', 'pornagraphy', 'porncast', 'porncity', 'porndirectory',
'porne', 'pornfree', 'pornmovies', 'pornno', 'pornnude', 'porno',
'pornoadult', 'pornocartoons', 'pornochat', 'pornodeluxe', 'pornoe', 'pornofilms',
'pornofree', 'pornogame', 'pornografi', 'pornografia', 'pornografie', 'pornografy',
'pornograph', 'pornographi', 'pornographia', 'pornographic', 'pornographicpictures', 'pornographics',
'pornographicvideo', 'pornographicvideos', 'pornographie', 'pornography', 'pornographyfree', 'pornographyphotos',
'pornographypictures', 'pornography-sex', 'pornographysex', 'pornoland', 'pornolinks', 'pornolynx',
'pornomagazines', 'pornomovies', 'pornompeg', 'pornophotos', 'pornopic', 'pornopics',
'pornopicture', 'pornopictures', 'pornos', 'pornosex', 'pornosite', 'pornosites',
'pornostar', 'pornostories', 'pornovideo', 'pornovideos', 'pornoxxx', 'pornphotos',
'pornpics', 'pornpictures', 'pornpix', 'pornpost', 'pornqueens', 'pornrated',
'porns', 'pornsex', 'pornstar', 'pornstories', 'porntrack', 'pornvideo',
'pornvideos', 'pornypics', 'porstars', 'privatesex', 'privatex', 'privatexx',
'privatexxx', 'prono', 'pronography', 'publicnudity', 'puffynipples', 'purple+passion',
'pussyboard', 'pussycam', 'pussycams', 'pussycloseup', 'pussycloseups', 'pussycum',
'pussycunt', 'pussyeater', 'pussyeaters', 'pussyeating', 'pussyfuck', 'pussyfucking',
'pussyhair', 'pussyheaven', 'pussyjuice', 'pussylickers', 'pussylicking', 'pussylink',
'pussylips', 'pussyphotos', 'pussyporno', 'pussys', 'pussysex', 'pussyshots',
'pussythumbnails', 'pussytits', 'pusy', 'racqueldarrian', 'rape', 'rawlinks',
'rawpussy', 'rawsex', 'realhardcore', 'realsex', 'redneckporn', 'redpussy',
'rubberfetish', 'russianxxx', 'scat', 'schiffernude', 'seka', 'seniorsex',
'sex4free', 'sex66', 'sexacts', 'sexadult', 'sexaids', 'sexamateur',
'sexanal', 'sexandpictures', 'sexaphone', 'sexaudio', 'sexavi', 'sexbondage',
'sexboner', 'sexbuttfucker', 'sexcam', 'sexcams', 'sexchat', 'sexchatrooms',
'sexcites', 'sexclub', 'sexclubs', 'sex.com', 'sexdating', 'sexe',
'sexfantasies', 'sexfantasy', 'sexfilm', 'sexgallery', 'sexgame', 'sexgames',
'sexgirl', 'sexgirls', 'sexgroup', 'sexguide', 'sexhardcore', 'sexhphoto',
'sexhungry', 'sexhungryjoe', 'sexhungryjoes', 'sexi', 'seximages', 'sexis',
'sexjapan', 'sex.jpg', 'sexlinda', 'sexlinks', 'sexlinx', 'sexlive',
'sexmagazine', 'sexmagazines', 'sexmature', 'sexmelayu', 'sexmodels', 'sexmovie',
'sexmovies', 'sexmpeg', 'sexnude', 'sexnudity', 'sexo', 'sexoanal',
'sexole', 'sexontheinternet', 'sexoral', 'sexpasswords', 'sexphoto', 'sexphotos',
'sexpic', 'sexpicnet', 'sexpics', 'sexpicture', 'sexpictures', 'sexpicturesfree',
'sexpix', 'sexplaza', 'sexporn', 'sexporno', 'sexpositions', 'sexpussy',
'sexroulette', 'sexs', 'sexsamples', 'sexsearch', 'sexsex', 'sexsexsex',
'sexshare', 'sexshops', 'sexshow', 'sexshows', 'sexsite', 'sexsites',
'sexslave', 'sexsound', 'sexsounds', 'sexsource', 'sexspaces', 'sexspaces.com',
'sexstories', 'sexstory', 'sexstorys', 'sextalk', 'sexteen', 'sexthumbnails',
'sex-toons', 'sextoons', 'sextour', 'sextoy', 'sextoys', 'sextpus',
'sextracker.com', 'sextropolis', 'sexualbondage', 'sexualfantasies', 'sexualfantasy', 'sexualintercourse',
'sexualpictures', 'sexualpleasure', 'sexualpositions', 'sexuncensored', 'sexvideo', 'sexvision',
'sexvote', 'sexwomen', 'sexworld', 'sexx', 'sexxx', 'sexxxx',
'sexxxxx', 'sexyamateurs', 'sexybabes', 'sexyblack', 'sexyboobs', 'sexybookmark',
'sexybookmarka', 'sexybookmarks', 'sexybutts', 'sexycunts', 'sexygirl', 'sexygirls',
'sexyladies', 'sexylady', 'sexylegs', 'sexylingerie', 'sexymen', 'sexyphotos',
'sexypics', 'sexypictures', 'sexypost', 'sexypussies', 'sexypussy', 'sexysites',
'sexystories', 'sexywoman', 'sexywomen', 'shavedcunt', 'shavedcunts', 'shavedpussies',
'shavedpussy', 'shavedpussypics', 'shavedsluts', 'shavedwomen', 'shavenpussies', 'shavenpussy',
'shavepussy', 'sheboy', 'she-male', 'shemale+video', 'shemaleyum', 'showcam',
'showcams', 'showercam', 'showercams', 'slavesex', 'smallboobs', 'smallbreasts',
'smalldicks', 'smallpussy', 'smalltits', 'smut', 'smut69', 'smutland',
'smutserver', 'smutshack', 'softcore', 'softporn', 'softpornography', 'spankingpage',
'spreadpussy', 'spycamadult', 'stockingsex', 'storieserotic', 'storiessex', 'straightsex',
'stretchedcunt', 'stripclub', 'stripclubs', 'stripshow', 'striptease', 'strokeit',
'strokeme', 'suckdick', 'sucksex', 'superchicken', 'supersex', 'supertits',
'swallowcum', 'swinger', 'swingerclub', 'swingers', 'swollenclits', 'syberporn',
'sylviasaint', 'teenanal', 'teenanalsex', 'teenhardcore', 'teenie', 'teenies',
'teennude', 'teenpics', 'teenporn', 'teenporno', 'teenpussy', 'teensex',
'teenslut', 'teensluts', 'teensuck', 'teenxxx', 'tgp', 'thaipussy',
'thaisex', 'thehun', 'threesomes', 'thumblords', 'thumbnailssex', 'thumbzilla',
'tiffanytowers', 'tightcunt', 'tightpussies', 'tightpussy', 'tinypussy', 'tinytits',
'tinytitties', 'tisandass', 'tit', 'titbondage', 'titfuck', 'titfucking',
'tities', 'titman', 'titsandass', 'titsass', 'titties', 'titts',
'titty', 'tittyfuck', 'tittyfucking', 'tittys', 'tokyotopless', 'tommysbookmark',
'toplesswomen', 'trannies', 'transexual', 'transsexuels', 'twat', 'twats',
'twink', 'ultradonkey', 'ultrahardcore', 'uncutcocks', 'vaginalintercourse', 'vaginapictures',
'videoporno', 'videosex', 'videoxxx', 'vintageporn', 'virtualsex', 'vividtv',
'wank', 'wankers', 'wanking', 'wendywhoppers', 'wetcunt', 'wetcunts',
'wetdick', 'wetpanties', 'wetpussies', 'wetpussy', 'wetsex', 'wetvagina',
'whitepussy', 'whitesex', 'whitetits', 'whitexxx', 'wifesharing', 'wifeswapping',
'wildpussy', 'wildsex', 'womensex', 'worldsex', 'worldsexguide', 'x-rated',
'xrated', 'x-ratedmovie', 'x-ratedmovies', 'x-ratedvideo', 'x-ratedvideos', 'xxx4free',
'xxxadult', 'xxxadultmovies', 'xxxadultvideo', 'xxxadultvideos', 'xxxbabes', 'xxxcam',
'xxxcelebrities', 'xxxfree', 'xxxfreepics', 'xxxgalleries', 'xxxhardcore', 'xxxjapan',
'xxxlinks', 'xxxmodels', 'xxxmovie', 'xxxmovies', 'xxxmpeg', 'xxxpassword',
'xxxphoto', 'xxxphotos', 'xxxpic', 'xxx-pics', 'xxxpics', 'xxxpicture',
'xxxpictures', 'xxxpicturesfree', 'xxxpicx', 'xxxpirtures', 'xxxporn', 'xxxporno',
'xxxpussy', 'xxx-rated', 'xxxrated', 'xxxsex', 'xxxsites', 'xxxsluts',
'xxxstories', 'xxxteen', 'xxxthumbnails', 'xxxthumbs', 'xxxtoons', 'xxxtoys',
'xxxvideo', 'xxxvideos', 'xxxwomen', 'xxxx', 'xxxxx', 'xxxxxx',
'filthyfarm', 'adultcheck', 'erotic', 'mistresses', 'naughty',
'adult', 'amateur', 'amateurs', 'anal', 'analau', 'poker',
'anally', 'asian', 'swinging', 'ass', 'orgy', 'sexvideos',
'babe', 'babes', 'bare', 'glamour', 'casino', 'bed',
'vibrator', 'bikini', 'bisexual', 'blonde', 'breasted', 'breasts',
'busty', 'butts', 'candy', 'caning', 'caps', 'catholic',
'chained', 'chested', 'chubby', 'close-ups', 'coupling',
'cucumber', 'doll', 'dolls', 'dolly', 'dominating', 'drinking',
'drunk', 'fat', 'feet', 'fingering', 'fishnet', 'flasher',
'footjob', 'foreplay', 'foreplaying', 'girlfriend', 'girlfriends', 'gymnast',
'hairy', 'holly', 'horse', 'housewife', 'houswife', 'insertion',
'insertions', 'interracial', 'jerka', 'juicy', 'kissing', 'kitty',
'lactating', 'ladyboy', 'lance', 'latina', 'latins', 'leather',
'legs', 'lesbian', 'lesbians', 'licking', 'lingerie', 'lubricants',
'mature', 'midget', 'minor', 'mistress', 'nude',
'older', 'olez', 'oral', 'panties', 'pantyhose', 'pedro',
'peeing', 'penetration', 'pigtailed', 'pleasure', 'plump',
'plumper', 'poser', 'posers', 'poses', 'posing', 'prosecuted',
'punishment', 'reproducing', 'sex', 'shaved', 'shock', 'slave',
'sleeping', 'speculums', 'spread', 'spreading', 'squirting', 'stripper',
'stripping', 'swapping', 'thong', 'topless', 'toying',
'trix', 'undressing', 'uniform', 'whipcream', 'brazzers', 'порно',
'порн', 'лесб', 'гей', 'геи', 'прон'
];
const XXX_VERY_BAD_WORDS = [
'catgoddess', 'gracel', 'fatman', 'falko', 'pthc',
'ptsc', 'yukikax', 'ls-models', '3yo', '4yo', '5yo', '6yo', '7yo', '8yo', '9yo',
'10yo', '11yo', '12yo', '13yo', '14yo', '15yo', '16yo'
'catgoddess', 'gracel', 'fatman', 'falko', 'pthc',
'ptsc', 'yukikax', 'ls-models', '3yo', '4yo', '5yo', '6yo', '7yo', '8yo', '9yo',
'10yo', '11yo', '12yo', '13yo', '14yo', '15yo', '16yo'
]
module.exports = {
XXX_BLOCK_WORDS,
XXX_VERY_BAD_WORDS
XXX_BLOCK_WORDS,
XXX_VERY_BAD_WORDS
};

View File

@ -4,23 +4,23 @@ import ReactMarkdown from 'react-markdown'
import fs from 'fs'
export default class ChangeLog extends Page {
constructor(props) {
super(props)
this.setTitle('Changelog');
constructor(props) {
super(props)
this.setTitle('Changelog');
let changelogPath = 'CHANGELOG.md'
if(!fs.existsSync(changelogPath))
changelogPath = 'resources/CHANGELOG.md'
let changelogPath = 'CHANGELOG.md'
if(!fs.existsSync(changelogPath))
changelogPath = 'resources/CHANGELOG.md'
this.changelog = fs.readFileSync(changelogPath)
if(!this.changelog)
throw new Error('no changelog file')
}
render() {
return (
<div className='pad0-75'>
<ReactMarkdown skipHtml={true} source={this.changelog} />
</div>
);
}
this.changelog = fs.readFileSync(changelogPath)
if(!this.changelog)
throw new Error('no changelog file')
}
render() {
return (
<div className='pad0-75'>
<ReactMarkdown skipHtml={true} source={this.changelog} />
</div>
);
}
}

View File

@ -6,13 +6,13 @@ export default class BTComponent extends Component {
// Свайп действия
if(
this.props.onSwipeLeft ||
this.props.onSwipeRight ||
this.props.onSwipeTop ||
this.props.onSwipeBottom ||
this.onSwipeLeft ||
this.onSwipeRight ||
this.onSwipeTop ||
this.onSwipeBottom
this.props.onSwipeRight ||
this.props.onSwipeTop ||
this.props.onSwipeBottom ||
this.onSwipeLeft ||
this.onSwipeRight ||
this.onSwipeTop ||
this.onSwipeBottom
)
{
this.swipeFunctions = listenSwipe(this, {

View File

@ -9,302 +9,302 @@ import Slider from 'material-ui/Slider'
import fs from 'fs'
let dialog
if(typeof WEB === 'undefined')
dialog = require('electron').remote.dialog
dialog = require('electron').remote.dialog
export default class ConfigPage extends Page {
constructor(props) {
super(props)
this.setTitle('Rats settings');
this.options = {}
}
componentDidMount() {
this.loadSettings()
}
loadSettings() {
window.torrentSocket.emit('config', window.customLoader((options) => {
this.options = options;
console.log(this.options)
this.forceUpdate();
}));
}
saveSettings() {
window.torrentSocket.emit('setConfig', this.options)
this.settingsSavedMessage = true
this.forceUpdate()
setTimeout(() => {
this.settingsSavedMessage = false
this.forceUpdate()
}, 1000)
}
render() {
return (
<div>
<div className='row center pad0-75'>
<RaisedButton label={__('Back to main page')} primary={true} onClick={() => {
window.router('/')
}} />
</div>
<div className='column center w100p pad0-75'>
<Toggle
style={{marginTop: '10px'}}
label={__('Enabled network scanning')}
toggled={this.options.indexer}
onToggle={(e, checked) => {
this.options.indexer = checked
if(!this.options.indexer)
this.options.p2p = false
this.forceUpdate()
}}
/>
<div className='column w100p'>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Scanning port')}</div>
<TextField
style={{width: 65}}
hintText={__('Port')}
errorText={this.options.spiderPort > 0 ? undefined : __('This field is required')}
value={this.options.spiderPort}
onChange={(e, value) => {
if(!value)
value = 0
if(value > 65535)
value = 65535
constructor(props) {
super(props)
this.setTitle('Rats settings');
this.options = {}
}
componentDidMount() {
this.loadSettings()
}
loadSettings() {
window.torrentSocket.emit('config', window.customLoader((options) => {
this.options = options;
console.log(this.options)
this.forceUpdate();
}));
}
saveSettings() {
window.torrentSocket.emit('setConfig', this.options)
this.settingsSavedMessage = true
this.forceUpdate()
setTimeout(() => {
this.settingsSavedMessage = false
this.forceUpdate()
}, 1000)
}
render() {
return (
<div>
<div className='row center pad0-75'>
<RaisedButton label={__('Back to main page')} primary={true} onClick={() => {
window.router('/')
}} />
</div>
<div className='column center w100p pad0-75'>
<Toggle
style={{marginTop: '10px'}}
label={__('Enabled network scanning')}
toggled={this.options.indexer}
onToggle={(e, checked) => {
this.options.indexer = checked
if(!this.options.indexer)
this.options.p2p = false
this.forceUpdate()
}}
/>
<div className='column w100p'>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Scanning port')}</div>
<TextField
style={{width: 65}}
hintText={__('Port')}
errorText={this.options.spiderPort > 0 ? undefined : __('This field is required')}
value={this.options.spiderPort}
onChange={(e, value) => {
if(!value)
value = 0
if(value > 65535)
value = 65535
this.options.spiderPort = parseInt(value)
this.forceUpdate()
}}
/>
</div>
<div className='fs0-75' style={{color: 'grey'}}>* {__('For current work TCP and UDP ports must be fully open and forward in case of router usage')}</div>
</div>
this.options.spiderPort = parseInt(value)
this.forceUpdate()
}}
/>
</div>
<div className='fs0-75' style={{color: 'grey'}}>* {__('For current work TCP and UDP ports must be fully open and forward in case of router usage')}</div>
</div>
<div className='column w100p'>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Trackers responce port')}</div>
<TextField
style={{width: 65}}
hintText="Port"
errorText={this.options.udpTrackersPort > 0 ? undefined : __('This field is required')}
value={this.options.udpTrackersPort}
onChange={(e, value) => {
if(!value)
value = 0
if(value > 65535)
value = 65535
<div className='column w100p'>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Trackers responce port')}</div>
<TextField
style={{width: 65}}
hintText="Port"
errorText={this.options.udpTrackersPort > 0 ? undefined : __('This field is required')}
value={this.options.udpTrackersPort}
onChange={(e, value) => {
if(!value)
value = 0
if(value > 65535)
value = 65535
this.options.udpTrackersPort = parseInt(value)
this.forceUpdate()
}}
/>
</div>
<div className='fs0-75' style={{color: 'grey'}}>* {__('For current work UDP port must be fully open and forward in case of router usage')}</div>
</div>
this.options.udpTrackersPort = parseInt(value)
this.forceUpdate()
}}
/>
</div>
<div className='fs0-75' style={{color: 'grey'}}>* {__('For current work UDP port must be fully open and forward in case of router usage')}</div>
</div>
<Toggle
style={{marginTop: '10px'}}
label={__('Enabled UPnP')}
toggled={this.options.upnp}
onToggle={(e, checked) => {
this.options.upnp = checked
this.forceUpdate()
}}
/>
<Toggle
style={{marginTop: '10px'}}
label={__('Enabled UPnP')}
toggled={this.options.upnp}
onToggle={(e, checked) => {
this.options.upnp = checked
this.forceUpdate()
}}
/>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Collection directory')}</div>
<TextField
hintText={__('Db path')}
errorText={this.options.dbPath && this.options.dbPath.length > 0 ? undefined : __('This field is required')}
value={this.options.dbPath}
onChange={(e, value) => {
if(!fs.existsSync(value))
return
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Collection directory')}</div>
<TextField
hintText={__('Db path')}
errorText={this.options.dbPath && this.options.dbPath.length > 0 ? undefined : __('This field is required')}
value={this.options.dbPath}
onChange={(e, value) => {
if(!fs.existsSync(value))
return
this.options.dbPath = value
this.forceUpdate()
}}
/>
<RaisedButton style={{marginLeft: 20}} label={__('Browse')} primary={true} onClick={() => {
if(!dialog)
return
const dir = dialog.showOpenDialog({properties: ['openDirectory']})[0]
if(dir)
{
this.options.dbPath = dir
this.forceUpdate()
}
}} />
</div>
this.options.dbPath = value
this.forceUpdate()
}}
/>
<RaisedButton style={{marginLeft: 20}} label={__('Browse')} primary={true} onClick={() => {
if(!dialog)
return
const dir = dialog.showOpenDialog({properties: ['openDirectory']})[0]
if(dir)
{
this.options.dbPath = dir
this.forceUpdate()
}
}} />
</div>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Download torrents directory')}</div>
<TextField
hintText={__('Download path')}
value={this.options.client && this.options.client.downloadPath}
onChange={(e, value) => {
if(!fs.existsSync(value))
return
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Download torrents directory')}</div>
<TextField
hintText={__('Download path')}
value={this.options.client && this.options.client.downloadPath}
onChange={(e, value) => {
if(!fs.existsSync(value))
return
this.options.client.downloadPath = value
this.forceUpdate()
}}
/>
<RaisedButton style={{marginLeft: 20}} label={__('Browse')} primary={true} onClick={() => {
if(!dialog)
return
const dir = dialog.showOpenDialog({properties: ['openDirectory']})[0]
if(dir)
{
this.options.client.downloadPath = dir
this.forceUpdate()
}
}} />
</div>
this.options.client.downloadPath = value
this.forceUpdate()
}}
/>
<RaisedButton style={{marginLeft: 20}} label={__('Browse')} primary={true} onClick={() => {
if(!dialog)
return
const dir = dialog.showOpenDialog({properties: ['openDirectory']})[0]
if(dir)
{
this.options.client.downloadPath = dir
this.forceUpdate()
}
}} />
</div>
<Toggle
style={{marginTop: '10px'}}
label={__('Hide to tray on application minimize')}
toggled={this.options.trayOnMinimize}
onToggle={(e, checked) => {
this.options.trayOnMinimize = checked
this.forceUpdate()
}}
/>
<Toggle
style={{marginTop: '10px'}}
label={__('Hide to tray on application minimize')}
toggled={this.options.trayOnMinimize}
onToggle={(e, checked) => {
this.options.trayOnMinimize = checked
this.forceUpdate()
}}
/>
<Toggle
style={{marginTop: '10px'}}
label={__('Hide to tray on application close')}
toggled={this.options.trayOnClose}
onToggle={(e, checked) => {
this.options.trayOnClose = checked
this.forceUpdate()
}}
/>
<Toggle
style={{marginTop: '10px'}}
label={__('Hide to tray on application close')}
toggled={this.options.trayOnClose}
onToggle={(e, checked) => {
this.options.trayOnClose = checked
this.forceUpdate()
}}
/>
<div style={{marginTop: 10}}>{__('P2P Rats network settings')}:</div>
<Toggle
style={{marginTop: '10px'}}
label={__('Enabled p2p search')}
toggled={this.options.p2p}
onToggle={(e, checked) => {
this.options.p2p = this.options.indexer && checked
this.forceUpdate()
}}
/>
<div className='column w100p'>
<Toggle
style={{marginTop: '10px'}}
label={__('Enabled bootstrap peers')}
toggled={this.options.p2pBootstrap}
onToggle={(e, checked) => {
this.options.p2pBootstrap = checked
this.forceUpdate()
}}
/>
<div className='fs0-75' style={{color: 'grey'}}>* {__('Use extrnral bootstrap nodes to get p2p peers when network setted wrong or need external source')}</div>
</div>
<div className='column w100p'>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Max peers limit')} ({__('current')}: {this.options.p2pConnections})</div>
<Slider
min={10}
max={25}
step={1}
style={{width: 300}}
value={this.options.p2pConnections}
onChange={(event, value) => {
this.options.p2pConnections = value
this.forceUpdate()
}}
/>
</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}}>{__('P2P Rats network settings')}:</div>
<Toggle
style={{marginTop: '10px'}}
label={__('Enabled p2p search')}
toggled={this.options.p2p}
onToggle={(e, checked) => {
this.options.p2p = this.options.indexer && checked
this.forceUpdate()
}}
/>
<div className='column w100p'>
<Toggle
style={{marginTop: '10px'}}
label={__('Enabled bootstrap peers')}
toggled={this.options.p2pBootstrap}
onToggle={(e, checked) => {
this.options.p2pBootstrap = checked
this.forceUpdate()
}}
/>
<div className='fs0-75' style={{color: 'grey'}}>* {__('Use extrnral bootstrap nodes to get p2p peers when network setted wrong or need external source')}</div>
</div>
<div className='column w100p'>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Max peers limit')} ({__('current')}: {this.options.p2pConnections})</div>
<Slider
min={10}
max={25}
step={1}
style={{width: 300}}
value={this.options.p2pConnections}
onChange={(event, value) => {
this.options.p2pConnections = value
this.forceUpdate()
}}
/>
</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 className='column w100p'>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Scanner walk speed')} ({__('current')}: {this.options.spider && this.options.spider.walkInterval}) [{__('affected after program reload')}]</div>
<Slider
min={1}
max={150}
step={1}
style={{width: 300}}
value={this.options.spider && this.options.spider.walkInterval}
onChange={(event, value) => {
this.options.spider.walkInterval = value
this.forceUpdate()
}}
/>
</div>
<div className='fs0-75' style={{color: 'grey'}}>* {__('Low value')} - {__('fast initial scanning and high cpu usage')}. {__('High Value')} - {__('low cpu usage but very slow scanning')}.
{__('Good value between')} 3-60. {__('Defaul value')}: 5</div>
</div>
<div className='column w100p'>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Nodes usage')} ({__('current')}: {this.options.spider && this.options.spider.nodesUsage})</div>
<Slider
min={0}
max={1000}
step={1}
style={{width: 300}}
value={this.options.spider && this.options.spider.nodesUsage}
onChange={(event, value) => {
this.options.spider.nodesUsage = value
this.forceUpdate()
}}
/>
</div>
<div className='fs0-75' style={{color: 'grey'}}>* {__('Low Value')} - {__('very low usage of nodes, low network traffic, slow torrent scanning')}. {__('High value')} - {__('high traffic, fast scanning, high routers usage')}.
{__('Recomended value between')} 10-1000. {__('Defaul value')}: 100. 0 - {__('Ignore this option')} ({__('no limit')}).
</div>
</div>
<div className='column w100p'>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Reduce network packages')} ({__('current')}: {this.options.spider && this.options.spider.packagesLimit})</div>
<Slider
min={0}
max={2000}
step={1}
style={{width: 300}}
value={this.options.spider && this.options.spider.packagesLimit}
onChange={(event, value) => {
this.options.spider.packagesLimit = value
this.forceUpdate()
}}
/>
</div>
<div className='fs0-75' style={{color: 'grey'}}>* {__('Low Value')} - {__('ignore more usless network packages, lower traffic and routers usage')}. {__('High Value')} - {__('high traffic and router usage in prospect')}.
{__('Recomended value between')} 300-2000. {__('Defaul value')}: 500. 0 - {__('Ignore this option')} ({__('no limit')}).
</div>
</div>
<div style={{marginTop: 10}}>{__('Torrent network scanner settings')}:</div>
<div className='column w100p'>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Scanner walk speed')} ({__('current')}: {this.options.spider && this.options.spider.walkInterval}) [{__('affected after program reload')}]</div>
<Slider
min={1}
max={150}
step={1}
style={{width: 300}}
value={this.options.spider && this.options.spider.walkInterval}
onChange={(event, value) => {
this.options.spider.walkInterval = value
this.forceUpdate()
}}
/>
</div>
<div className='fs0-75' style={{color: 'grey'}}>* {__('Low value')} - {__('fast initial scanning and high cpu usage')}. {__('High Value')} - {__('low cpu usage but very slow scanning')}.
{__('Good value between')} 3-60. {__('Defaul value')}: 5</div>
</div>
<div className='column w100p'>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Nodes usage')} ({__('current')}: {this.options.spider && this.options.spider.nodesUsage})</div>
<Slider
min={0}
max={1000}
step={1}
style={{width: 300}}
value={this.options.spider && this.options.spider.nodesUsage}
onChange={(event, value) => {
this.options.spider.nodesUsage = value
this.forceUpdate()
}}
/>
</div>
<div className='fs0-75' style={{color: 'grey'}}>* {__('Low Value')} - {__('very low usage of nodes, low network traffic, slow torrent scanning')}. {__('High value')} - {__('high traffic, fast scanning, high routers usage')}.
{__('Recomended value between')} 10-1000. {__('Defaul value')}: 100. 0 - {__('Ignore this option')} ({__('no limit')}).
</div>
</div>
<div className='column w100p'>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Reduce network packages')} ({__('current')}: {this.options.spider && this.options.spider.packagesLimit})</div>
<Slider
min={0}
max={2000}
step={1}
style={{width: 300}}
value={this.options.spider && this.options.spider.packagesLimit}
onChange={(event, value) => {
this.options.spider.packagesLimit = value
this.forceUpdate()
}}
/>
</div>
<div className='fs0-75' style={{color: 'grey'}}>* {__('Low Value')} - {__('ignore more usless network packages, lower traffic and routers usage')}. {__('High Value')} - {__('high traffic and router usage in prospect')}.
{__('Recomended value between')} 300-2000. {__('Defaul value')}: 500. 0 - {__('Ignore this option')} ({__('no limit')}).
</div>
</div>
{
this.settingsSavedMessage
{
this.settingsSavedMessage
&&
<div style={{color: 'green'}}>{__('Settings saved')}</div>
}
}
<div className='row center pad0-75'>
<RaisedButton label={__('Save Settings')} primary={true} onClick={() => {
this.saveSettings()
}} />
</div>
<div className='row center pad0-75'>
<RaisedButton label={__('Save Settings')} primary={true} onClick={() => {
this.saveSettings()
}} />
</div>
</div>
</div>
);
}
</div>
</div>
);
}
}

View File

@ -236,7 +236,7 @@ const ContentCategoryProp = 'contentCategory';
const {
XXX_BLOCK_WORDS,
XXX_VERY_BAD_WORDS
XXX_VERY_BAD_WORDS
} = require('./bad-words');
// блокируем порнографию
@ -270,7 +270,7 @@ const detectSubCategory = (torrent, files, typesPriority, contentType) => {
fileCheck = fileCheck.join('.');
blockBadName(torrent, fileCheck);
return torrent[ContentTypeProp] == 'bad';
})
}
@ -304,7 +304,7 @@ const torrentTypeDetect = (torrent, files) => {
}
}
let priority = Object.keys(typesPriority).sort(function(a, b){
return typesPriority[b] - typesPriority[a]
return typesPriority[b] - typesPriority[a]
});
if(priority.length > 0)
torrent[ContentTypeProp] = priority[0];

View File

@ -7,27 +7,27 @@ import NavigationClose from 'material-ui/svg-icons/navigation/close';
export default class DMCAPage extends Page {
render() {
return (
<div className='w100p column'>
<AppBar
title="DMCA / Copyright"
iconElementLeft={<IconButton onClick={()=>{ window.router('/') }}><NavigationClose /></IconButton>}
/>
render() {
return (
<div className='w100p column'>
<AppBar
title="DMCA / Copyright"
iconElementLeft={<IconButton onClick={()=>{ window.router('/') }}><NavigationClose /></IconButton>}
/>
<div className='column w100p pad1 center'>
<div>RatsOnTheBoat.org is in compliance with 17 U.S.C. § 512, the Digital Millennium Copyright Act ("DMCA") and the Directive 2001/29/EC of the European Parliament.</div>
<div className='fs1-5 pad0-75'>Content status</div>
<div className='column w100p pad1 center'>
<div>RatsOnTheBoat.org is in compliance with 17 U.S.C. § 512, the Digital Millennium Copyright Act ("DMCA") and the Directive 2001/29/EC of the European Parliament.</div>
<div className='fs1-5 pad0-75'>Content status</div>
Our main goal is collect and make analysis of information from the torrent network. We don't save/distribute any real content/data and also don't save any torrents files - we are respect DMCA law.
Information collected automaticly and the real source are torrent clients based on torrent protocol.
Our main goal is collect and make analysis of information from the torrent network. We don't save/distribute any real content/data and also don't save any torrents files - we are respect DMCA law.
Information collected automaticly and the real source are torrent clients based on torrent protocol.
<div className='fs1-5 pad0-75'>Block mechanisms (for rightholders)</div>
<div className='fs1-5 pad0-75'>Block mechanisms (for rightholders)</div>
Right holders can block/remove content that they responsible for. Contact administration or left application about content removal.
</div>
</div>
);
}
Right holders can block/remove content that they responsible for. Contact administration or left application about content removal.
</div>
</div>
);
}
}

View File

@ -10,48 +10,48 @@ export default class TopPage extends Page {
downloads = []
constructor(props) {
super(props)
this.setTitle('Current Downloads');
super(props)
this.setTitle('Current Downloads');
}
getDownloads()
{
window.torrentSocket.emit('downloads', window.customLoader((downloads) => {
this.downloads = downloads
this.forceUpdate()
}))
window.torrentSocket.emit('downloads', window.customLoader((downloads) => {
this.downloads = downloads
this.forceUpdate()
}))
}
componentDidMount()
{
super.componentDidMount();
this.getDownloads()
this.downloading = () => this.getDownloads()
window.torrentSocket.on('downloading', this.downloading);
this.downloadDone = () => this.getDownloads()
window.torrentSocket.on('downloadDone', this.downloadDone);
super.componentDidMount();
this.getDownloads()
this.downloading = () => this.getDownloads()
window.torrentSocket.on('downloading', this.downloading);
this.downloadDone = () => this.getDownloads()
window.torrentSocket.on('downloadDone', this.downloadDone);
}
componentWillUnmount()
{
if(this.downloading)
window.torrentSocket.off('downloading', this.downloading);
if(this.downloadDone)
window.torrentSocket.off('downloadDone', this.downloadDone);
if(this.downloading)
window.torrentSocket.off('downloading', this.downloading);
if(this.downloadDone)
window.torrentSocket.off('downloadDone', this.downloadDone);
}
render() {
return (
<div>
<div className='column center w100p pad0-75'>
<RaisedButton label={__('Back to main page')} primary={true} onClick={() => {
window.router('/')
}} />
<List style={{paddingBottom: '70px'}} className='animated recent-torrents'>
{
this.downloads.map((download, index) => {
return <TorrentLine key={index} torrent={download.torrentObject} download={download} />
})
}
</List>
</div>
</div>
);
return (
<div>
<div className='column center w100p pad0-75'>
<RaisedButton label={__('Back to main page')} primary={true} onClick={() => {
window.router('/')
}} />
<List style={{paddingBottom: '70px'}} className='animated recent-torrents'>
{
this.downloads.map((download, index) => {
return <TorrentLine key={index} torrent={download.torrentObject} download={download} />
})
}
</List>
</div>
</div>
);
}
}

View File

@ -8,89 +8,89 @@ import MenuItem from 'material-ui/MenuItem';
import Feed from './feed';
export default class FeedPage extends Page {
constructor(props) {
super(props)
this.setTitle('Rats On The Boat - Content Search Engine');
}
componentDidMount()
{
Search.instance().onSearchUpdate = () => this.forceUpdate()
}
componentWillUnmount()
{
Search.instance().onSearchUpdate = () => {}
}
render() {
const orderText = (text, field) => {
if(field !== Search.instance().state.orderBy)
return text;
constructor(props) {
super(props)
this.setTitle('Rats On The Boat - Content Search Engine');
}
componentDidMount()
{
Search.instance().onSearchUpdate = () => this.forceUpdate()
}
componentWillUnmount()
{
Search.instance().onSearchUpdate = () => {}
}
render() {
const orderText = (text, field) => {
if(field !== Search.instance().state.orderBy)
return text;
if(Search.instance().state.orderDesc)
return text + ' ⇩'
else
return text + ' ⇧'
}
if(Search.instance().state.orderDesc)
return text + ' ⇩'
else
return text + ' ⇧'
}
return (
<div id='index-window' className='column center'>
<div className='torrents-container'>
<SearchResults
torrentsSearchResults={Search.instance().searchTorrents}
filesSearchResults={Search.instance().searchFiles}
currentSearching={Search.instance().state.searchingIndicator}
searchText={Search.instance().currentSearch}
resultSelector={
<SelectField
floatingLabelText={__("Sort by")}
floatingLabelFixed={true}
value={Search.instance().state.orderBy}
onChange={(event, index, value) => {
event.preventDefault(); // fix overclick on torrent
if(value === 'none') {
Search.instance().setState({orderBy: null}, () => {
Search.instance().search(true)
})
return;
}
return (
<div id='index-window' className='column center'>
<div className='torrents-container'>
<SearchResults
torrentsSearchResults={Search.instance().searchTorrents}
filesSearchResults={Search.instance().searchFiles}
currentSearching={Search.instance().state.searchingIndicator}
searchText={Search.instance().currentSearch}
resultSelector={
<SelectField
floatingLabelText={__("Sort by")}
floatingLabelFixed={true}
value={Search.instance().state.orderBy}
onChange={(event, index, value) => {
event.preventDefault(); // fix overclick on torrent
if(value === 'none') {
Search.instance().setState({orderBy: null}, () => {
Search.instance().search(true)
})
return;
}
if(value === Search.instance().state.orderBy)
{
Search.instance().setState({orderDesc: !Search.instance().state.orderDesc}, () => {
Search.instance().search(true)
})
return;
}
if(value === Search.instance().state.orderBy)
{
Search.instance().setState({orderDesc: !Search.instance().state.orderDesc}, () => {
Search.instance().search(true)
})
return;
}
Search.instance().setState({
orderBy: value,
orderDesc: (value === 'seeders' || value === 'completed' || value === 'added') ? true : Search.instance().state.orderDesc
}, () => {
Search.instance().search(true)
})
}}
>
<MenuItem value='none' primaryText={__('None')} />
<MenuItem value='seeders' primaryText={orderText(__('Seeders'), 'seeders')} />
<MenuItem value='name' primaryText={orderText(__('Name'), 'name')} />
<MenuItem value='files' primaryText={orderText(__('Files'), 'files')} />
<MenuItem value='size' primaryText={orderText(__('Size'), 'size')} />
<MenuItem value='added' primaryText={orderText(__('Added date'), 'added')} />
<MenuItem value='completed' primaryText={orderText(__('Completed'), 'completed')} />
</SelectField>
}
Search.instance().setState({
orderBy: value,
orderDesc: (value === 'seeders' || value === 'completed' || value === 'added') ? true : Search.instance().state.orderDesc
}, () => {
Search.instance().search(true)
})
}}
>
<MenuItem value='none' primaryText={__('None')} />
<MenuItem value='seeders' primaryText={orderText(__('Seeders'), 'seeders')} />
<MenuItem value='name' primaryText={orderText(__('Name'), 'name')} />
<MenuItem value='files' primaryText={orderText(__('Files'), 'files')} />
<MenuItem value='size' primaryText={orderText(__('Size'), 'size')} />
<MenuItem value='added' primaryText={orderText(__('Added date'), 'added')} />
<MenuItem value='completed' primaryText={orderText(__('Completed'), 'completed')} />
</SelectField>
}
moreTorrentsEnabled={Search.instance().moreSearchTorrents && !Search.instance().state.searchingIndicator}
moreFilesEnabled={Search.instance().moreSearchFiles && !Search.instance().state.searchingIndicator}
onMoreTorrents={() => Search.instance().moreTorrents()}
onMoreFiles={() => Search.instance().moreFiles()}
moreTorrentsIndicator={Search.instance().state.moreTorrentsIndicator}
moreFilesIndicator={Search.instance().state.moreFilesIndicator}
/>
</div>
<div className='column center w100p pad0-75'>
<Feed />
</div>
</div>
);
}
moreTorrentsEnabled={Search.instance().moreSearchTorrents && !Search.instance().state.searchingIndicator}
moreFilesEnabled={Search.instance().moreSearchFiles && !Search.instance().state.searchingIndicator}
onMoreTorrents={() => Search.instance().moreTorrents()}
onMoreFiles={() => Search.instance().moreFiles()}
moreTorrentsIndicator={Search.instance().state.moreTorrentsIndicator}
moreFilesIndicator={Search.instance().state.moreFilesIndicator}
/>
</div>
<div className='column center w100p pad0-75'>
<Feed />
</div>
</div>
);
}
}

View File

@ -5,32 +5,32 @@ import Divider from 'material-ui/Divider';
import Subheader from 'material-ui/Subheader';
export default class RecentTorrents extends Component {
constructor() {
super()
this.torrents = [];
}
componentDidMount() {
window.torrentSocket.emit('feed', window.customLoader((data) => {
if(data) {
this.torrents = data;
console.log(data)
this.forceUpdate();
}
}))
}
render() {
return (
<List className='animated torrents-container'>
<Subheader className='recent-title' inset={true}>
{__('Feed')}
</Subheader>
<Divider />
{
this.torrents.map((torrent, index) =>{
return <TorrentLine key={index} torrent={torrent} />;
})
}
</List>
);
}
constructor() {
super()
this.torrents = [];
}
componentDidMount() {
window.torrentSocket.emit('feed', window.customLoader((data) => {
if(data) {
this.torrents = data;
console.log(data)
this.forceUpdate();
}
}))
}
render() {
return (
<List className='animated torrents-container'>
<Subheader className='recent-title' inset={true}>
{__('Feed')}
</Subheader>
<Divider />
{
this.torrents.map((torrent, index) =>{
return <TorrentLine key={index} torrent={torrent} />;
})
}
</List>
);
}
}

View File

@ -11,182 +11,182 @@ import MenuItem from 'material-ui/MenuItem';
import fs from 'fs'
export default class ConfigPage extends Page {
constructor(props) {
super(props)
this.setTitle('Rats filters');
this.options = {}
}
componentDidMount() {
this.loadSettings()
}
loadSettings() {
window.torrentSocket.emit('config', window.customLoader((options) => {
this.options = options;
console.log(this.options)
this.forceUpdate();
}));
}
saveSettings() {
window.torrentSocket.emit('setConfig', this.options)
this.settingsSavedMessage = true
this.forceUpdate()
setTimeout(() => {
this.settingsSavedMessage = false
this.forceUpdate()
}, 1000)
}
render() {
return (
<div>
<div className='row center pad0-75'>
<RaisedButton label={__("Back to main page")} primary={true} onClick={() => {
window.router('/')
}} />
</div>
constructor(props) {
super(props)
this.setTitle('Rats filters');
this.options = {}
}
componentDidMount() {
this.loadSettings()
}
loadSettings() {
window.torrentSocket.emit('config', window.customLoader((options) => {
this.options = options;
console.log(this.options)
this.forceUpdate();
}));
}
saveSettings() {
window.torrentSocket.emit('setConfig', this.options)
this.settingsSavedMessage = true
this.forceUpdate()
setTimeout(() => {
this.settingsSavedMessage = false
this.forceUpdate()
}, 1000)
}
render() {
return (
<div>
<div className='row center pad0-75'>
<RaisedButton label={__("Back to main page")} primary={true} onClick={() => {
window.router('/')
}} />
</div>
<div className='column center w100p pad0-75'>
<div className='column center w100p pad0-75'>
<div className='column w100p'>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Max files per torrent')} ({__('current')}: {this.options.filters && this.options.filters.maxFiles})</div>
<Slider
min={0}
max={50000}
step={1}
style={{width: 300}}
value={this.options.spider && this.options.filters.maxFiles}
onChange={(event, value) => {
this.options.filters.maxFiles = value
this.forceUpdate()
}}
/>
<TextField
hintText={__('Max files')}
className='pad0-75'
style={{width: 200}}
value={this.options.filters && this.options.filters.maxFiles}
onChange={(e, value) => {
if(!this.options.filters)
return
<div className='column w100p'>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Max files per torrent')} ({__('current')}: {this.options.filters && this.options.filters.maxFiles})</div>
<Slider
min={0}
max={50000}
step={1}
style={{width: 300}}
value={this.options.spider && this.options.filters.maxFiles}
onChange={(event, value) => {
this.options.filters.maxFiles = value
this.forceUpdate()
}}
/>
<TextField
hintText={__('Max files')}
className='pad0-75'
style={{width: 200}}
value={this.options.filters && this.options.filters.maxFiles}
onChange={(e, value) => {
if(!this.options.filters)
return
this.options.filters.maxFiles = parseInt(value)
this.forceUpdate()
}}
/>
</div>
<div className='fs0-75' style={{color: 'grey'}}>* 0 - {__('Disabled')}.
</div>
</div>
this.options.filters.maxFiles = parseInt(value)
this.forceUpdate()
}}
/>
</div>
<div className='fs0-75' style={{color: 'grey'}}>* 0 - {__('Disabled')}.
</div>
</div>
<div className='column w100p'>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Torrent name regular extension filtering')}</div>
<TextField
hintText="regex"
className='pad0-75'
style={{width: 400}}
value={this.options.filters && this.options.filters.namingRegExp}
onChange={(e, value) => {
if(!this.options.filters)
return
<div className='column w100p'>
<div className='row inline w100p'>
<div style={{flex: 1}}>{__('Torrent name regular extension filtering')}</div>
<TextField
hintText="regex"
className='pad0-75'
style={{width: 400}}
value={this.options.filters && this.options.filters.namingRegExp}
onChange={(e, value) => {
if(!this.options.filters)
return
this.options.filters.namingRegExp = value
this.forceUpdate()
}}
/>
<SelectField
style={{marginLeft: 15}}
floatingLabelText={__('Examples')}
value={this.options.filters && this.options.filters.namingRegExp}
onChange={(event, index, value) => {
if(!this.options.filters)
return
this.options.filters.namingRegExp = value
this.forceUpdate()
}}
/>
<SelectField
style={{marginLeft: 15}}
floatingLabelText={__('Examples')}
value={this.options.filters && this.options.filters.namingRegExp}
onChange={(event, index, value) => {
if(!this.options.filters)
return
this.options.filters.namingRegExp = value
this.forceUpdate()
}}
>
<MenuItem value={String.raw`^[А-Яа-я0-9A-Za-z.!@?#"$%&:;() *\+,\/;\-=[\\\]\^_{|}<>\u0400-\u04FF]+$`} primaryText={__('Russian + English only (With symbols)')} />
<MenuItem value={'^[0-9A-Za-z.!@?#"$%&:;() *\+,\/;\-=[\\\]\^_{|}<>]+$'} primaryText={__('English only (With symbols)')} />
<MenuItem value={'^((?!badword).)*$'} primaryText={__('Ignore badword')} />
</SelectField>
</div>
<Toggle
style={{marginTop: '10px'}}
label={__('Negative regular extension filtering')}
toggled={this.options.filters && this.options.filters.namingRegExpNegative}
onToggle={(e, checked) => {
if(!this.options.filters)
return
this.options.filters.namingRegExp = value
this.forceUpdate()
}}
>
<MenuItem value={String.raw`^[А-Яа-я0-9A-Za-z.!@?#"$%&:;() *\+,\/;\-=[\\\]\^_{|}<>\u0400-\u04FF]+$`} primaryText={__('Russian + English only (With symbols)')} />
<MenuItem value={'^[0-9A-Za-z.!@?#"$%&:;() *\+,\/;\-=[\\\]\^_{|}<>]+$'} primaryText={__('English only (With symbols)')} />
<MenuItem value={'^((?!badword).)*$'} primaryText={__('Ignore badword')} />
</SelectField>
</div>
<Toggle
style={{marginTop: '10px'}}
label={__('Negative regular extension filtering')}
toggled={this.options.filters && this.options.filters.namingRegExpNegative}
onToggle={(e, checked) => {
if(!this.options.filters)
return
this.options.filters.namingRegExpNegative = checked
this.forceUpdate()
}}
/>
<div className='fs0-75' style={{color: 'grey'}}>
this.options.filters.namingRegExpNegative = checked
this.forceUpdate()
}}
/>
<div className='fs0-75' style={{color: 'grey'}}>
* - {__('clean string means disabled')}
</div>
</div>
</div>
</div>
<Toggle
style={{marginTop: '10px'}}
label={__('Adult filter')}
toggled={this.options.filters && this.options.filters.adultFilter}
onToggle={(e, checked) => {
if(!this.options.filters)
return
<Toggle
style={{marginTop: '10px'}}
label={__('Adult filter')}
toggled={this.options.filters && this.options.filters.adultFilter}
onToggle={(e, checked) => {
if(!this.options.filters)
return
this.options.filters.adultFilter = checked
this.forceUpdate()
}}
/>
this.options.filters.adultFilter = checked
this.forceUpdate()
}}
/>
{
this.toRemoveProbably && this.toRemoveProbably > 0
?
<div style={{color: 'orange'}}>{__('Torrents to clean')}: {this.toRemoveProbably}</div>
:
null
}
{
this.toRemove && this.toRemove > 0
?
<div style={{color: 'red'}}>{__('Torrents cleaned')}: {this.toRemove}</div>
:
null
}
{
this.toRemoveProbably && this.toRemoveProbably > 0
?
<div style={{color: 'orange'}}>{__('Torrents to clean')}: {this.toRemoveProbably}</div>
:
null
}
{
this.toRemove && this.toRemove > 0
?
<div style={{color: 'red'}}>{__('Torrents cleaned')}: {this.toRemove}</div>
:
null
}
{
this.settingsSavedMessage
{
this.settingsSavedMessage
&&
<div style={{color: 'green'}}>{__('Settings saved')}</div>
}
}
<div className='row center pad0-75'>
<RaisedButton label={__('Check torrents')} primary={true} onClick={() => {
window.torrentSocket.emit('removeTorrents', true, window.customLoader((toRemove) => {
this.toRemoveProbably = toRemove
this.forceUpdate()
}));
}} />
<RaisedButton label={__('Clean torrents')} secondary={true} onClick={() => {
window.torrentSocket.emit('removeTorrents', false, window.customLoader((toRemove) => {
this.toRemove = toRemove
this.forceUpdate()
}));
}} />
</div>
<div className='row center pad0-75'>
<RaisedButton label={__('Check torrents')} primary={true} onClick={() => {
window.torrentSocket.emit('removeTorrents', true, window.customLoader((toRemove) => {
this.toRemoveProbably = toRemove
this.forceUpdate()
}));
}} />
<RaisedButton label={__('Clean torrents')} secondary={true} onClick={() => {
window.torrentSocket.emit('removeTorrents', false, window.customLoader((toRemove) => {
this.toRemove = toRemove
this.forceUpdate()
}));
}} />
</div>
<div className='row center pad0-75'>
<RaisedButton label={__('Save Settings')} primary={true} onClick={() => {
this.saveSettings()
}} />
</div>
<div className='row center pad0-75'>
<RaisedButton label={__('Save Settings')} primary={true} onClick={() => {
this.saveSettings()
}} />
</div>
</div>
</div>
</div>
);
}
</div>
);
}
}

View File

@ -3,18 +3,18 @@ import React from 'react';
export default (props) => {
return (
<div className='column center' style={{color: 'grey', marginTop: '12px'}}>
<svg style={{height: '100px', fill: 'grey'}} viewBox="0 0 264.725 264.725">
<svg style={{height: '100px', fill: 'grey'}} viewBox="0 0 264.725 264.725">
<path d="M220.195,71.427c-0.589-7.654-9.135-15.619-17.979-16.209c-8.844-0.584-17.398,0.301-12.087,6.483
c5.308,6.188,7.074,12.091,4.423,11.212c-2.66-0.896-13.267-7.08-45.104-2.066c-4.126,1.17-21.221-12.682-44.513-12.977
c-23.283-0.295-40.381,6.346-64.85,72.296c-2.356,5.828-18.866,19.386-27.71,25.865C3.536,162.529,0.007,169.787,0,182.763
c-0.018,18.158,25.934,27.187,81.648,26.889c55.715-0.292,85.195-9.388,85.195-9.388c-62.789,6.773-158.907,10.52-158.907-18.687
c0-20.641,28.321-28.47,36.281-28.184c7.958,0.3,13.562,12.673,33.307,5.603c3.247-0.295,1.48,4.423-1.18,7.369
c-2.651,2.942-0.586,6.487,9.73,6.487c10.315,0,41.183,0.295,47.707,0c6.531-0.299,11.839-11.792-9.384-12.68
c-18.548,0.311,12.023-5.773,15.915-21.813c0.709-3.927,8.84-4.139,15.918-4.119c20.777,0.029,34.485,38.193,38.912,38.338
c4.416,0.15,17.979,1.621,17.683-4.273c-0.292-5.897-11.491-3.241-13.854-6.487c-2.359-3.234-10.023-15.504-7.366-21.104
c2.65-5.59,12.674-21.229,24.463-22.988c11.789-1.777,42.451,7.361,47.459,0c5.012-7.372-6.783-11.512-15.918-28.611
C243.779,80.572,238.768,71.728,220.195,71.427z"/>
c5.308,6.188,7.074,12.091,4.423,11.212c-2.66-0.896-13.267-7.08-45.104-2.066c-4.126,1.17-21.221-12.682-44.513-12.977
c-23.283-0.295-40.381,6.346-64.85,72.296c-2.356,5.828-18.866,19.386-27.71,25.865C3.536,162.529,0.007,169.787,0,182.763
c-0.018,18.158,25.934,27.187,81.648,26.889c55.715-0.292,85.195-9.388,85.195-9.388c-62.789,6.773-158.907,10.52-158.907-18.687
c0-20.641,28.321-28.47,36.281-28.184c7.958,0.3,13.562,12.673,33.307,5.603c3.247-0.295,1.48,4.423-1.18,7.369
c-2.651,2.942-0.586,6.487,9.73,6.487c10.315,0,41.183,0.295,47.707,0c6.531-0.299,11.839-11.792-9.384-12.68
c-18.548,0.311,12.023-5.773,15.915-21.813c0.709-3.927,8.84-4.139,15.918-4.119c20.777,0.029,34.485,38.193,38.912,38.338
c4.416,0.15,17.979,1.621,17.683-4.273c-0.292-5.897-11.491-3.241-13.854-6.487c-2.359-3.234-10.023-15.504-7.366-21.104
c2.65-5.59,12.674-21.229,24.463-22.988c11.789-1.777,42.451,7.361,47.459,0c5.012-7.372-6.783-11.512-15.918-28.611
C243.779,80.572,238.768,71.728,220.195,71.427z"/>
</svg>
</div>
</div>
)
}

View File

@ -1,8 +1,8 @@
export default function formatBytes(bytes,decimals) {
if(bytes == 0) return '0 Byte';
var k = 1000; // or 1024 for binary
var dm = decimals + 1 || 3;
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
var i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
if(bytes == 0) return '0 Byte';
var k = 1000; // or 1024 for binary
var dm = decimals + 1 || 3;
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
var i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}

View File

@ -5,64 +5,64 @@ import RaisedButton from 'material-ui/RaisedButton';
import Search from './search'
class Header extends React.Component {
constructor(props)
{
super(props)
this.header = React.createRef();
}
componentDidMount()
{
constructor(props)
{
super(props)
this.header = React.createRef();
}
componentDidMount()
{
window.onscroll = () => {
if (window.pageYOffset >= 15)
{
const scrollHeight = Math.max(
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight,
document.body.clientHeight, document.documentElement.clientHeight
);
const scrollHeight = Math.max(
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight,
document.body.clientHeight, document.documentElement.clientHeight
);
if(scrollHeight - 240 < document.documentElement.clientHeight)
{
return
}
if(scrollHeight - 240 < document.documentElement.clientHeight)
{
return
}
if(!this.stickyHeader)
{
this.stickyHeader = true
this.header.current.classList.add("sticky");
this.stickyHeader = true
this.header.current.classList.add("sticky");
}
}
else
{
if(this.stickyHeader)
{
this.stickyHeader = false
this.header.current.classList.remove("sticky");
this.stickyHeader = false
this.header.current.classList.remove("sticky");
}
}
};
}
componentWillUnmount()
{
window.onscroll = null
}
}
componentWillUnmount()
{
window.onscroll = null
}
render()
{
return (
<div ref={this.header} className='header'>
<Card className='w100p header-main' style={{position: 'fixed', zIndex: 2}}>
<CardMedia
overlay={<CardTitle className='header-title' title={__('Yarrr, Landlubbers!')} style={{paddingTop: 2}} subtitle={
<div>
<div className='row' style={{position: 'absolute', top: -65}}>
<svg className='clickable'
onClick={() => {
window.router('/config')
}}
fill='white' style={{height: 45, margin: 4}} viewBox="0 0 932.179 932.179">
<g>
<path d="M61.2,341.538c4.9,16.8,11.7,33,20.3,48.2l-24.5,30.9c-8,10.1-7.1,24.5,1.9,33.6l42.2,42.2c9.1,9.1,23.5,9.899,33.6,1.899
render()
{
return (
<div ref={this.header} className='header'>
<Card className='w100p header-main' style={{position: 'fixed', zIndex: 2}}>
<CardMedia
overlay={<CardTitle className='header-title' title={__('Yarrr, Landlubbers!')} style={{paddingTop: 2}} subtitle={
<div>
<div className='row' style={{position: 'absolute', top: -65}}>
<svg className='clickable'
onClick={() => {
window.router('/config')
}}
fill='white' style={{height: 45, margin: 4}} viewBox="0 0 932.179 932.179">
<g>
<path d="M61.2,341.538c4.9,16.8,11.7,33,20.3,48.2l-24.5,30.9c-8,10.1-7.1,24.5,1.9,33.6l42.2,42.2c9.1,9.1,23.5,9.899,33.6,1.899
l30.7-24.3c15.8,9.101,32.6,16.2,50.1,21.2l4.6,39.5c1.5,12.8,12.3,22.4,25.1,22.4h59.7c12.8,0,23.6-9.601,25.1-22.4l4.4-38.1
c18.8-4.9,36.8-12.2,53.7-21.7l29.7,23.5c10.1,8,24.5,7.1,33.6-1.9l42.2-42.2c9.1-9.1,9.9-23.5,1.9-33.6l-23.1-29.3
c9.6-16.601,17.1-34.3,22.1-52.8l35.6-4.1c12.801-1.5,22.4-12.3,22.4-25.1v-59.7c0-12.8-9.6-23.6-22.4-25.1l-35.1-4.1
@ -71,7 +71,7 @@ class Header extends React.Component {
c-19.8,5.3-38.7,13.3-56.3,23.8l-27.5-21.8c-10.1-8-24.5-7.1-33.6,1.9l-42.2,42.2c-9.1,9.1-9.9,23.5-1.9,33.6l23,29.1
c-9.2,16.6-16.2,34.3-20.8,52.7l-36.8,4.2c-12.8,1.5-22.4,12.3-22.4,25.1v59.7c0,12.8,9.6,23.6,22.4,25.1L61.2,341.538z
M277.5,180.038c54.4,0,98.7,44.3,98.7,98.7s-44.3,98.7-98.7,98.7c-54.399,0-98.7-44.3-98.7-98.7S223.1,180.038,277.5,180.038z"/>
<path d="M867.699,356.238l-31.5-26.6c-9.699-8.2-24-7.8-33.199,0.9l-17.4,16.3c-14.699-7.1-30.299-12.1-46.4-15l-4.898-24
<path d="M867.699,356.238l-31.5-26.6c-9.699-8.2-24-7.8-33.199,0.9l-17.4,16.3c-14.699-7.1-30.299-12.1-46.4-15l-4.898-24
c-2.5-12.4-14-21-26.602-20l-41.1,3.5c-12.6,1.1-22.5,11.4-22.9,24.1l-0.799,24.4c-15.801,5.7-30.701,13.5-44.301,23.3
l-20.799-13.8c-10.602-7-24.701-5-32.9,4.7l-26.6,31.7c-8.201,9.7-7.801,24,0.898,33.2l18.201,19.399
c-6.301,14.2-10.801,29.101-13.4,44.4l-26,5.3c-12.4,2.5-21,14-20,26.601l3.5,41.1c1.1,12.6,11.4,22.5,24.1,22.9l28.1,0.899
@ -82,7 +82,7 @@ class Header extends React.Component {
c-5.201-14.6-12.201-28.399-20.9-41.2l13.699-20.6C879.4,378.638,877.4,364.438,867.699,356.238z M712.801,593.837
c-44.4,3.801-83.602-29.3-87.301-73.699c-3.801-44.4,29.301-83.601,73.699-87.301c44.4-3.8,83.602,29.301,87.301,73.7
C790.301,550.938,757.199,590.138,712.801,593.837z"/>
<path d="M205,704.438c-12.6,1.3-22.3,11.899-22.4,24.6l-0.3,25.3c-0.2,12.7,9.2,23.5,21.8,25.101l18.6,2.399
<path d="M205,704.438c-12.6,1.3-22.3,11.899-22.4,24.6l-0.3,25.3c-0.2,12.7,9.2,23.5,21.8,25.101l18.6,2.399
c3.1,11.301,7.5,22.101,13.2,32.301l-12,14.8c-8,9.899-7.4,24.1,1.5,33.2l17.7,18.1c8.9,9.1,23.1,10.1,33.2,2.3l14.899-11.5
c10.5,6.2,21.601,11.101,33.2,14.5l2,19.2c1.3,12.6,11.9,22.3,24.6,22.4l25.301,0.3c12.699,0.2,23.5-9.2,25.1-21.8l2.3-18.2
c12.601-3.101,24.601-7.8,36-14l14,11.3c9.9,8,24.101,7.4,33.201-1.5l18.1-17.7c9.1-8.899,10.1-23.1,2.301-33.2L496.6,818.438
@ -93,47 +93,47 @@ class Header extends React.Component {
l-18.2,17.801c-9.1,8.899-10.1,23.1-2.3,33.199l10.7,13.801c-6.2,11-11.1,22.699-14.3,35L205,704.438z M368.3,675.837
c36.3,0.4,65.399,30.301,65,66.601c-0.4,36.3-30.301,65.399-66.601,65c-36.3-0.4-65.399-30.3-65-66.601
C302.1,704.538,332,675.438,368.3,675.837z"/>
</g>
</svg>
<svg className='clickable'
onClick={() => {
window.router('/filters')
}}
fill='white' style={{height: 45, margin: 4}} viewBox="0 0 512 512">
<g>
<g>
<path d="M256,103.536c-58.559,0-106.2,47.641-106.2,106.2c0,4.512,3.657,8.169,8.169,8.169s8.169-3.658,8.169-8.169
</g>
</svg>
<svg className='clickable'
onClick={() => {
window.router('/filters')
}}
fill='white' style={{height: 45, margin: 4}} viewBox="0 0 512 512">
<g>
<g>
<path d="M256,103.536c-58.559,0-106.2,47.641-106.2,106.2c0,4.512,3.657,8.169,8.169,8.169s8.169-3.658,8.169-8.169
c0-49.55,40.313-89.862,89.862-89.862c49.549,0,89.862,40.311,89.862,89.862c0,4.512,3.658,8.169,8.169,8.169
c4.513,0,8.169-3.658,8.169-8.169C362.2,151.177,314.559,103.536,256,103.536z"/>
</g>
</g>
<g>
<g>
<path d="M256,136.213c-40.541,0-73.523,32.982-73.523,73.523c0,4.512,3.657,8.169,8.169,8.169s8.169-3.658,8.169-8.169
</g>
</g>
<g>
<g>
<path d="M256,136.213c-40.541,0-73.523,32.982-73.523,73.523c0,4.512,3.657,8.169,8.169,8.169s8.169-3.658,8.169-8.169
c0-31.532,25.654-57.185,57.185-57.185s57.185,25.653,57.185,57.185c0,4.512,3.657,8.169,8.169,8.169
c4.513,0,8.169-3.658,8.169-8.169C329.523,169.195,296.541,136.213,256,136.213z"/>
</g>
</g>
<g>
<g>
<path d="M503.801,234.245H8.199c-4.513,0-8.169,3.658-8.169,8.169v43.569c0,2.721,1.354,5.263,3.612,6.781L167.331,402.76
</g>
</g>
<g>
<g>
<path d="M503.801,234.245H8.199c-4.513,0-8.169,3.658-8.169,8.169v43.569c0,2.721,1.354,5.263,3.612,6.781L167.331,402.76
c5.092,3.857,9.699,13.038,9.699,19.379v81.693c0,4.512,3.656,8.169,8.169,8.169h141.6c4.513,0,8.169-3.658,8.169-8.169v-81.693
c0-6.34,4.606-15.522,9.699-19.379l163.691-109.995c2.258-1.517,3.612-4.06,3.612-6.781v-43.569
C511.97,237.903,508.313,234.245,503.801,234.245z M495.632,277.815H157.969c-4.513,0-8.169,3.658-8.169,8.169
s3.657,8.169,8.169,8.169h319.028l-141.61,95.159c-0.1,0.068-0.199,0.137-0.296,0.209c-9.383,6.93-16.458,20.951-16.458,32.616
v73.523H193.37v-73.523c0-11.665-7.076-25.686-16.458-32.616c-0.098-0.072-0.197-0.142-0.296-0.209L35.003,294.153h90.289
c4.513,0,8.169-3.658,8.169-8.169s-3.657-8.169-8.169-8.169H16.368v-27.231h479.263V277.815z"/>
</g>
</g>
<g>
<g>
<path d="M296.504,397.63h-81.006c-4.513,0-8.169,3.658-8.169,8.169c0,4.512,3.657,8.169,8.169,8.169h81.006
</g>
</g>
<g>
<g>
<path d="M296.504,397.63h-81.006c-4.513,0-8.169,3.658-8.169,8.169c0,4.512,3.657,8.169,8.169,8.169h81.006
c4.513,0,8.169-3.658,8.169-8.169C304.674,401.288,301.016,397.63,296.504,397.63z"/>
</g>
</g>
<g>
<g>
<path d="M458.171,168.573l-36.481-3.382c-3.895-14.477-9.68-28.351-17.251-41.376l23.416-28.2
</g>
</g>
<g>
<g>
<path d="M458.171,168.573l-36.481-3.382c-3.895-14.477-9.68-28.351-17.251-41.376l23.416-28.2
c2.696-3.246,2.475-8.011-0.509-10.995l-46.545-46.545c-2.983-2.983-7.749-3.206-10.996-0.509L341.51,61.063
c-13.017-7.52-26.88-13.258-41.343-17.113l-3.386-36.534C296.391,3.213,292.866,0,288.647,0h-65.826
c-4.22,0-7.745,3.213-8.134,7.416l-3.401,36.68c-14.413,3.894-28.222,9.657-41.187,17.193L141.73,37.731
@ -146,56 +146,56 @@ class Header extends React.Component {
c2.986,1.866,6.834,1.607,9.545-0.645l27.023-22.438l36.013,36.013l-22.371,26.941c-2.255,2.717-2.511,6.575-0.632,9.565
c9.189,14.625,15.814,30.514,19.69,47.225c0.795,3.429,3.699,5.964,7.203,6.287l34.829,3.229v25.582
c0,4.512,3.657,8.169,8.169,8.169s8.169-3.658,8.169-8.169v-33.029C465.586,172.488,462.373,168.963,458.171,168.573z"/>
</g>
</g>
</svg>
</div>
</g>
</g>
</svg>
</div>
{__('Welcome to')} ROTB! {__('This is file search engine based on the torrents from the internet')}.
{__('Here you can easily find torrent or file that you intrested for')}. {__('We are not responsible for any content')}:
{__('this is only information about content that collected automatically')}!
</div>} />}
>
<div className='row header-row' style={{
padding: '15px',
background: `url('${Background}') no-repeat`,
backgroundSize: 'cover',
transition: '1s'
}}>
<RaisedButton
label={__('Feed')}
onClick={() => {
window.router('/')
}}
backgroundColor='#69238c'
labelColor='white'
style={{height: 60, borderRadius: 6, margin: 5, zIndex: 1}}
buttonStyle={{borderRadius: 5}}
icon={<svg fill='white' style={{height: 28}} viewBox="0 0 64.051 64.051">
<g>
<path d="M8,0v2c16,0,31.173,7.065,41.472,19.386C57.567,31.065,62.051,43.358,62.051,56h2c0-13.11-4.649-25.858-13.044-35.897
C40.326,7.327,25,0,8,0z"/>
<path d="M50.769,56h2C52.769,31.343,33,11.282,8,11.282v2C32,13.282,50.769,32.445,50.769,56z"/>
<path d="M8,22.564v2c17,0,31.486,14.102,31.486,31.436h2C41.486,37.563,26,22.564,8,22.564z"/>
<path d="M30.205,56C30.205,43.784,20,33.846,8,33.846v2c11,0,20.205,9.041,20.205,20.154H30.205z"/>
<path d="M16.103,56c0-4.439-3.612-8.051-8.052-8.051S0,51.561,0,56s3.611,8.051,8.051,8.051S16.103,60.439,16.103,56z M2,56
c0-3.336,2.715-6.051,6.051-6.051c3.337,0,6.052,2.715,6.052,6.051s-2.715,6.051-6.052,6.051C4.715,62.051,2,59.336,2,56z"/>
</g>
</svg>
{__('Welcome to')} ROTB! {__('This is file search engine based on the torrents from the internet')}.
{__('Here you can easily find torrent or file that you intrested for')}. {__('We are not responsible for any content')}:
{__('this is only information about content that collected automatically')}!
</div>} />}
>
<div className='row header-row' style={{
padding: '15px',
background: `url('${Background}') no-repeat`,
backgroundSize: 'cover',
transition: '1s'
}}>
<RaisedButton
label={__('Feed')}
onClick={() => {
window.router('/')
}}
backgroundColor='#69238c'
labelColor='white'
style={{height: 60, borderRadius: 6, margin: 5, zIndex: 1}}
buttonStyle={{borderRadius: 5}}
icon={<svg fill='white' style={{height: 28}} viewBox="0 0 64.051 64.051">
<g>
<path d="M8,0v2c16,0,31.173,7.065,41.472,19.386C57.567,31.065,62.051,43.358,62.051,56h2c0-13.11-4.649-25.858-13.044-35.897
C40.326,7.327,25,0,8,0z"/>
<path d="M50.769,56h2C52.769,31.343,33,11.282,8,11.282v2C32,13.282,50.769,32.445,50.769,56z"/>
<path d="M8,22.564v2c17,0,31.486,14.102,31.486,31.436h2C41.486,37.563,26,22.564,8,22.564z"/>
<path d="M30.205,56C30.205,43.784,20,33.846,8,33.846v2c11,0,20.205,9.041,20.205,20.154H30.205z"/>
<path d="M16.103,56c0-4.439-3.612-8.051-8.052-8.051S0,51.561,0,56s3.611,8.051,8.051,8.051S16.103,60.439,16.103,56z M2,56
c0-3.336,2.715-6.051,6.051-6.051c3.337,0,6.052,2.715,6.052,6.051s-2.715,6.051-6.052,6.051C4.715,62.051,2,59.336,2,56z"/>
</g>
</svg>
}
/>
<RaisedButton
label={__('Downloads')}
onClick={() => {
window.router('/downloads')
}}
backgroundColor='#2080E4'
labelColor='white'
style={{height: 60, borderRadius: 6, margin: 5, zIndex: 1}}
buttonStyle={{borderRadius: 5}}
icon={<svg fill='white' style={{height: 30}} viewBox="0 0 548.176 548.176">
<path d="M524.326,297.352c-15.896-19.89-36.21-32.782-60.959-38.684c7.81-11.8,11.704-24.934,11.704-39.399
}
/>
<RaisedButton
label={__('Downloads')}
onClick={() => {
window.router('/downloads')
}}
backgroundColor='#2080E4'
labelColor='white'
style={{height: 60, borderRadius: 6, margin: 5, zIndex: 1}}
buttonStyle={{borderRadius: 5}}
icon={<svg fill='white' style={{height: 30}} viewBox="0 0 548.176 548.176">
<path d="M524.326,297.352c-15.896-19.89-36.21-32.782-60.959-38.684c7.81-11.8,11.704-24.934,11.704-39.399
c0-20.177-7.139-37.401-21.409-51.678c-14.273-14.272-31.498-21.411-51.675-21.411c-18.083,0-33.879,5.901-47.39,17.703
c-11.225-27.41-29.171-49.393-53.817-65.95c-24.646-16.562-51.818-24.842-81.514-24.842c-40.349,0-74.802,14.279-103.353,42.83
c-28.553,28.544-42.825,62.999-42.825,103.351c0,2.474,0.191,6.567,0.571,12.275c-22.459,10.469-40.349,26.171-53.676,47.106
@ -206,82 +206,82 @@ class Header extends React.Component {
c0-2.474,0.905-4.616,2.712-6.427c1.809-1.805,3.949-2.708,6.423-2.708h54.823c2.478,0,4.609,0.9,6.427,2.708
c1.804,1.811,2.707,3.953,2.707,6.427v100.497h63.954c2.665,0,4.855,0.855,6.563,2.566c1.714,1.711,2.562,3.901,2.562,6.567
C365.438,303.789,364.494,306.064,362.595,308.344z"/>
</svg>
}
/>
<RaisedButton
label={__('Top')}
onClick={() => {
window.router('/top')
}}
backgroundColor='#B1CE57'
labelColor='white'
style={{height: 60, width: 120, borderRadius: 5, margin: 5, zIndex: 1}}
buttonStyle={{borderRadius: 5}}
icon={<svg fill='white' style={{height: 30}} viewBox="0 0 284.929 284.929">
<g>
<path d="M17.128,167.872c1.903,1.902,4.093,2.854,6.567,2.854c2.474,0,4.664-0.952,6.567-2.854L142.466,55.666l112.208,112.206
</svg>
}
/>
<RaisedButton
label={__('Top')}
onClick={() => {
window.router('/top')
}}
backgroundColor='#B1CE57'
labelColor='white'
style={{height: 60, width: 120, borderRadius: 5, margin: 5, zIndex: 1}}
buttonStyle={{borderRadius: 5}}
icon={<svg fill='white' style={{height: 30}} viewBox="0 0 284.929 284.929">
<g>
<path d="M17.128,167.872c1.903,1.902,4.093,2.854,6.567,2.854c2.474,0,4.664-0.952,6.567-2.854L142.466,55.666l112.208,112.206
c1.902,1.902,4.093,2.854,6.563,2.854c2.478,0,4.668-0.952,6.57-2.854l14.274-14.277c1.902-1.902,2.847-4.093,2.847-6.563
c0-2.475-0.951-4.665-2.847-6.567L149.028,7.419c-1.901-1.906-4.088-2.853-6.562-2.853s-4.665,0.95-6.567,2.853L2.856,140.464
C0.95,142.367,0,144.554,0,147.034c0,2.468,0.953,4.658,2.856,6.561L17.128,167.872z"/>
<path d="M149.028,117.055c-1.901-1.906-4.088-2.856-6.562-2.856s-4.665,0.953-6.567,2.856L2.856,250.1
<path d="M149.028,117.055c-1.901-1.906-4.088-2.856-6.562-2.856s-4.665,0.953-6.567,2.856L2.856,250.1
C0.95,252.003,0,254.192,0,256.67c0,2.472,0.953,4.661,2.856,6.564l14.272,14.276c1.903,1.903,4.093,2.848,6.567,2.848
c2.474,0,4.664-0.951,6.567-2.848l112.204-112.209l112.208,112.209c1.902,1.903,4.093,2.852,6.563,2.852
c2.478,0,4.668-0.948,6.57-2.852l14.274-14.276c1.902-1.903,2.847-4.093,2.847-6.564c0-2.478-0.951-4.667-2.847-6.57
L149.028,117.055z"/>
</g>
</svg>
}
/>
<RaisedButton
label={__('Activity')}
onClick={() => {
window.router('/activity')
}}
backgroundColor='#2a5cba'
labelColor='white'
style={{height: 60, width: 150, borderRadius: 5, margin: 5, zIndex: 1}}
buttonStyle={{borderRadius: 5}}
icon={<svg fill='white' style={{height: 30}} viewBox="0 0 352.352 352.352">
<g>
<path d="M255.432,37.172c-7.956-15.3-20.808-28.152-36.107-36.108c-4.284-2.448-10.404-0.612-11.628,4.896
</g>
</svg>
}
/>
<RaisedButton
label={__('Activity')}
onClick={() => {
window.router('/activity')
}}
backgroundColor='#2a5cba'
labelColor='white'
style={{height: 60, width: 150, borderRadius: 5, margin: 5, zIndex: 1}}
buttonStyle={{borderRadius: 5}}
icon={<svg fill='white' style={{height: 30}} viewBox="0 0 352.352 352.352">
<g>
<path d="M255.432,37.172c-7.956-15.3-20.808-28.152-36.107-36.108c-4.284-2.448-10.404-0.612-11.628,4.896
c-1.225,4.284-3.061,8.568-4.896,12.24C153.84-4.444,95.699,20.036,58.979,54.308c-41.616,39.168-62.424,102.816-45.9,157.896
c1.837,7.344,12.24,4.283,11.017-3.061c-7.956-51.408,6.12-99.756,40.392-138.924c37.332-42.228,82.008-45.9,133.416-40.392
c-1.224,3.672-1.836,7.344-1.836,11.016c0,3.06,1.225,4.896,3.673,5.508c0,1.224,0,3.06,0.611,4.284
c3.672,7.956,12.853,6.732,20.196,5.508c11.016-1.224,22.644-3.672,32.436-8.568C256.656,45.74,257.268,40.231,255.432,37.172z
M210.144,40.844c-0.611,0-1.224,0-2.447,0c1.224-7.344,5.508-14.688,9.791-22.032c7.345,4.896,13.465,11.016,18.973,18.36
c-5.508,1.836-11.017,2.448-16.524,3.06C216.876,40.844,213.204,40.844,210.144,40.844z"/>
<path d="M297.048,318.08c58.752-74.664,80.172-204.408-9.18-265.608c-5.509-3.672-11.628,4.896-6.732,9.18
<path d="M297.048,318.08c58.752-74.664,80.172-204.408-9.18-265.608c-5.509-3.672-11.628,4.896-6.732,9.18
c71.604,67.32,63.036,173.196,4.896,246.636c-1.836-1.837-4.284-3.061-6.12-4.284c0-6.12-7.956-11.017-12.853-5.508
c-11.016,12.239-14.688,27.539-18.972,43.451c-1.224,4.896,2.448,10.404,7.956,10.404c17.136,0,34.884-3.672,50.796-11.016
c4.284-2.448,4.896-9.181,1.836-12.24C303.78,324.812,300.72,321.752,297.048,318.08z M273.18,314.407
c5.508,4.896,10.404,10.404,15.3,15.912c-7.344,2.448-15.3,3.672-23.256,4.896C267.06,327.872,269.508,320.527,273.18,314.407z"/>
<path d="M219.936,321.14c-36.72,14.076-63.648,15.912-100.979-0.612c-27.54-12.239-51.408-31.212-71.604-52.02
<path d="M219.936,321.14c-36.72,14.076-63.648,15.912-100.979-0.612c-27.54-12.239-51.408-31.212-71.604-52.02
c4.896-3.672,9.792-7.344,15.3-11.016c4.896-3.673,4.896-9.793,0-13.465c-13.464-9.18-29.376-16.523-45.288-20.808
c-4.284-1.224-8.567,1.224-9.792,5.508c-3.06,11.017-4.284,21.42-2.447,33.048c1.224,7.956,5.508,20.809,13.464,24.48
c3.672,1.836,9.792,0,9.18-5.508l0,0c3.06-1.836,6.12-3.061,9.18-4.896c19.584,28.764,51.408,50.796,83.232,63.036
c30.6,12.24,80.784,22.644,105.264-7.344C229.728,327.26,226.668,318.691,219.936,321.14z M21.035,260.552
c-1.836-6.732-1.224-13.464,0-19.584c9.181,3.06,17.748,7.344,26.316,12.24c-6.732,6.731-14.688,11.628-22.644,17.136
C23.483,266.06,21.647,263.611,21.035,260.552z"/>
</g>
</svg>
}
/>
<div className='fs0-85 pad0-75 column search-panel' style={{
marginLeft: 'auto',
marginTop: '-10px',
zIndex: 2
}}>
<Search />
</g>
</svg>
}
/>
<div className='fs0-85 pad0-75 column search-panel' style={{
marginLeft: 'auto',
marginTop: '-10px',
zIndex: 2
}}>
<Search />
</div>
</div>
</div>
</CardMedia>
</Card>
<div className='clear-header-space' style={{transition: '1.0s'}} />
</div>
)
</CardMedia>
</Card>
<div className='clear-header-space' style={{transition: '1.0s'}} />
</div>
)
}
}
}
export {Header}

View File

@ -12,6 +12,6 @@ import './css/izi/animations.css';
import './index.css';
ReactDOM.render(
<App />,
document.getElementById('mount-point')
<App />,
document.getElementById('mount-point')
);

View File

@ -8,65 +8,65 @@ import MenuItem from 'material-ui/MenuItem';
import Checkbox from 'material-ui/Checkbox';
export default class InputFilesFilter extends Component {
constructor(props)
{
super(props)
this.state = {
files: this.props.value || { min: 0, max: 50 },
enabled: false || this.props.enabled,
filesMax: this.props.filesMax || 100 // 1mb
}
}
setState(val)
{
if(val.filesMax && this.state.files.max > val.filesMax)
val.files = {min: this.state.files.min, max: val.filesMax};
constructor(props)
{
super(props)
this.state = {
files: this.props.value || { min: 0, max: 50 },
enabled: false || this.props.enabled,
filesMax: this.props.filesMax || 100 // 1mb
}
}
setState(val)
{
if(val.filesMax && this.state.files.max > val.filesMax)
val.files = {min: this.state.files.min, max: val.filesMax};
if(val.filesMax && this.state.files.min > val.filesMax)
val.files = {min: 0, max: val.files ? val.files.max || this.state.files.max : this.state.files.max };
if(val.filesMax && this.state.files.min > val.filesMax)
val.files = {min: 0, max: val.files ? val.files.max || this.state.files.max : this.state.files.max };
super.setState(val, () => {
if(this.props.onChange)
this.props.onChange({
enabled: this.state.enabled,
filesMax: this.state.filesMax,
files: !this.state.enabled ? {min: 0, max: 0} : this.state.files
})
})
}
render() {
return (
<div className='filter-row row inline w100p'>
<Checkbox
label={__("Files filter")}
checked={this.state.enabled}
style={{width: 150, display: 'flex', minWidth: 130}}
onCheck={() => this.setState({enabled: !this.state.enabled})}
/>
<div className='filter-control-row row inline w100p' style={{opacity: this.state.enabled ? 1 : 0.4, transition: '0.5s', paddingLeft: 9}}>
<InputRange
maxValue={this.state.filesMax}
minValue={0}
value={this.state.files}
style={this.props.style}
className={this.props.className}
onChange={files => this.setState({ files })}
/>
<SelectField
floatingLabelText={__('Size type')}
value={this.state.filesMax}
onChange={(event, index, value) => this.setState({filesMax: value})}
className='filter-control-border'
>
<MenuItem value={10} primaryText={"10 " + __("Files or less")} />
<MenuItem value={100} primaryText={"100 " + __("Files or less")} />
<MenuItem value={1000} primaryText={"1000 " + __("Files or less")} />
<MenuItem value={10000} primaryText={"10000 " + __("Files or less")} />
<MenuItem value={100000} primaryText={"100000 " + __("Files or less")} />
<MenuItem value={1000000} primaryText={"1000000 " + __("Files or less")} />
</SelectField>
</div>
</div>
);
}
super.setState(val, () => {
if(this.props.onChange)
this.props.onChange({
enabled: this.state.enabled,
filesMax: this.state.filesMax,
files: !this.state.enabled ? {min: 0, max: 0} : this.state.files
})
})
}
render() {
return (
<div className='filter-row row inline w100p'>
<Checkbox
label={__("Files filter")}
checked={this.state.enabled}
style={{width: 150, display: 'flex', minWidth: 130}}
onCheck={() => this.setState({enabled: !this.state.enabled})}
/>
<div className='filter-control-row row inline w100p' style={{opacity: this.state.enabled ? 1 : 0.4, transition: '0.5s', paddingLeft: 9}}>
<InputRange
maxValue={this.state.filesMax}
minValue={0}
value={this.state.files}
style={this.props.style}
className={this.props.className}
onChange={files => this.setState({ files })}
/>
<SelectField
floatingLabelText={__('Size type')}
value={this.state.filesMax}
onChange={(event, index, value) => this.setState({filesMax: value})}
className='filter-control-border'
>
<MenuItem value={10} primaryText={"10 " + __("Files or less")} />
<MenuItem value={100} primaryText={"100 " + __("Files or less")} />
<MenuItem value={1000} primaryText={"1000 " + __("Files or less")} />
<MenuItem value={10000} primaryText={"10000 " + __("Files or less")} />
<MenuItem value={100000} primaryText={"100000 " + __("Files or less")} />
<MenuItem value={1000000} primaryText={"1000000 " + __("Files or less")} />
</SelectField>
</div>
</div>
);
}
}

View File

@ -8,67 +8,67 @@ import MenuItem from 'material-ui/MenuItem';
import Checkbox from 'material-ui/Checkbox';
export default class InputSize extends Component {
constructor(props)
{
super(props)
this.state = {
size: this.props.value || { min: 0, max: 500 * 1024 },
enabled: false || this.props.enabled,
maxSize: this.props.maxSize || 1024 * 1024 // 1mb
}
}
setState(val)
{
if(val.maxSize && this.state.size.max > val.maxSize)
val.size = {min: this.state.size.min, max: val.maxSize};
constructor(props)
{
super(props)
this.state = {
size: this.props.value || { min: 0, max: 500 * 1024 },
enabled: false || this.props.enabled,
maxSize: this.props.maxSize || 1024 * 1024 // 1mb
}
}
setState(val)
{
if(val.maxSize && this.state.size.max > val.maxSize)
val.size = {min: this.state.size.min, max: val.maxSize};
if(val.maxSize && this.state.size.min > val.maxSize)
val.size = {min: 0, max: val.size ? val.size.max || this.state.size.max : this.state.size.max };
if(val.maxSize && this.state.size.min > val.maxSize)
val.size = {min: 0, max: val.size ? val.size.max || this.state.size.max : this.state.size.max };
super.setState(val, () => {
if(this.props.onChange)
this.props.onChange({
enabled: this.state.enabled,
maxSize: this.state.maxSize,
size: !this.state.enabled ? {min: 0, max: 0} : this.state.size
})
})
}
render() {
return (
<div className='filter-row row inline w100p'>
<Checkbox
label={__('Size filter')}
checked={this.state.enabled}
style={{width: 150, display: 'flex', minWidth: 130}}
onCheck={() => this.setState({enabled: !this.state.enabled})}
/>
<div className='filter-control-row row inline w100p' style={{opacity: this.state.enabled ? 1 : 0.4, transition: '0.5s', paddingLeft: 9}}>
<InputRange
maxValue={this.state.maxSize}
minValue={0}
value={this.state.size}
formatLabel={size => formatBytes(size)}
style={this.props.style}
className={this.props.className}
onChange={size => this.setState({ size })}
/>
<SelectField
floatingLabelText={__('Size type max')}
value={this.state.maxSize}
onChange={(event, index, value) => this.setState({maxSize: value})}
className='filter-control-border'
>
<MenuItem value={1024} primaryText="KB" />
<MenuItem value={1024 * 1024} primaryText="MB" />
<MenuItem value={1024 * 1024 * 100} primaryText="100 MB" />
<MenuItem value={1024 * 1024 * 1024} primaryText="GB" />
<MenuItem value={10 * 1024 * 1024 * 1024} primaryText="10 GB" />
<MenuItem value={100 * 1024 * 1024 * 1024} primaryText="100 GB" />
<MenuItem value={1024 * 1024 * 1024 * 1024} primaryText="TB" />
</SelectField>
</div>
</div>
);
}
super.setState(val, () => {
if(this.props.onChange)
this.props.onChange({
enabled: this.state.enabled,
maxSize: this.state.maxSize,
size: !this.state.enabled ? {min: 0, max: 0} : this.state.size
})
})
}
render() {
return (
<div className='filter-row row inline w100p'>
<Checkbox
label={__('Size filter')}
checked={this.state.enabled}
style={{width: 150, display: 'flex', minWidth: 130}}
onCheck={() => this.setState({enabled: !this.state.enabled})}
/>
<div className='filter-control-row row inline w100p' style={{opacity: this.state.enabled ? 1 : 0.4, transition: '0.5s', paddingLeft: 9}}>
<InputRange
maxValue={this.state.maxSize}
minValue={0}
value={this.state.size}
formatLabel={size => formatBytes(size)}
style={this.props.style}
className={this.props.className}
onChange={size => this.setState({ size })}
/>
<SelectField
floatingLabelText={__('Size type max')}
value={this.state.maxSize}
onChange={(event, index, value) => this.setState({maxSize: value})}
className='filter-control-border'
>
<MenuItem value={1024} primaryText="KB" />
<MenuItem value={1024 * 1024} primaryText="MB" />
<MenuItem value={1024 * 1024 * 100} primaryText="100 MB" />
<MenuItem value={1024 * 1024 * 1024} primaryText="GB" />
<MenuItem value={10 * 1024 * 1024 * 1024} primaryText="10 GB" />
<MenuItem value={100 * 1024 * 1024 * 1024} primaryText="100 GB" />
<MenuItem value={1024 * 1024 * 1024 * 1024} primaryText="TB" />
</SelectField>
</div>
</div>
);
}
}

View File

@ -2,84 +2,84 @@ import React, { Component } from 'react';
import singleton from './singleton'
class PagesPie extends Component {
pie = [];
pie = [];
open(pages, params) {
if (params && params.replace) {
if (params.replace === 'all') {
this.pie = [];
} else
if (params.replace === 'last') {
this.pie.pop();
}
this.forceUpdate();
delete params.replace;
}
setTimeout(() => {
if (Array.isArray(pages)) {
for (let i in pages) {
this.pie.push({
Page: pages[i],
params: params
});
}
} else {
this.pie.push({
Page: pages,
params: params
});
}
this.forceUpdate();
}, 0);
}
close(count) {
if (count && typeof count === 'number') {
for (let i = 0; i < count; i++) {
this.pie.pop();
}
} else {
this.pie.pop();
}
this.forceUpdate();
}
findOpened(windowType) {
for (let i in this.refs) {
if(this.refs[i] instanceof windowType)
return this.refs[i];
}
}
// ОТРИСОВКА
render() {
if (this.pie.length > 0) {
return (
<div
className={'pie full-size ' + (this.props.className || '')}
>
{
this.pie.map(({Page, params}, index) => {
let focus = false;
if (index === this.pie.length-1) {
focus = true;
}
return (
<Page
focused={focus}
closeHandler={() => { index> 0 ? this.close() : null}}
index={index}
key={index}
ref={index}
{...params}
>
</Page>
)
})
}
</div>
)
} else {
return null
}
}
open(pages, params) {
if (params && params.replace) {
if (params.replace === 'all') {
this.pie = [];
} else
if (params.replace === 'last') {
this.pie.pop();
}
this.forceUpdate();
delete params.replace;
}
setTimeout(() => {
if (Array.isArray(pages)) {
for (let i in pages) {
this.pie.push({
Page: pages[i],
params: params
});
}
} else {
this.pie.push({
Page: pages,
params: params
});
}
this.forceUpdate();
}, 0);
}
close(count) {
if (count && typeof count === 'number') {
for (let i = 0; i < count; i++) {
this.pie.pop();
}
} else {
this.pie.pop();
}
this.forceUpdate();
}
findOpened(windowType) {
for (let i in this.refs) {
if(this.refs[i] instanceof windowType)
return this.refs[i];
}
}
// ОТРИСОВКА
render() {
if (this.pie.length > 0) {
return (
<div
className={'pie full-size ' + (this.props.className || '')}
>
{
this.pie.map(({Page, params}, index) => {
let focus = false;
if (index === this.pie.length-1) {
focus = true;
}
return (
<Page
focused={focus}
closeHandler={() => { index> 0 ? this.close() : null}}
index={index}
key={index}
ref={index}
{...params}
>
</Page>
)
})
}
</div>
)
} else {
return null
}
}
}
export default singleton(PagesPie)

View File

@ -8,152 +8,152 @@ import FlatButton from 'material-ui/FlatButton';
import RefreshIndicator from 'material-ui/RefreshIndicator';
export default class RecentTorrents extends Component {
constructor() {
super()
this.torrents = [];
this.torrentsAssoc = {};
this.displayQueue = [];
this.displayQueueAssoc = {};
this.maxQueueSize = 1000;
this.maxDisplaySize = 10;
this.state = {
pause: false,
searchingIndicator: false
}
}
componentDidMount() {
window.torrentSocket.emit('recentTorrents', window.customLoader((data) => {
if(data) {
this.torrents = data;
//this.forceUpdate(); // вызывается через searchingIndicator
}
constructor() {
super()
this.torrents = [];
this.torrentsAssoc = {};
this.displayQueue = [];
this.displayQueueAssoc = {};
this.maxQueueSize = 1000;
this.maxDisplaySize = 10;
this.state = {
pause: false,
searchingIndicator: false
}
}
componentDidMount() {
window.torrentSocket.emit('recentTorrents', window.customLoader((data) => {
if(data) {
this.torrents = data;
//this.forceUpdate(); // вызывается через searchingIndicator
}
this.displayNewTorrent = () => {
if(!this.displayNewTorrent) {
return;
}
this.displayNewTorrent = () => {
if(!this.displayNewTorrent) {
return;
}
if(this.displayQueue.length == 0) {
setTimeout(this.displayNewTorrent, 1000);
return;
}
if(this.displayQueue.length == 0) {
setTimeout(this.displayNewTorrent, 1000);
return;
}
const speed = 850;
const speed = 850;
if(this.state.pause) {
setTimeout(this.displayNewTorrent, speed);
return;
}
if(this.state.pause) {
setTimeout(this.displayNewTorrent, speed);
return;
}
let torrent = this.displayQueue.shift();
this.torrents.unshift(torrent);
this.torrentsAssoc[torrent.hash] = torrent;
if(this.torrents.length > this.maxDisplaySize) {
let toDelete = this.torrents.pop()
delete this.torrentsAssoc[toDelete.hash];
delete this.displayQueueAssoc[toDelete.hash];
}
this.displayTorrentCounterValue = this.displayQueue.length;
let torrent = this.displayQueue.shift();
this.torrents.unshift(torrent);
this.torrentsAssoc[torrent.hash] = torrent;
if(this.torrents.length > this.maxDisplaySize) {
let toDelete = this.torrents.pop()
delete this.torrentsAssoc[toDelete.hash];
delete this.displayQueueAssoc[toDelete.hash];
}
this.displayTorrentCounterValue = this.displayQueue.length;
this.forceUpdate();
setTimeout(this.displayNewTorrent, speed);
}
this.displayNewTorrent();
this.forceUpdate();
setTimeout(this.displayNewTorrent, speed);
}
this.displayNewTorrent();
this.displayTorrentCounterValue = 0;
this.displayTorrentCounter = setInterval(() => {
if(this.displayTorrentCounterValue != this.displayQueue.length) {
this.displayTorrentCounterValue = this.displayQueue.length;
this.forceUpdate();
}
}, 40);
}, () => {
this.setState({
searchingIndicator: true
});
}, () => {
this.setState({
searchingIndicator: false
});
}));
this.newTorrentFunc = (torrent) => {
if(this.displayQueue.length < this.maxQueueSize) {
this.displayQueue.push(torrent);
this.displayQueueAssoc[torrent.hash] = torrent;
}
};
window.torrentSocket.on('newTorrent', this.newTorrentFunc);
this.displayTorrentCounterValue = 0;
this.displayTorrentCounter = setInterval(() => {
if(this.displayTorrentCounterValue != this.displayQueue.length) {
this.displayTorrentCounterValue = this.displayQueue.length;
this.forceUpdate();
}
}, 40);
}, () => {
this.setState({
searchingIndicator: true
});
}, () => {
this.setState({
searchingIndicator: false
});
}));
this.newTorrentFunc = (torrent) => {
if(this.displayQueue.length < this.maxQueueSize) {
this.displayQueue.push(torrent);
this.displayQueueAssoc[torrent.hash] = torrent;
}
};
window.torrentSocket.on('newTorrent', this.newTorrentFunc);
this.tracketUpdate = (statistic) => {
if(statistic.hash in this.displayQueueAssoc)
{
Object.assign(this.displayQueueAssoc[statistic.hash], statistic);
if(statistic.hash in this.torrentsAssoc) {
this.forceUpdate();
}
}
}
window.torrentSocket.on('trackerTorrentUpdate', this.tracketUpdate);
}
pauseAndContinue() {
this.setState({
pause: !this.state.pause
});
}
componentWillUnmount() {
if(this.newTorrentFunc)
window.torrentSocket.off('newTorrent', this.newTorrentFunc);
if(this.tracketUpdate)
window.torrentSocket.off('trackerTorrentUpdate', this.tracketUpdate);
if(this.displayNewTorrent)
delete this.displayNewTorrent;
if(this.displayTorrentCounter)
clearInterval(this.displayTorrentCounter);
}
render() {
const style = {
refresh: {
display: 'inline-block',
position: 'relative',
},
};
this.tracketUpdate = (statistic) => {
if(statistic.hash in this.displayQueueAssoc)
{
Object.assign(this.displayQueueAssoc[statistic.hash], statistic);
if(statistic.hash in this.torrentsAssoc) {
this.forceUpdate();
}
}
}
window.torrentSocket.on('trackerTorrentUpdate', this.tracketUpdate);
}
pauseAndContinue() {
this.setState({
pause: !this.state.pause
});
}
componentWillUnmount() {
if(this.newTorrentFunc)
window.torrentSocket.off('newTorrent', this.newTorrentFunc);
if(this.tracketUpdate)
window.torrentSocket.off('trackerTorrentUpdate', this.tracketUpdate);
if(this.displayNewTorrent)
delete this.displayNewTorrent;
if(this.displayTorrentCounter)
clearInterval(this.displayTorrentCounter);
}
render() {
const style = {
refresh: {
display: 'inline-block',
position: 'relative',
},
};
if(this.state.searchingIndicator) {
return (
<div className='pad1'>
<RefreshIndicator
size={50}
left={0}
top={0}
loadingColor="#FF9800"
status="loading"
style={style.refresh}
/>
</div>
);
}
if(this.state.searchingIndicator) {
return (
<div className='pad1'>
<RefreshIndicator
size={50}
left={0}
top={0}
loadingColor="#FF9800"
status="loading"
style={style.refresh}
/>
</div>
);
}
if(!this.torrents || this.torrents.length == 0)
return null;
if(!this.torrents || this.torrents.length == 0)
return null;
return (
<List className='animated recent-torrents torrents-container'>
<Subheader className='recent-title' inset={true}>
<FlatButton style={{marginRight: '8px'}} primary={true} label={__('top')} labelStyle={{color: "#a4c639"}} onClick={() =>{
window.router('/top');
}} />
<FlatButton style={{marginRight: '8px'}} label={!this.state.pause ? __('running') : __('stoped')} secondary={this.state.pause} primary={!this.state.pause} onClick={() =>{
this.pauseAndContinue()
}} />
{__('Most recent torrents')}{this.displayQueue.length > 0 ? ` (${__('and')} ${this.displayQueue.length} ${__('more')})` : null}
</Subheader>
<Divider />
{
this.torrents.map((torrent, index) =>{
return <TorrentLine key={index} torrent={torrent} />;
})
}
</List>
);
}
return (
<List className='animated recent-torrents torrents-container'>
<Subheader className='recent-title' inset={true}>
<FlatButton style={{marginRight: '8px'}} primary={true} label={__('top')} labelStyle={{color: "#a4c639"}} onClick={() =>{
window.router('/top');
}} />
<FlatButton style={{marginRight: '8px'}} label={!this.state.pause ? __('running') : __('stoped')} secondary={this.state.pause} primary={!this.state.pause} onClick={() =>{
this.pauseAndContinue()
}} />
{__('Most recent torrents')}{this.displayQueue.length > 0 ? ` (${__('and')} ${this.displayQueue.length} ${__('more')})` : null}
</Subheader>
<Divider />
{
this.torrents.map((torrent, index) =>{
return <TorrentLine key={index} torrent={torrent} />;
})
}
</List>
);
}
}

View File

@ -9,105 +9,105 @@
// This link also includes instructions on opting out of this behavior.
const isLocalhost = Boolean(
window.location.hostname === 'localhost' ||
window.location.hostname === 'localhost' ||
// [::1] is the IPv6 localhost address.
window.location.hostname === '[::1]' ||
// 127.0.0.1/8 is considered localhost for IPv4.
window.location.hostname.match(
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
)
);
export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
return;
}
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
// The URL constructor is available in all browsers that support SW.
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
// from what our page is served on. This might happen if a CDN is used to
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (!isLocalhost) {
// Is not local host. Just register service worker
registerValidSW(swUrl);
} else {
// This is running on localhost. Lets check if a service worker still exists or not.
checkValidServiceWorker(swUrl);
}
});
}
if (!isLocalhost) {
// Is not local host. Just register service worker
registerValidSW(swUrl);
} else {
// This is running on localhost. Lets check if a service worker still exists or not.
checkValidServiceWorker(swUrl);
}
});
}
}
function registerValidSW(swUrl) {
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and
// the fresh content will have been added to the cache.
// It's the perfect time to display a "New content is
// available; please refresh." message in your web app.
console.log('New content is available; please refresh.');
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
navigator.serviceWorker
.register(swUrl)
.then(registration => {
registration.onupdatefound = () => {
const installingWorker = registration.installing;
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and
// the fresh content will have been added to the cache.
// It's the perfect time to display a "New content is
// available; please refresh." message in your web app.
console.log('New content is available; please refresh.');
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
}
}
};
};
})
.catch(error => {
console.error('Error during service worker registration:', error);
});
}
function checkValidServiceWorker(swUrl) {
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
if (
response.status === 404 ||
// Check if the service worker can be found. If it can't reload the page.
fetch(swUrl)
.then(response => {
// Ensure service worker exists, and that we really are getting a JS file.
if (
response.status === 404 ||
response.headers.get('content-type').indexOf('javascript') === -1
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
) {
// No service worker found. Probably a different app. Reload the page.
navigator.serviceWorker.ready.then(registration => {
registration.unregister().then(() => {
window.location.reload();
});
});
} else {
// Service worker found. Proceed as normal.
registerValidSW(swUrl);
}
})
.catch(() => {
console.log(
'No internet connection found. App is running in offline mode.'
);
});
}
export function unregister() {
if ('serviceWorker' in navigator) {
return navigator.serviceWorker.ready.then(registration => {
console.log("Unregister service worker");
return registration.unregister();
});
} else {
return new Promise(function(resolve, reject) {
resolve();
})
}
if ('serviceWorker' in navigator) {
return navigator.serviceWorker.ready.then(registration => {
console.log("Unregister service worker");
return registration.unregister();
});
} else {
return new Promise(function(resolve, reject) {
resolve();
})
}
}

View File

@ -31,7 +31,7 @@ const router = (page, callback) => {
params[pg.args[i]] = p[i]
}
console.log(params)
pg.callback({
params
})

View File

@ -5,52 +5,52 @@ import InputSize from './input-size';
import FilesFilterInput from './input-files-filter';
export default class AdvancedSearchControl extends Component {
constructor(props)
{
super(props)
this.state = {
type: undefined,
size: {min: 0, max: 0},
maxSize: 1024 * 1024 * 1024,
sizeEnabled: false,
filesEnabled: false,
files: {min: 0, max: 0},
filesMax: 100,
}
if(this.props.state)
this.state = Object.assign(this.state, this.props.state)
}
setState(val)
{
super.setState(val, (v) => {
if(this.props.onChange)
this.props.onChange(this.state)
})
}
render() {
return (
<div className='column w100p' style={{maxWidth: 750, overflow: 'hidden', padding: '0px 18px 15px', background: 'white', borderRadius: 3}}>
<SelectField
floatingLabelText={__('Filter content type')}
value={this.state.type}
onChange={(event, index, value) => this.setState({type: value})}
>
<MenuItem value={undefined} primaryText="" />
<MenuItem value='video' primaryText={__('Video')} />
<MenuItem value='audio' primaryText={__('Audio')} />
<MenuItem value='pictures' primaryText={__('Pictures')} />
<MenuItem value='books' primaryText={__('Books')} />
<MenuItem value='application' primaryText={__('Applications')} />
<MenuItem value='archive' primaryText={__('Archives')} />
<MenuItem value='disc' primaryText={__('Disk Images')} />
</SelectField>
<div className='w100p'>
<InputSize value={this.state.size} enabled={this.state.sizeEnabled} maxSize={this.state.maxSize} onChange={({size, maxSize, enabled}) => this.setState({size, maxSize, sizeEnabled: enabled})} />
</div>
<div className='w100p'>
<FilesFilterInput value={this.state.files} filesMax={this.state.filesMax} enabled={this.state.filesEnabled} onChange={({files, filesMax, enabled}) => this.setState({files, filesMax, filesEnabled: enabled})} />
</div>
</div>
);
}
constructor(props)
{
super(props)
this.state = {
type: undefined,
size: {min: 0, max: 0},
maxSize: 1024 * 1024 * 1024,
sizeEnabled: false,
filesEnabled: false,
files: {min: 0, max: 0},
filesMax: 100,
}
if(this.props.state)
this.state = Object.assign(this.state, this.props.state)
}
setState(val)
{
super.setState(val, (v) => {
if(this.props.onChange)
this.props.onChange(this.state)
})
}
render() {
return (
<div className='column w100p' style={{maxWidth: 750, overflow: 'hidden', padding: '0px 18px 15px', background: 'white', borderRadius: 3}}>
<SelectField
floatingLabelText={__('Filter content type')}
value={this.state.type}
onChange={(event, index, value) => this.setState({type: value})}
>
<MenuItem value={undefined} primaryText="" />
<MenuItem value='video' primaryText={__('Video')} />
<MenuItem value='audio' primaryText={__('Audio')} />
<MenuItem value='pictures' primaryText={__('Pictures')} />
<MenuItem value='books' primaryText={__('Books')} />
<MenuItem value='application' primaryText={__('Applications')} />
<MenuItem value='archive' primaryText={__('Archives')} />
<MenuItem value='disc' primaryText={__('Disk Images')} />
</SelectField>
<div className='w100p'>
<InputSize value={this.state.size} enabled={this.state.sizeEnabled} maxSize={this.state.maxSize} onChange={({size, maxSize, enabled}) => this.setState({size, maxSize, sizeEnabled: enabled})} />
</div>
<div className='w100p'>
<FilesFilterInput value={this.state.files} filesMax={this.state.filesMax} enabled={this.state.filesEnabled} onChange={({files, filesMax, enabled}) => this.setState({files, filesMax, filesEnabled: enabled})} />
</div>
</div>
);
}
}

View File

@ -7,130 +7,130 @@ import Divider from 'material-ui/Divider';
import LinearProgress from 'material-ui/LinearProgress';
export default class SearchResults extends Component {
render() {
return (
<List style={{minWidth: '20em'}}>
{
(this.props.torrentsSearchResults && this.props.torrentsSearchResults.length > 0)
|| (this.props.filesSearchResults && this.props.filesSearchResults.length > 0)
?
<div>
<Subheader className='row center w100p' style={{paddingLeft: 0}} inset={true}><span>{__('Search results for')}</span> <span style={{marginLeft: '0.4em'}}><b>{this.props.searchText}</b></span></Subheader>
<div className='w100p row center' style={{marginTop: '-16px'}}>{this.props.resultSelector}</div>
</div>
:
null
}
{
this.props.torrentsSearchResults && this.props.torrentsSearchResults.length > 0
?
this.props.torrentsSearchResults.map((torrent, index) =>{
return(
<TorrentLine torrent={torrent} key={index} />
);
})
:
null
}
{
this.props.moreTorrentsEnabled && !this.props.moreTorrentsIndicator
?
<div>
<ListItem innerDivStyle={{textAlign: 'center', padding: '1em'}} primaryText={<span>{__('More Torrents')}</span>} onClick={() => {
if(this.props.onMoreTorrents)
this.props.onMoreTorrents();
}} />
<Divider />
</div>
:
null
}
{
this.props.moreTorrentsIndicator
?
<div style={{padding: '0.8em'}}>
<LinearProgress mode="indeterminate" />
</div>
:
null
}
{
this.props.filesSearchResults && this.props.filesSearchResults.length > 0
?
this.props.filesSearchResults.map((torrent, index) =>{
return(
<TorrentLine torrent={torrent} key={index} />
);
})
:
null
}
{
this.props.moreFilesEnabled && !this.props.moreFilesIndicator
?
<div>
<ListItem innerDivStyle={{textAlign: 'center', padding: '1em'}} primaryText={__('More Files')} onClick={() => {
if(this.props.onMoreFiles)
this.props.onMoreFiles();
}} />
<Divider />
</div>
:
null
}
{
this.props.moreFilesIndicator
?
<div style={{padding: '0.8em'}}>
<LinearProgress mode="indeterminate" />
</div>
:
null
}
{
this.props.torrentsSearchResults && this.props.torrentsSearchResults.length == 0
&& this.props.filesSearchResults && this.props.filesSearchResults.length == 0
&& !this.props.currentSearching
?
<div className='row inline center w100p pad0-75'>
<svg style={{fill: 'grey', height: '30px'}} viewBox="0 0 264.695 264.695">
<g>
<path d="M219.171,216.785c-4.762,0-10.322,2.3-16.672,6.881l-35.211-12.968l35.734-12.978
c6.003,3.888,11.558,5.833,16.682,5.833c5.639,0,9.347-2.917,11.117-8.733c0.351-1.235,0.527-2.57,0.527-3.981
c0-7.397-4.766-11.378-14.295-11.9c3.876-3.882,5.828-7.687,5.828-11.392c0-3.871-2.039-7.149-6.092-9.797
c-2.118-1.049-4.325-1.584-6.615-1.584c-7.769,0-13.064,6.258-15.887,18.797l-61.941,23.039l-61.94-22.504
c-2.823-12.885-8.125-19.332-15.885-19.332c-2.293,0-4.501,0.535-6.62,1.584c-3.876,2.647-5.82,5.926-5.82,9.797
c0,3.705,1.944,7.51,5.82,11.392c-9.701,0.522-14.555,4.503-14.555,11.901c0,1.41,0.179,2.746,0.526,3.98
c1.946,5.816,5.651,8.733,11.122,8.733c5.113,0,10.671-1.945,16.677-5.832l35.998,12.977l-35.476,12.698
c-6.175-4.406-11.637-6.611-16.402-6.611c-5.654,0-9.623,2.918-11.919,8.733c-0.348,1.235-0.526,2.553-0.526,3.975
c0,7.405,4.853,11.385,14.555,11.907c-3.876,3.883-5.82,7.688-5.82,11.393c0,3.869,1.944,7.134,5.82,9.797
c2.477,1.412,4.854,2.105,7.153,2.105c7.227,0,12.443-6.176,15.619-18.525l61.673-22.504l61.678,22.504
c3.178,12.35,8.475,18.525,15.882,18.525c2.121,0,4.407-0.693,6.884-2.105c4.052-2.663,6.092-5.928,6.092-9.797
c0-3.705-1.953-7.51-5.828-11.393c9.528-0.522,14.295-4.502,14.295-11.907c0-1.422-0.177-2.739-0.527-3.975
C228.702,219.702,224.82,216.785,219.171,216.785z"/>
<path d="M48.436,128.904c9.703,11.114,23.379,19.242,41.035,24.346v3.986c0,4.936,1.672,9.086,5.025,12.433
c3.35,3.358,7.498,5.211,12.441,5.563c5.116,0.357,8.905-0.528,11.378-2.646c3.879,2.817,8.204,4.229,12.974,4.229
c4.41,0,8.474-1.316,12.175-3.963c2.471,1.934,6.087,2.738,10.856,2.381c4.937-0.528,9.089-2.426,12.44-5.689
c3.35-3.281,5.025-7.371,5.025-12.307v-2.91c19.057-4.945,33.795-13.237,44.21-24.898c10.059-11.109,15.087-24.253,15.087-39.435
c0-3.359-0.355-6.886-1.063-10.597c-3.525-22.571-13.938-41.201-31.229-55.844C180.612,7.856,158.464,0,132.347,0
c-26.123,0-48.27,7.767-66.44,23.282C48.61,38.118,38.289,56.825,34.937,79.396c-0.709,3.711-1.064,7.238-1.064,10.597
C33.873,104.817,38.724,117.778,48.436,128.904L48.436,128.904z M152.865,60.749c5.206-6.085,11.514-9.13,18.922-9.13
c7.592,0,13.986,3.045,19.194,9.13c5.2,6.076,7.81,13.446,7.81,22.087c0,8.649-2.609,16.021-7.81,22.108
c-5.208,6.097-11.603,9.13-19.194,9.13c-7.408,0-13.716-3.033-18.922-9.13c-5.211-6.087-7.814-13.459-7.814-22.108
C145.05,74.195,147.654,66.825,152.865,60.749z M124.805,121.428c2.556-3.307,5.065-4.968,7.542-4.968
c2.47,0,4.802,1.831,7.012,5.509c2.205,3.662,3.317,7.145,3.317,10.469c0,5.062-3.361,7.581-10.067,7.581
c-4.414,0-7.677-1.136-9.792-3.396c-1.237-1.411-1.849-3.147-1.849-5.249C120.969,128.065,122.245,124.752,124.805,121.428z
M71.465,60.749c5.295-6.085,11.65-9.13,19.059-9.13c7.406,0,13.762,3.045,19.06,9.13c5.296,6.076,7.948,13.446,7.948,22.087
c0,8.649-2.651,16.021-7.948,22.108c-5.297,6.097-11.654,9.13-19.06,9.13c-7.409,0-13.764-3.033-19.059-9.13
c-5.292-6.087-7.944-13.459-7.944-22.108C63.521,74.195,66.173,66.825,71.465,60.749z"/>
</g>
</svg>
<div className='fs0-85 pad0-75' style={{color: 'grey'}}>{__('no torrents for')} <b>{this.props.searchText}</b> {__('were found')}</div>
</div>
:
null
}
</List>
);
}
render() {
return (
<List style={{minWidth: '20em'}}>
{
(this.props.torrentsSearchResults && this.props.torrentsSearchResults.length > 0)
|| (this.props.filesSearchResults && this.props.filesSearchResults.length > 0)
?
<div>
<Subheader className='row center w100p' style={{paddingLeft: 0}} inset={true}><span>{__('Search results for')}</span> <span style={{marginLeft: '0.4em'}}><b>{this.props.searchText}</b></span></Subheader>
<div className='w100p row center' style={{marginTop: '-16px'}}>{this.props.resultSelector}</div>
</div>
:
null
}
{
this.props.torrentsSearchResults && this.props.torrentsSearchResults.length > 0
?
this.props.torrentsSearchResults.map((torrent, index) =>{
return(
<TorrentLine torrent={torrent} key={index} />
);
})
:
null
}
{
this.props.moreTorrentsEnabled && !this.props.moreTorrentsIndicator
?
<div>
<ListItem innerDivStyle={{textAlign: 'center', padding: '1em'}} primaryText={<span>{__('More Torrents')}</span>} onClick={() => {
if(this.props.onMoreTorrents)
this.props.onMoreTorrents();
}} />
<Divider />
</div>
:
null
}
{
this.props.moreTorrentsIndicator
?
<div style={{padding: '0.8em'}}>
<LinearProgress mode="indeterminate" />
</div>
:
null
}
{
this.props.filesSearchResults && this.props.filesSearchResults.length > 0
?
this.props.filesSearchResults.map((torrent, index) =>{
return(
<TorrentLine torrent={torrent} key={index} />
);
})
:
null
}
{
this.props.moreFilesEnabled && !this.props.moreFilesIndicator
?
<div>
<ListItem innerDivStyle={{textAlign: 'center', padding: '1em'}} primaryText={__('More Files')} onClick={() => {
if(this.props.onMoreFiles)
this.props.onMoreFiles();
}} />
<Divider />
</div>
:
null
}
{
this.props.moreFilesIndicator
?
<div style={{padding: '0.8em'}}>
<LinearProgress mode="indeterminate" />
</div>
:
null
}
{
this.props.torrentsSearchResults && this.props.torrentsSearchResults.length == 0
&& this.props.filesSearchResults && this.props.filesSearchResults.length == 0
&& !this.props.currentSearching
?
<div className='row inline center w100p pad0-75'>
<svg style={{fill: 'grey', height: '30px'}} viewBox="0 0 264.695 264.695">
<g>
<path d="M219.171,216.785c-4.762,0-10.322,2.3-16.672,6.881l-35.211-12.968l35.734-12.978
c6.003,3.888,11.558,5.833,16.682,5.833c5.639,0,9.347-2.917,11.117-8.733c0.351-1.235,0.527-2.57,0.527-3.981
c0-7.397-4.766-11.378-14.295-11.9c3.876-3.882,5.828-7.687,5.828-11.392c0-3.871-2.039-7.149-6.092-9.797
c-2.118-1.049-4.325-1.584-6.615-1.584c-7.769,0-13.064,6.258-15.887,18.797l-61.941,23.039l-61.94-22.504
c-2.823-12.885-8.125-19.332-15.885-19.332c-2.293,0-4.501,0.535-6.62,1.584c-3.876,2.647-5.82,5.926-5.82,9.797
c0,3.705,1.944,7.51,5.82,11.392c-9.701,0.522-14.555,4.503-14.555,11.901c0,1.41,0.179,2.746,0.526,3.98
c1.946,5.816,5.651,8.733,11.122,8.733c5.113,0,10.671-1.945,16.677-5.832l35.998,12.977l-35.476,12.698
c-6.175-4.406-11.637-6.611-16.402-6.611c-5.654,0-9.623,2.918-11.919,8.733c-0.348,1.235-0.526,2.553-0.526,3.975
c0,7.405,4.853,11.385,14.555,11.907c-3.876,3.883-5.82,7.688-5.82,11.393c0,3.869,1.944,7.134,5.82,9.797
c2.477,1.412,4.854,2.105,7.153,2.105c7.227,0,12.443-6.176,15.619-18.525l61.673-22.504l61.678,22.504
c3.178,12.35,8.475,18.525,15.882,18.525c2.121,0,4.407-0.693,6.884-2.105c4.052-2.663,6.092-5.928,6.092-9.797
c0-3.705-1.953-7.51-5.828-11.393c9.528-0.522,14.295-4.502,14.295-11.907c0-1.422-0.177-2.739-0.527-3.975
C228.702,219.702,224.82,216.785,219.171,216.785z"/>
<path d="M48.436,128.904c9.703,11.114,23.379,19.242,41.035,24.346v3.986c0,4.936,1.672,9.086,5.025,12.433
c3.35,3.358,7.498,5.211,12.441,5.563c5.116,0.357,8.905-0.528,11.378-2.646c3.879,2.817,8.204,4.229,12.974,4.229
c4.41,0,8.474-1.316,12.175-3.963c2.471,1.934,6.087,2.738,10.856,2.381c4.937-0.528,9.089-2.426,12.44-5.689
c3.35-3.281,5.025-7.371,5.025-12.307v-2.91c19.057-4.945,33.795-13.237,44.21-24.898c10.059-11.109,15.087-24.253,15.087-39.435
c0-3.359-0.355-6.886-1.063-10.597c-3.525-22.571-13.938-41.201-31.229-55.844C180.612,7.856,158.464,0,132.347,0
c-26.123,0-48.27,7.767-66.44,23.282C48.61,38.118,38.289,56.825,34.937,79.396c-0.709,3.711-1.064,7.238-1.064,10.597
C33.873,104.817,38.724,117.778,48.436,128.904L48.436,128.904z M152.865,60.749c5.206-6.085,11.514-9.13,18.922-9.13
c7.592,0,13.986,3.045,19.194,9.13c5.2,6.076,7.81,13.446,7.81,22.087c0,8.649-2.609,16.021-7.81,22.108
c-5.208,6.097-11.603,9.13-19.194,9.13c-7.408,0-13.716-3.033-18.922-9.13c-5.211-6.087-7.814-13.459-7.814-22.108
C145.05,74.195,147.654,66.825,152.865,60.749z M124.805,121.428c2.556-3.307,5.065-4.968,7.542-4.968
c2.47,0,4.802,1.831,7.012,5.509c2.205,3.662,3.317,7.145,3.317,10.469c0,5.062-3.361,7.581-10.067,7.581
c-4.414,0-7.677-1.136-9.792-3.396c-1.237-1.411-1.849-3.147-1.849-5.249C120.969,128.065,122.245,124.752,124.805,121.428z
M71.465,60.749c5.295-6.085,11.65-9.13,19.059-9.13c7.406,0,13.762,3.045,19.06,9.13c5.296,6.076,7.948,13.446,7.948,22.087
c0,8.649-2.651,16.021-7.948,22.108c-5.297,6.097-11.654,9.13-19.06,9.13c-7.409,0-13.764-3.033-19.059-9.13
c-5.292-6.087-7.944-13.459-7.944-22.108C63.521,74.195,66.173,66.825,71.465,60.749z"/>
</g>
</svg>
<div className='fs0-85 pad0-75' style={{color: 'grey'}}>{__('no torrents for')} <b>{this.props.searchText}</b> {__('were found')}</div>
</div>
:
null
}
</List>
);
}
}

View File

@ -17,332 +17,332 @@ import _ from 'lodash'
import singleton from './singleton';
class Search extends Component {
constructor(props)
{
super(props)
this.onSearchUpdate = () => {}
constructor(props)
{
super(props)
this.onSearchUpdate = () => {}
this.state = {
searchingIndicator: false,
safeSearchText: __('safe search enabled'),
safeSearchColor: 'rgb(0, 188, 212)',
moreTorrentsIndicator: false,
moreFilesIndicator: false,
orderBy: null,
orderDesc: false,
advancedSearch: false,
}
this.searchLimit = 10
this.advanced = {}
this.searchError = undefined;
}
this.state = {
searchingIndicator: false,
safeSearchText: __('safe search enabled'),
safeSearchColor: 'rgb(0, 188, 212)',
moreTorrentsIndicator: false,
moreFilesIndicator: false,
orderBy: null,
orderDesc: false,
advancedSearch: false,
}
this.searchLimit = 10
this.advanced = {}
this.searchError = undefined;
}
search(oldSearch) {
window.router('/')
this.setState({
searchingIndicator: true
});
this.onSearchUpdate('indicator')
search(oldSearch) {
window.router('/')
this.setState({
searchingIndicator: true
});
this.onSearchUpdate('indicator')
this.searchTorrents = [];
this.moreSearchTorrents = true;
this.searchFiles = [];
this.moreSearchFiles = true;
this.currentSearch = this.searchValue;
let queries = 2;
let searchTorrentsParams = {
limit: this.searchLimit,
safeSearch: !this.notSafeSearch,
orderBy: this.state.orderBy,
orderDesc: this.state.orderDesc,
};
if(this.state.advancedSearch && this.advanced)
searchTorrentsParams = Object.assign(searchTorrentsParams, this.advanced);
this.searchTorrents = [];
this.moreSearchTorrents = true;
this.searchFiles = [];
this.moreSearchFiles = true;
this.currentSearch = this.searchValue;
let queries = 2;
let searchTorrentsParams = {
limit: this.searchLimit,
safeSearch: !this.notSafeSearch,
orderBy: this.state.orderBy,
orderDesc: this.state.orderDesc,
};
if(this.state.advancedSearch && this.advanced)
searchTorrentsParams = Object.assign(searchTorrentsParams, this.advanced);
window.torrentSocket.emit('searchTorrent', oldSearch ? this.currentSearch : this.searchValue, searchTorrentsParams, window.customLoader((torrents) => {
if(torrents) {
this.searchTorrents = torrents;
if(torrents.length != this.searchLimit)
this.moreSearchTorrents = false;
}
else
{
this.moreSearchTorrents = false;
}
if(--queries == 0) {
this.setState({
searchingIndicator: false
});
}
this.onSearchUpdate('torrents')
}));
let searchFilesParams = {
limit: this.searchLimit,
safeSearch: !this.notSafeSearch,
orderBy: this.state.orderBy,
orderDesc: this.state.orderDesc,
};
if(this.state.advancedSearch && this.advanced)
searchFilesParams = Object.assign(searchFilesParams, this.advanced);
window.torrentSocket.emit('searchTorrent', oldSearch ? this.currentSearch : this.searchValue, searchTorrentsParams, window.customLoader((torrents) => {
if(torrents) {
this.searchTorrents = torrents;
if(torrents.length != this.searchLimit)
this.moreSearchTorrents = false;
}
else
{
this.moreSearchTorrents = false;
}
if(--queries == 0) {
this.setState({
searchingIndicator: false
});
}
this.onSearchUpdate('torrents')
}));
let searchFilesParams = {
limit: this.searchLimit,
safeSearch: !this.notSafeSearch,
orderBy: this.state.orderBy,
orderDesc: this.state.orderDesc,
};
if(this.state.advancedSearch && this.advanced)
searchFilesParams = Object.assign(searchFilesParams, this.advanced);
window.torrentSocket.emit('searchFiles', oldSearch ? this.currentSearch : this.searchValue, searchFilesParams, window.customLoader((torrents) => {
if(torrents) {
this.searchFiles = torrents;
let files = 0;
torrents.forEach((torrent) => {
if(torrent.path && torrent.path.length > 0)
files += torrent.path.length
});
if(files != this.searchLimit)
this.moreSearchFiles = false;
}
else
{
this.moreSearchFiles = false;
}
if(--queries == 0) {
this.setState({
searchingIndicator: false
});
}
this.onSearchUpdate('files')
}));
}
moreTorrents() {
this.setState({moreTorrentsIndicator: true});
this.onSearchUpdate('indicator')
window.torrentSocket.emit('searchFiles', oldSearch ? this.currentSearch : this.searchValue, searchFilesParams, window.customLoader((torrents) => {
if(torrents) {
this.searchFiles = torrents;
let files = 0;
torrents.forEach((torrent) => {
if(torrent.path && torrent.path.length > 0)
files += torrent.path.length
});
if(files != this.searchLimit)
this.moreSearchFiles = false;
}
else
{
this.moreSearchFiles = false;
}
if(--queries == 0) {
this.setState({
searchingIndicator: false
});
}
this.onSearchUpdate('files')
}));
}
moreTorrents() {
this.setState({moreTorrentsIndicator: true});
this.onSearchUpdate('indicator')
window.torrentSocket.emit('searchTorrent', this.currentSearch, {
index: this.searchTorrents.length,
limit: this.searchLimit,
safeSearch: !this.notSafeSearch,
orderBy: this.state.orderBy,
orderDesc: this.state.orderDesc,
}, window.customLoader((torrents) => {
if(torrents) {
this.searchTorrents = this.searchTorrents.concat(torrents);
if(torrents.length != this.searchLimit)
this.moreSearchTorrents = false;
window.torrentSocket.emit('searchTorrent', this.currentSearch, {
index: this.searchTorrents.length,
limit: this.searchLimit,
safeSearch: !this.notSafeSearch,
orderBy: this.state.orderBy,
orderDesc: this.state.orderDesc,
}, window.customLoader((torrents) => {
if(torrents) {
this.searchTorrents = this.searchTorrents.concat(torrents);
if(torrents.length != this.searchLimit)
this.moreSearchTorrents = false;
this.setState({moreTorrentsIndicator: false});
this.onSearchUpdate('more torrents')
}
}));
}
calcTorrentsFiles(torrents)
{
let files = 0;
torrents.forEach((torrent) => {
if(torrent.path && torrent.path.length > 0)
files += torrent.path.length
});
return files
}
moreFiles() {
let index = 0;
this.searchFiles.forEach((torrent) => {
if(torrent.path && torrent.path.length > 0)
index += torrent.path.length;
});
this.setState({moreTorrentsIndicator: false});
this.onSearchUpdate('more torrents')
}
}));
}
calcTorrentsFiles(torrents)
{
let files = 0;
torrents.forEach((torrent) => {
if(torrent.path && torrent.path.length > 0)
files += torrent.path.length
});
return files
}
moreFiles() {
let index = 0;
this.searchFiles.forEach((torrent) => {
if(torrent.path && torrent.path.length > 0)
index += torrent.path.length;
});
this.setState({moreFilesIndicator: true});
this.onSearchUpdate('indicator')
this.setState({moreFilesIndicator: true});
this.onSearchUpdate('indicator')
window.torrentSocket.emit('searchFiles', this.currentSearch, {
index: index,
limit: this.searchLimit,
safeSearch: !this.notSafeSearch,
orderBy: this.state.orderBy,
orderDesc: this.state.orderDesc,
}, window.customLoader((torrents) => {
if(torrents) {
this.searchFiles = this.searchFiles.concat(torrents);
window.torrentSocket.emit('searchFiles', this.currentSearch, {
index: index,
limit: this.searchLimit,
safeSearch: !this.notSafeSearch,
orderBy: this.state.orderBy,
orderDesc: this.state.orderDesc,
}, window.customLoader((torrents) => {
if(torrents) {
this.searchFiles = this.searchFiles.concat(torrents);
if(this.calcTorrentsFiles(torrents) != this.searchLimit)
this.moreSearchFiles = false;
if(this.calcTorrentsFiles(torrents) != this.searchLimit)
this.moreSearchFiles = false;
this.mergeFiles()
this.mergeFiles()
this.setState({moreFilesIndicator: false});
this.onSearchUpdate('more files')
}
}));
}
mergeFiles()
{
for(let i = 0; i < this.searchFiles.length; i++)
{
for(let j = i + 1; j < this.searchFiles.length; j++)
{
if(this.searchFiles[i].hash != this.searchFiles[j].hash)
continue
this.setState({moreFilesIndicator: false});
this.onSearchUpdate('more files')
}
}));
}
mergeFiles()
{
for(let i = 0; i < this.searchFiles.length; i++)
{
for(let j = i + 1; j < this.searchFiles.length; j++)
{
if(this.searchFiles[i].hash != this.searchFiles[j].hash)
continue
if(!this.searchFiles[i].remove)
{
this.searchFiles[i].path = this.searchFiles[i].path.concat(this.searchFiles[j].path)
}
if(!this.searchFiles[i].remove)
{
this.searchFiles[i].path = this.searchFiles[i].path.concat(this.searchFiles[j].path)
}
this.searchFiles[j].remove = true
}
}
this.searchFiles[j].remove = true
}
}
this.searchFiles = this.searchFiles.filter(torrent => !torrent.remove)
}
this.searchFiles = this.searchFiles.filter(torrent => !torrent.remove)
}
componentDidMount() {
this.newStatisticFunc = (statistic) => {
if(statistic) {
this.stats = statistic;
this.forceUpdate();
}
};
window.torrentSocket.emit('statistic', window.customLoader(this.newStatisticFunc));
window.torrentSocket.on('newStatistic', this.newStatisticFunc);
componentDidMount() {
this.newStatisticFunc = (statistic) => {
if(statistic) {
this.stats = statistic;
this.forceUpdate();
}
};
window.torrentSocket.emit('statistic', window.customLoader(this.newStatisticFunc));
window.torrentSocket.on('newStatistic', this.newStatisticFunc);
this.remoteSearchTorrent = (torrents) => {
if(!torrents)
return
this.remoteSearchTorrent = (torrents) => {
if(!torrents)
return
if(torrents.length === this.searchLimit)
this.moreSearchTorrents = true;
if(torrents.length === this.searchLimit)
this.moreSearchTorrents = true;
this.searchTorrents = _.unionBy(this.searchTorrents, torrents, 'hash')
this.onSearchUpdate('remote torrents')
}
window.torrentSocket.on('remoteSearchTorrent', this.remoteSearchTorrent);
this.searchTorrents = _.unionBy(this.searchTorrents, torrents, 'hash')
this.onSearchUpdate('remote torrents')
}
window.torrentSocket.on('remoteSearchTorrent', this.remoteSearchTorrent);
this.remoteSearchFiles = (torrents) => {
if(!torrents)
return
this.remoteSearchFiles = (torrents) => {
if(!torrents)
return
if(torrents.length > 0 && this.calcTorrentsFiles(torrents) === this.searchLimit)
this.moreSearchFiles = true;
if(torrents.length > 0 && this.calcTorrentsFiles(torrents) === this.searchLimit)
this.moreSearchFiles = true;
this.searchFiles = _.unionBy(this.searchFiles, torrents, 'hash')
this.mergeFiles()
this.onSearchUpdate('remote files')
}
window.torrentSocket.on('remoteSearchFiles', this.remoteSearchFiles);
}
componentWillUnmount()
{
if(this.newStatisticFunc)
window.torrentSocket.off('newStatistic', this.newStatisticFunc);
this.searchFiles = _.unionBy(this.searchFiles, torrents, 'hash')
this.mergeFiles()
this.onSearchUpdate('remote files')
}
window.torrentSocket.on('remoteSearchFiles', this.remoteSearchFiles);
}
componentWillUnmount()
{
if(this.newStatisticFunc)
window.torrentSocket.off('newStatistic', this.newStatisticFunc);
if(this.remoteSearchTorrent)
window.torrentSocket.off('remoteSearchTorrent', this.remoteSearchTorrent);
if(this.remoteSearchTorrent)
window.torrentSocket.off('remoteSearchTorrent', this.remoteSearchTorrent);
if(this.remoteSearchFiles)
window.torrentSocket.off('remoteSearchFiles', this.remoteSearchFiles);
}
setSafeSearch(ch) {
this.notSafeSearch = ch;
if(ch)
{
return {safeSearchText: __('safe search disabled'), safeSearchColor: '#EC407A'}
}
else
{
return {safeSearchText: __('safe search enabled'), safeSearchColor: 'rgb(0, 188, 212)'}
}
}
render() {
const style = {
refresh: {
display: 'inline-block',
position: 'relative',
},
};
if(this.remoteSearchFiles)
window.torrentSocket.off('remoteSearchFiles', this.remoteSearchFiles);
}
setSafeSearch(ch) {
this.notSafeSearch = ch;
if(ch)
{
return {safeSearchText: __('safe search disabled'), safeSearchColor: '#EC407A'}
}
else
{
return {safeSearchText: __('safe search enabled'), safeSearchColor: 'rgb(0, 188, 212)'}
}
}
render() {
const style = {
refresh: {
display: 'inline-block',
position: 'relative',
},
};
return (
<div className="column w100p center">
<div className='row inline w100p pad0-75 search-row' style={{minWidth: '35em', backgroundColor: 'white', paddingTop: 0, paddingBottom: this.searchError ? 17 : 0, margin: 5, borderRadius: 3}}>
<TextField
style={{marginTop: -12}}
hintText={__('Search torrent or file')}
floatingLabelText={__('What to search?')}
fullWidth={true}
ref='searchInput'
defaultValue={this.searchValue}
errorText={this.searchError}
onKeyPress={(e) => {
if (e.key === 'Enter') {
this.search();
}
}}
onChange={e => {
this.searchValue = e.target.value
if(this.searchValue.length < 3 && this.searchValue.length > 0)
this.searchError = __('too short string for search');
else
this.searchError = undefined;
this.forceUpdate()
}}
/>
return (
<div className="column w100p center">
<div className='row inline w100p pad0-75 search-row' style={{minWidth: '35em', backgroundColor: 'white', paddingTop: 0, paddingBottom: this.searchError ? 17 : 0, margin: 5, borderRadius: 3}}>
<TextField
style={{marginTop: -12}}
hintText={__('Search torrent or file')}
floatingLabelText={__('What to search?')}
fullWidth={true}
ref='searchInput'
defaultValue={this.searchValue}
errorText={this.searchError}
onKeyPress={(e) => {
if (e.key === 'Enter') {
this.search();
}
}}
onChange={e => {
this.searchValue = e.target.value
if(this.searchValue.length < 3 && this.searchValue.length > 0)
this.searchError = __('too short string for search');
else
this.searchError = undefined;
this.forceUpdate()
}}
/>
<div style={{width: 25, height: 25, margin: 2}}>
<Checkbox
ref='safeSearch'
checked={this.notSafeSearch ? true : false}
checkedIcon={<Visibility />}
uncheckedIcon={<VisibilityOff />}
iconStyle={{fill: this.state.safeSearchColor}}
onCheck={(ev, ch) => {
this.setState(this.setSafeSearch(ch));
}}
style={{paddingBottom: '0.8em'}}
/>
</div>
<div style={{width: 25, height: 25, margin: 2}}>
<Checkbox
ref='advancedSearch'
checked={this.state.advancedSearch}
checkedIcon={<RemoveIcon />}
uncheckedIcon={<AddIcon />}
iconStyle={{fill: 'black'}}
onCheck={(ev, ch) => {
this.setState({advancedSearch: ch});
}}
style={{paddingBottom: '0.8em'}}
/>
</div>
<div style={{width: 25, height: 25, margin: 2}}>
<Checkbox
ref='safeSearch'
checked={this.notSafeSearch ? true : false}
checkedIcon={<Visibility />}
uncheckedIcon={<VisibilityOff />}
iconStyle={{fill: this.state.safeSearchColor}}
onCheck={(ev, ch) => {
this.setState(this.setSafeSearch(ch));
}}
style={{paddingBottom: '0.8em'}}
/>
</div>
<div style={{width: 25, height: 25, margin: 2}}>
<Checkbox
ref='advancedSearch'
checked={this.state.advancedSearch}
checkedIcon={<RemoveIcon />}
uncheckedIcon={<AddIcon />}
iconStyle={{fill: 'black'}}
onCheck={(ev, ch) => {
this.setState({advancedSearch: ch});
}}
style={{paddingBottom: '0.8em'}}
/>
</div>
<RaisedButton style={{marginLeft: '10px'}} label={__('Search')} primary={true} onClick={() =>{
this.search()
}} />
</div>
{
this.state.advancedSearch
<RaisedButton style={{marginLeft: '10px'}} label={__('Search')} primary={true} onClick={() =>{
this.search()
}} />
</div>
{
this.state.advancedSearch
&&
<AdvancedSearch onChange={(state) => {
this.advanced = state;
this.advanced = state;
}} state={this.advanced} />
}
{
this.stats
}
{
this.stats
&&
<TorrentsStatistic stats={this.stats} />
}
{
this.state.searchingIndicator
?
<div className='pad1'>
<RefreshIndicator
size={50}
left={0}
top={0}
loadingColor="#FF9800"
status="loading"
style={style.refresh}
/>
</div>
:
null
}
</div>
);
}
}
{
this.state.searchingIndicator
?
<div className='pad1'>
<RefreshIndicator
size={50}
left={0}
top={0}
loadingColor="#FF9800"
status="loading"
style={style.refresh}
/>
</div>
:
null
}
</div>
);
}
}
export default singleton(Search)

View File

@ -1,6 +1,6 @@
export default (Superclass) => {
let instance;
return class Singleton extends Superclass {
constructor(props) {
super(props)
@ -14,12 +14,12 @@ export default (Superclass) => {
if (instance) {
return instance;
} else {
return new Singleton();
return new Singleton();
}
}
static do(key, ...params) {
if ( typeof this.instance()[key] === 'function') {
return this.instance()[key](...params);
return this.instance()[key](...params);
} else {
return this.instance()[key];
}

View File

@ -12,171 +12,171 @@ import {Tabs, Tab} from 'material-ui/Tabs';
import _ from 'lodash'
export default class TopPage extends Page {
constructor(props) {
super(props)
this.setTitle('Rats On The Boat - Torrents top');
this.topTorrents = {};
this.types = ['main', 'video', 'audio', 'books', 'pictures', 'application', 'archive']
this.descriptions = {
main: __('All'),
video: __('Video'),
audio: __('Audio/Music'),
books: __('Books'),
pictures: __('Pictures/Images'),
application: __('Apps/Games'),
archive: __('Archives')
}
this.times = {
overall: __('Overall'),
hours: __('Last hour'),
week: __('Last week'),
month: __('Last month')
}
this.state = {type: 'main', time: 'overall'}
}
loadMoreTorrents(type, time)
{
time = time ? time : this.state.time
window.torrentSocket.emit('topTorrents',
type == 'main' ? null : type,
{index: (this.topTorrents[type] && this.topTorrents[type][time] && this.topTorrents[type][time].length) || 0, time},
window.customLoader((data) => {
if(!this.topTorrents[type])
this.topTorrents[type] = {}
if(!this.topTorrents[type][time])
this.topTorrents[type][time] = []
constructor(props) {
super(props)
this.setTitle('Rats On The Boat - Torrents top');
this.topTorrents = {};
this.types = ['main', 'video', 'audio', 'books', 'pictures', 'application', 'archive']
this.descriptions = {
main: __('All'),
video: __('Video'),
audio: __('Audio/Music'),
books: __('Books'),
pictures: __('Pictures/Images'),
application: __('Apps/Games'),
archive: __('Archives')
}
this.times = {
overall: __('Overall'),
hours: __('Last hour'),
week: __('Last week'),
month: __('Last month')
}
this.state = {type: 'main', time: 'overall'}
}
loadMoreTorrents(type, time)
{
time = time ? time : this.state.time
window.torrentSocket.emit('topTorrents',
type == 'main' ? null : type,
{index: (this.topTorrents[type] && this.topTorrents[type][time] && this.topTorrents[type][time].length) || 0, time},
window.customLoader((data) => {
if(!this.topTorrents[type])
this.topTorrents[type] = {}
if(!this.topTorrents[type][time])
this.topTorrents[type][time] = []
if(data && data.length > 0)
{
this.topTorrents[type][time] = this.topTorrents[type][time].concat(data);
this._update()
}
})
)
}
_update()
{
if(this.timeForce)
return
this.timeForce = setTimeout(() => {
delete this.timeForce
this.forceUpdate()
}, 550)
}
componentDidMount()
{
super.componentDidMount();
for(const type of this.types)
{
this.loadMoreTorrents(type)
}
this.remoteTopTorrents = ({torrents, type, time}) => {
if(!torrents)
return
if(data && data.length > 0)
{
this.topTorrents[type][time] = this.topTorrents[type][time].concat(data);
this._update()
}
})
)
}
_update()
{
if(this.timeForce)
return
this.timeForce = setTimeout(() => {
delete this.timeForce
this.forceUpdate()
}, 550)
}
componentDidMount()
{
super.componentDidMount();
for(const type of this.types)
{
this.loadMoreTorrents(type)
}
this.remoteTopTorrents = ({torrents, type, time}) => {
if(!torrents)
return
time = time ? time : 'overall'
type = type ? type : 'main'
this.topTorrents[type][time] = _.orderBy(_.unionBy(this.topTorrents[type][time], torrents, 'hash'), ['seeders'], ['desc'])
this._update();
}
window.torrentSocket.on('remoteTopTorrents', this.remoteTopTorrents);
}
componentWillUnmount()
{
if(this.remoteTopTorrents)
window.torrentSocket.off('remoteTopTorrents', this.remoteTopTorrents);
}
render() {
return (
<div>
<div className='column center w100p'>
{
Object.keys(this.topTorrents).length == 0
time = time ? time : 'overall'
type = type ? type : 'main'
this.topTorrents[type][time] = _.orderBy(_.unionBy(this.topTorrents[type][time], torrents, 'hash'), ['seeders'], ['desc'])
this._update();
}
window.torrentSocket.on('remoteTopTorrents', this.remoteTopTorrents);
}
componentWillUnmount()
{
if(this.remoteTopTorrents)
window.torrentSocket.off('remoteTopTorrents', this.remoteTopTorrents);
}
render() {
return (
<div>
<div className='column center w100p'>
{
Object.keys(this.topTorrents).length == 0
&&
<div className='pad0-75 w100p '>
<LinearProgress mode="indeterminate" />
<LinearProgress mode="indeterminate" />
</div>
}
<Tabs
className='w100p'
value={this.state.type}
onChange={(type) => {
this.setState({type});
// lost other content
if(!this.topTorrents[type][this.state.time])
{
this.loadMoreTorrents(type, this.state.time)
}
}}
tabItemContainerStyle={{flexWrap: 'wrap', alignItems: 'stretch'}}
inkBarStyle={{display: 'none'}}
>
{
this.types.map((type, index) => {
if(!this.topTorrents[type])
return null;
}
<Tabs
className='w100p'
value={this.state.type}
onChange={(type) => {
this.setState({type});
// lost other content
if(!this.topTorrents[type][this.state.time])
{
this.loadMoreTorrents(type, this.state.time)
}
}}
tabItemContainerStyle={{flexWrap: 'wrap', alignItems: 'stretch'}}
inkBarStyle={{display: 'none'}}
>
{
this.types.map((type, index) => {
if(!this.topTorrents[type])
return null;
return (
<Tab buttonStyle={type === this.state.type ? {fontWeight: 'bold'} : undefined} style={{minWidth: 150}} key={index} label={this.descriptions[type]} value={type}>
<Tabs
className='w100p'
value={this.state.time}
onChange={(time) => {
this.setState({time})
// lost other content
if(!this.topTorrents[type][time])
{
this.loadMoreTorrents(type, time)
}
}}
tabItemContainerStyle={{flexWrap: 'wrap', alignItems: 'stretch'}}
inkBarStyle={{display: 'none'}}
>
{
Object.keys(this.times).map((time, index) => {
const torrents = this.topTorrents[type][time];
return (
<Tab buttonStyle={type === this.state.type ? {fontWeight: 'bold'} : undefined} style={{minWidth: 150}} key={index} label={this.descriptions[type]} value={type}>
<Tabs
className='w100p'
value={this.state.time}
onChange={(time) => {
this.setState({time})
// lost other content
if(!this.topTorrents[type][time])
{
this.loadMoreTorrents(type, time)
}
}}
tabItemContainerStyle={{flexWrap: 'wrap', alignItems: 'stretch'}}
inkBarStyle={{display: 'none'}}
>
{
Object.keys(this.times).map((time, index) => {
const torrents = this.topTorrents[type][time];
if(!torrents)
return (
<Tab buttonStyle={time === this.state.time ? {fontWeight: 'bold'} : undefined} style={{minWidth: 150}} key={index} label={this.times[time]} value={time}>
<div className='pad0-75 w100p '>
<LinearProgress mode="indeterminate" />
</div>
</Tab>
)
if(!torrents)
return (
<Tab buttonStyle={time === this.state.time ? {fontWeight: 'bold'} : undefined} style={{minWidth: 150}} key={index} label={this.times[time]} value={time}>
<div className='pad0-75 w100p '>
<LinearProgress mode="indeterminate" />
</div>
</Tab>
)
return (
<Tab buttonStyle={time === this.state.time ? {fontWeight: 'bold'} : undefined} style={{minWidth: 150}} key={index} label={this.times[time]} value={time}>
<List style={{minWidth: '20em'}}>
{
torrents.map((torrent, index) => {
return <TorrentLine key={index} torrent={torrent} />
})
}
{
torrents.length > 0
return (
<Tab buttonStyle={time === this.state.time ? {fontWeight: 'bold'} : undefined} style={{minWidth: 150}} key={index} label={this.times[time]} value={time}>
<List style={{minWidth: '20em'}}>
{
torrents.map((torrent, index) => {
return <TorrentLine key={index} torrent={torrent} />
})
}
{
torrents.length > 0
&&
<div>
<ListItem innerDivStyle={{textAlign: 'center', padding: '1em'}} primaryText={<span>{__('More Torrents')}</span>} onClick={() => {
this.loadMoreTorrents(type)
}} />
<Divider />
<ListItem innerDivStyle={{textAlign: 'center', padding: '1em'}} primaryText={<span>{__('More Torrents')}</span>} onClick={() => {
this.loadMoreTorrents(type)
}} />
<Divider />
</div>
}
</List>
</Tab>)
})
}
}
</List>
</Tab>)
})
}
</Tabs>
</Tab>
)
</Tabs>
</Tab>
)
})
}
</Tabs>
</div>
</div>
);
}
})
}
</Tabs>
</div>
</div>
);
}
}

View File

@ -53,34 +53,34 @@ const treeToTorrentFiles = (tree) => {
arr.push(<ListItem
key={file}
primaryText={file}
secondaryText={formatBytes(tree[file].__sizeBT)}
nestedItems={treeToTorrentFiles(tree[file])}
primaryTogglesNestedList={true}
innerDivStyle={{wordBreak: 'break-word'}}
leftIcon={tree[file] && Object.keys(tree[file]).length > 1 ? <FileFolder /> : contentIcon(fileTypeDetect({path: file}))}
/>);
secondaryText={formatBytes(tree[file].__sizeBT)}
nestedItems={treeToTorrentFiles(tree[file])}
primaryTogglesNestedList={true}
innerDivStyle={{wordBreak: 'break-word'}}
leftIcon={tree[file] && Object.keys(tree[file]).length > 1 ? <FileFolder /> : contentIcon(fileTypeDetect({path: file}))}
/>);
}
return arr;
}
const TorrentFiles = (props) => {
let filesList = props.torrent.filesList;
let filesList = props.torrent.filesList;
let tree = buildFilesTree(filesList);
return (
<List className='w100p'>
{
filesList.length > 0
?
<div className='w100p'>
<Subheader inset={true}>{__('Content of the torrent')}:</Subheader>
{treeToTorrentFiles(tree)}
</div>
:
<div className='column center'>
<span className='pad0-75'>{__('Processing files')}...</span>
<LinearProgress mode="indeterminate" />
</div>
}
{
filesList.length > 0
?
<div className='w100p'>
<Subheader inset={true}>{__('Content of the torrent')}:</Subheader>
{treeToTorrentFiles(tree)}
</div>
:
<div className='column center'>
<span className='pad0-75'>{__('Processing files')}...</span>
<LinearProgress mode="indeterminate" />
</div>
}
</List>
);
};
@ -89,84 +89,84 @@ const TorrentInformation = (props) => {
let torrent = props.torrent;
return (
<List className='w100p'>
<Subheader inset={true}>{__('Information about torrent')}</Subheader>
<ListItem
//leftAvatar={<Avatar icon={<ActionAssignment />} backgroundColor={blue500} />}
rightIcon={<ActionInfo />}
primaryText={__('Torrent Name')}
secondaryText={<span className='break-word' style={{whiteSpace: 'normal'}}>{torrent.name}</span>}
/>
<ListItem
// leftAvatar={<Avatar icon={<EditorInsertChart />} backgroundColor={yellow600} />}
rightIcon={<ActionInfo />}
primaryText={__('Torrent Size')}
secondaryText={formatBytes(torrent.size)}
/>
<ListItem
// leftAvatar={<Avatar icon={<EditorInsertChart />} backgroundColor={yellow600} />}
rightIcon={<ActionInfo />}
primaryText={__('Torrent contains files')}
secondaryText={torrent.files}
onClick={() => {
if(!props.parent)
return
<Subheader inset={true}>{__('Information about torrent')}</Subheader>
<ListItem
//leftAvatar={<Avatar icon={<ActionAssignment />} backgroundColor={blue500} />}
rightIcon={<ActionInfo />}
primaryText={__('Torrent Name')}
secondaryText={<span className='break-word' style={{whiteSpace: 'normal'}}>{torrent.name}</span>}
/>
<ListItem
// leftAvatar={<Avatar icon={<EditorInsertChart />} backgroundColor={yellow600} />}
rightIcon={<ActionInfo />}
primaryText={__('Torrent Size')}
secondaryText={formatBytes(torrent.size)}
/>
<ListItem
// leftAvatar={<Avatar icon={<EditorInsertChart />} backgroundColor={yellow600} />}
rightIcon={<ActionInfo />}
primaryText={__('Torrent contains files')}
secondaryText={torrent.files}
onClick={() => {
if(!props.parent)
return
props.parent.setState({
value: 'files'
})
}}
/>
<ListItem
// leftAvatar={<Avatar icon={<EditorInsertChart />} backgroundColor={yellow600} />}
rightIcon={<ActionInfo />}
primaryText={__('Indexed/Added torrent date')}
secondaryText={moment(torrent.added * 1000).format('MMMM Do YYYY, hh:mm')}
/>
<ListItem
// leftAvatar={<Avatar icon={<EditorInsertChart />} backgroundColor={yellow600} />}
rightIcon={<ActionInfo />}
primaryText={__('Content type')}
secondaryText={torrent.contentType || 'unknown'}
/>
<ListItem
// leftAvatar={<Avatar icon={<EditorInsertChart />} backgroundColor={yellow600} />}
rightIcon={<ActionInfo />}
primaryText={__('Category')}
secondaryText={torrent.contentCategory || 'unknown'}
/>
</List>
props.parent.setState({
value: 'files'
})
}}
/>
<ListItem
// leftAvatar={<Avatar icon={<EditorInsertChart />} backgroundColor={yellow600} />}
rightIcon={<ActionInfo />}
primaryText={__('Indexed/Added torrent date')}
secondaryText={moment(torrent.added * 1000).format('MMMM Do YYYY, hh:mm')}
/>
<ListItem
// leftAvatar={<Avatar icon={<EditorInsertChart />} backgroundColor={yellow600} />}
rightIcon={<ActionInfo />}
primaryText={__('Content type')}
secondaryText={torrent.contentType || 'unknown'}
/>
<ListItem
// leftAvatar={<Avatar icon={<EditorInsertChart />} backgroundColor={yellow600} />}
rightIcon={<ActionInfo />}
primaryText={__('Category')}
secondaryText={torrent.contentCategory || 'unknown'}
/>
</List>
);
}
export default class TorrentPage extends Page {
constructor(props) {
super(props);
this.state = {
value: 'info',
searchingIndicator: false,
voting: false,
voted: false,
askDownloading: false,
downloading: false,
downloadProgress: {}
};
this.setTitle('Information about torrent');
}
constructor(props) {
super(props);
this.state = {
value: 'info',
searchingIndicator: false,
voting: false,
voted: false,
askDownloading: false,
downloading: false,
downloadProgress: {}
};
this.setTitle('Information about torrent');
}
changeTab(tab) {
if(this.state.value != tab) {
this.setState({
value: tab
});
console.log('change');
}
}
onSwipeRight() {
this.changeTab('files');
}
onSwipeLeft() {
this.changeTab('info');
}
changeTab(tab) {
if(this.state.value != tab) {
this.setState({
value: tab
});
console.log('change');
}
}
onSwipeRight() {
this.changeTab('files');
}
onSwipeLeft() {
this.changeTab('info');
}
handleChange = (value) => {
if(value == 'main') {
@ -174,18 +174,18 @@ export default class TorrentPage extends Page {
return;
}
this.setState({
value: value,
});
this.setState({
value: value,
});
};
getTorrentInfo() {
window.torrentSocket.emit('torrent', this.props.hash, {files: true, peer: this.props.peer}, window.customLoader((data) => {
if(data) {
this.torrent = data
this.setTitle(this.torrent.name + ' - Rats On The Boat');
if(this.torrent.contentCategory == 'xxx') {
this.setMetaTag('robots', 'noindex');
}
if(this.torrent.contentCategory == 'xxx') {
this.setMetaTag('robots', 'noindex');
}
//this.forceUpdate(); // вызывается через searchingIndicator
// Получаем более новую статистику пира
@ -194,195 +194,195 @@ export default class TorrentPage extends Page {
}
}
}, () => {
this.setState({
searchingIndicator: true
});
}, () => {
this.setState({
searchingIndicator: false
});
}));
this.setState({
searchingIndicator: true
});
}, () => {
this.setState({
searchingIndicator: false
});
}));
}
componentDidMount() {
super.componentDidMount();
this.filesUpdated = (hash) => {
if(this.props.hash != hash)
return;
return;
this.getTorrentInfo();
}
window.torrentSocket.on('filesReady', this.filesUpdated);
this.trackerUpdate = (info) => {
if(this.props.hash != info.hash)
return;
if(this.props.hash != info.hash)
return;
if(!this.torrent)
return;
if(!this.torrent)
return;
Object.assign(this.torrent, info);
this.forceUpdate();
}
window.torrentSocket.on('trackerTorrentUpdate', this.trackerUpdate);
Object.assign(this.torrent, info);
this.forceUpdate();
}
window.torrentSocket.on('trackerTorrentUpdate', this.trackerUpdate);
this.onVotes = async ({hash, good, bad, selfVote}) => {
if(this.props.hash != hash)
return;
this.onVotes = async ({hash, good, bad, selfVote}) => {
if(this.props.hash != hash)
return;
if(!this.torrent)
return
if(!this.torrent)
return
this.torrent.good = good;
this.torrent.bad = bad;
this.state.voted = selfVote;
this.forceUpdate();
}
window.torrentSocket.on('votes', this.onVotes);
this.torrent.good = good;
this.torrent.bad = bad;
this.state.voted = selfVote;
this.forceUpdate();
}
window.torrentSocket.on('votes', this.onVotes);
this.downloading = (hash) => {
if(this.props.hash != hash)
return;
this.downloading = (hash) => {
if(this.props.hash != hash)
return;
this.setState({downloading: true})
}
window.torrentSocket.on('downloading', this.downloading);
this.setState({downloading: true})
}
window.torrentSocket.on('downloading', this.downloading);
this.downloadDone = (hash, canceled) => {
if(this.props.hash != hash)
return;
this.downloadDone = (hash, canceled) => {
if(this.props.hash != hash)
return;
this.setState({
downloading: false,
askDownloading: !canceled
})
}
window.torrentSocket.on('downloadDone', this.downloadDone);
this.setState({
downloading: false,
askDownloading: !canceled
})
}
window.torrentSocket.on('downloadDone', this.downloadDone);
this.downloadProgress = (hash, progress) => {
if(this.props.hash != hash)
return;
this.downloadProgress = (hash, progress) => {
if(this.props.hash != hash)
return;
this.setState({downloadProgress: progress})
}
window.torrentSocket.on('downloadProgress', this.downloadProgress);
this.setState({downloadProgress: progress})
}
window.torrentSocket.on('downloadProgress', this.downloadProgress);
this.getTorrentInfo();
this.getTorrentInfo();
}
componentWillUnmount() {
if(this.filesUpdated)
window.torrentSocket.off('filesReady', this.filesUpdated);
window.torrentSocket.off('filesReady', this.filesUpdated);
if(this.trackerUpdate)
window.torrentSocket.off('trackerTorrentUpdate', this.trackerUpdate);
if(this.onVotes)
window.torrentSocket.off('votes', this.onVotes);
if(this.torrent && this.torrent.contentCategory == 'xxx') {
this.removeMetaTag('robots');
}
if(this.downloading)
window.torrentSocket.off('downloading', this.downloading);
if(this.downloadDone)
window.torrentSocket.off('downloadDone', this.downloadDone);
if(this.downloadProgress)
window.torrentSocket.off('downloadProgress', this.downloadProgress);
window.torrentSocket.off('trackerTorrentUpdate', this.trackerUpdate);
if(this.onVotes)
window.torrentSocket.off('votes', this.onVotes);
if(this.torrent && this.torrent.contentCategory == 'xxx') {
this.removeMetaTag('robots');
}
if(this.downloading)
window.torrentSocket.off('downloading', this.downloading);
if(this.downloadDone)
window.torrentSocket.off('downloadDone', this.downloadDone);
if(this.downloadProgress)
window.torrentSocket.off('downloadProgress', this.downloadProgress);
}
vote(good) {
if(!this.torrent)
return;
if(!this.torrent)
return;
this.setState({
voting: true
});
window.torrentSocket.emit('vote', this.torrent.hash, !!good, window.customLoader((success) => {
this.setState({
voted: true,
voting: false
});
}));
this.setState({
voting: true
});
window.torrentSocket.emit('vote', this.torrent.hash, !!good, window.customLoader((success) => {
this.setState({
voted: true,
voting: false
});
}));
}
render() {
const style = {
refresh: {
display: 'inline-block',
position: 'relative',
},
};
refresh: {
display: 'inline-block',
position: 'relative',
},
};
if(this.state.searchingIndicator) {
return (
<div className='pad1 w100p column center'>
<RefreshIndicator
size={50}
left={0}
top={0}
loadingColor="#FF9800"
status="loading"
style={style.refresh}
/>
</div>
);
}
if(this.state.searchingIndicator) {
return (
<div className='pad1 w100p column center'>
<RefreshIndicator
size={50}
left={0}
top={0}
loadingColor="#FF9800"
status="loading"
style={style.refresh}
/>
</div>
);
}
let torrentRating;
if(this.torrent) {
torrentRating = Math.round(rating(this.torrent.good, this.torrent.bad) * 100);
}
let torrentRating;
if(this.torrent) {
torrentRating = Math.round(rating(this.torrent.good, this.torrent.bad) * 100);
}
return (
<div className="w100p column center">
{
this.torrent
?
<Tabs
className='w100p'
value={this.state.value}
onChange={this.handleChange}
>
<Tab label={__('Back to main')} value="main" />
<Tab label={__('Information')} value="info" >
<div className='column w100p'>
<div className='row w100p torrent-information-row'>
<div className='info-table'>
<TorrentInformation torrent={this.torrent} parent={this} />
</div>
<div style={{flexBasis: '40%'}} className='column center w100p'>
<img src={NoImage} className='pad0-75' style={{height: '200px'}} />
<RaisedButton
href={`magnet:?xt=urn:btih:${this.torrent.hash}`}
target="_self"
label="Magnet"
secondary={true}
onClick={(e) => {
e.preventDefault();
window.open(`magnet:?xt=urn:btih:${this.torrent.hash}`, '_self')
}}
icon={<svg fill='white' viewBox="0 0 24 24"><path d="M17.374 20.235c2.444-2.981 6.626-8.157 6.626-8.157l-3.846-3.092s-2.857 3.523-6.571 8.097c-4.312 5.312-11.881-2.41-6.671-6.671 4.561-3.729 8.097-6.57 8.097-6.57l-3.092-3.842s-5.173 4.181-8.157 6.621c-2.662 2.175-3.76 4.749-3.76 7.24 0 5.254 4.867 10.139 10.121 10.139 2.487 0 5.064-1.095 7.253-3.765zm4.724-7.953l-1.699 2.111-1.74-1.397 1.701-2.114 1.738 1.4zm-10.386-10.385l1.4 1.738-2.113 1.701-1.397-1.74 2.11-1.699z"/></svg>}
/>
{
!this.state.askDownloading && !this.state.downloading
return (
<div className="w100p column center">
{
this.torrent
?
<Tabs
className='w100p'
value={this.state.value}
onChange={this.handleChange}
>
<Tab label={__('Back to main')} value="main" />
<Tab label={__('Information')} value="info" >
<div className='column w100p'>
<div className='row w100p torrent-information-row'>
<div className='info-table'>
<TorrentInformation torrent={this.torrent} parent={this} />
</div>
<div style={{flexBasis: '40%'}} className='column center w100p'>
<img src={NoImage} className='pad0-75' style={{height: '200px'}} />
<RaisedButton
href={`magnet:?xt=urn:btih:${this.torrent.hash}`}
target="_self"
label="Magnet"
secondary={true}
onClick={(e) => {
e.preventDefault();
window.open(`magnet:?xt=urn:btih:${this.torrent.hash}`, '_self')
}}
icon={<svg fill='white' viewBox="0 0 24 24"><path d="M17.374 20.235c2.444-2.981 6.626-8.157 6.626-8.157l-3.846-3.092s-2.857 3.523-6.571 8.097c-4.312 5.312-11.881-2.41-6.671-6.671 4.561-3.729 8.097-6.57 8.097-6.57l-3.092-3.842s-5.173 4.181-8.157 6.621c-2.662 2.175-3.76 4.749-3.76 7.24 0 5.254 4.867 10.139 10.121 10.139 2.487 0 5.064-1.095 7.253-3.765zm4.724-7.953l-1.699 2.111-1.74-1.397 1.701-2.114 1.738 1.4zm-10.386-10.385l1.4 1.738-2.113 1.701-1.397-1.74 2.11-1.699z"/></svg>}
/>
{
!this.state.askDownloading && !this.state.downloading
&&
<RaisedButton
href={`magnet:?xt=urn:btih:${this.torrent.hash}`}
target="_self"
label={__('Download')}
backgroundColor='#00C853'
labelColor='white'
style={{marginTop: 8}}
onClick={(e) => {
e.preventDefault();
this.setState({askDownloading: true})
window.torrentSocket.emit('download', this.torrent)
}}
icon={
<svg viewBox="0 0 56 56" fill='white'>
<g>
<path d="M35.586,41.586L31,46.172V28c0-1.104-0.896-2-2-2s-2,0.896-2,2v18.172l-4.586-4.586c-0.781-0.781-2.047-0.781-2.828,0
href={`magnet:?xt=urn:btih:${this.torrent.hash}`}
target="_self"
label={__('Download')}
backgroundColor='#00C853'
labelColor='white'
style={{marginTop: 8}}
onClick={(e) => {
e.preventDefault();
this.setState({askDownloading: true})
window.torrentSocket.emit('download', this.torrent)
}}
icon={
<svg viewBox="0 0 56 56" fill='white'>
<g>
<path d="M35.586,41.586L31,46.172V28c0-1.104-0.896-2-2-2s-2,0.896-2,2v18.172l-4.586-4.586c-0.781-0.781-2.047-0.781-2.828,0
s-0.781,2.047,0,2.828l7.999,7.999c0.093,0.094,0.196,0.177,0.307,0.251c0.047,0.032,0.099,0.053,0.148,0.081
c0.065,0.036,0.127,0.075,0.196,0.103c0.065,0.027,0.133,0.042,0.2,0.062c0.058,0.017,0.113,0.04,0.173,0.051
C28.738,52.986,28.869,53,29,53s0.262-0.014,0.392-0.04c0.06-0.012,0.115-0.034,0.173-0.051c0.067-0.02,0.135-0.035,0.2-0.062
c0.069-0.028,0.131-0.067,0.196-0.103c0.05-0.027,0.101-0.049,0.148-0.081c0.11-0.074,0.213-0.157,0.307-0.251l7.999-7.999
c0.781-0.781,0.781-2.047,0-2.828S36.367,40.805,35.586,41.586z"/>
<path d="M47.835,18.986c-0.137-0.019-2.457-0.335-4.684,0.002C43.1,18.996,43.049,19,42.999,19c-0.486,0-0.912-0.354-0.987-0.85
<path d="M47.835,18.986c-0.137-0.019-2.457-0.335-4.684,0.002C43.1,18.996,43.049,19,42.999,19c-0.486,0-0.912-0.354-0.987-0.85
c-0.083-0.546,0.292-1.056,0.838-1.139c1.531-0.233,3.062-0.196,4.083-0.124C46.262,9.135,39.83,3,32.085,3
C27.388,3,22.667,5.379,19.8,9.129C21.754,10.781,23,13.246,23,16c0,0.553-0.447,1-1,1s-1-0.447-1-1
c0-2.462-1.281-4.627-3.209-5.876c-0.227-0.147-0.462-0.277-0.702-0.396c-0.069-0.034-0.139-0.069-0.21-0.101
@ -391,120 +391,120 @@ export default class TorrentPage extends Page {
l0.012,0.21l-0.009,0.16C7.008,16.744,7,16.873,7,17v0.63l-0.567,0.271C2.705,19.688,0,24,0,28.154C0,34.135,4.865,39,10.845,39H25
V28c0-2.209,1.791-4,4-4s4,1.791,4,4v11h2.353c0.059,0,0.116-0.005,0.174-0.009l0.198-0.011l0.271,0.011
C36.053,38.995,36.11,39,36.169,39h9.803C51.501,39,56,34.501,56,28.972C56,24.161,52.49,19.872,47.835,18.986z"/>
</g>
</svg>
}
</g>
</svg>
}
/>
}
{
this.state.downloading
}
{
this.state.downloading
&&
<div className='column center pad0-75' style={{width: '300px'}}>
<div className='fs0-75' style={{color: 'rgb(0, 188, 212)'}}>{__('downloading')} {this.state.downloadProgress && (this.state.downloadProgress.progress * 100).toFixed(1)}%</div>
<LinearProgress
style={{marginTop: 3}}
mode="determinate"
value={this.state.downloadProgress && (this.state.downloadProgress.progress ? this.state.downloadProgress.progress : 0) * 100}
/>
<FlatButton
onClick={() => {
window.torrentSocket.emit('downloadCancel', this.torrent.hash)
}}
label={__('Cancel download')}
secondary={true}
icon={<svg fill='rgb(255, 64, 129)' viewBox="0 0 18 18"><path d="M9 1C4.58 1 1 4.58 1 9s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zm4 10.87L11.87 13 9 10.13 6.13 13 5 11.87 7.87 9 5 6.13 6.13 5 9 7.87 11.87 5 13 6.13 10.13 9 13 11.87z"/></svg>}
/>
<div className='fs0-75' style={{color: 'rgb(0, 188, 212)'}}>{__('downloading')} {this.state.downloadProgress && (this.state.downloadProgress.progress * 100).toFixed(1)}%</div>
<LinearProgress
style={{marginTop: 3}}
mode="determinate"
value={this.state.downloadProgress && (this.state.downloadProgress.progress ? this.state.downloadProgress.progress : 0) * 100}
/>
<FlatButton
onClick={() => {
window.torrentSocket.emit('downloadCancel', this.torrent.hash)
}}
label={__('Cancel download')}
secondary={true}
icon={<svg fill='rgb(255, 64, 129)' viewBox="0 0 18 18"><path d="M9 1C4.58 1 1 4.58 1 9s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zm4 10.87L11.87 13 9 10.13 6.13 13 5 11.87 7.87 9 5 6.13 6.13 5 9 7.87 11.87 5 13 6.13 10.13 9 13 11.87z"/></svg>}
/>
</div>
}
<div className='fs0-75 pad0-75 center column' style={{color: 'rgba(0, 0, 0, 0.541176)'}}><div>BTIH:</div><div>{this.torrent.hash}</div></div>
{
this.torrent.seeders || this.torrent.leechers || this.torrent.completed
?
<div className='fs0-85 pad0-75 center column'>
<div className='pad0-25' style={{color: '#00C853'}}>{__('seeders')}: {this.torrent.seeders}</div>
<div className='pad0-25' style={{color: '#AA00FF'}}>{__('leechers')}: {this.torrent.leechers}</div>
<div className='pad0-25' style={{color: '#FF6D00'}}>{__('completed')}: {this.torrent.completed}</div>
</div>
:
null
}
{
!this.state.voted && !this.state.voting
?
<div className='row pad0-25'>
<RaisedButton
label={__('Good') + ` (${this.torrent.good})`}
labelColor="white"
backgroundColor="#00C853"
icon={
<svg viewBox="0 0 489.543 489.543" fill="white">
<g>
<path d="M270.024,0c-22.6,0-15,48.3-15,48.3s-48.3,133.2-94.5,168.7c-9.9,10.4-16.1,21.9-20,31.3l0,0l0,0
}
<div className='fs0-75 pad0-75 center column' style={{color: 'rgba(0, 0, 0, 0.541176)'}}><div>BTIH:</div><div>{this.torrent.hash}</div></div>
{
this.torrent.seeders || this.torrent.leechers || this.torrent.completed
?
<div className='fs0-85 pad0-75 center column'>
<div className='pad0-25' style={{color: '#00C853'}}>{__('seeders')}: {this.torrent.seeders}</div>
<div className='pad0-25' style={{color: '#AA00FF'}}>{__('leechers')}: {this.torrent.leechers}</div>
<div className='pad0-25' style={{color: '#FF6D00'}}>{__('completed')}: {this.torrent.completed}</div>
</div>
:
null
}
{
!this.state.voted && !this.state.voting
?
<div className='row pad0-25'>
<RaisedButton
label={__('Good') + ` (${this.torrent.good})`}
labelColor="white"
backgroundColor="#00C853"
icon={
<svg viewBox="0 0 489.543 489.543" fill="white">
<g>
<path d="M270.024,0c-22.6,0-15,48.3-15,48.3s-48.3,133.2-94.5,168.7c-9.9,10.4-16.1,21.9-20,31.3l0,0l0,0
c-0.9,2.3-1.7,4.5-2.4,6.5c-3.1,6.3-9.7,16-23.8,24.5l46.2,200.9c0,0,71.5,9.3,143.2,7.8c28.7,2.3,59.1,2.5,83.3-2.7
c82.2-17.5,61.6-74.8,61.6-74.8c44.3-33.3,19.1-74.9,19.1-74.9c39.4-41.1,0.7-75.6,0.7-75.6s21.3-33.2-6.2-58.3
c-34.3-31.4-127.4-10.5-127.4-10.5l0,0c-6.5,1.1-13.4,2.5-20.8,4.3c0,0-32.2,15,0-82.7C346.324,15.1,292.624,0,270.024,0z"/>
<path d="M127.324,465.7l-35-166.3c-2-9.5-11.6-17.3-21.3-17.3h-66.8l-0.1,200.8h109.1C123.024,483,129.324,475.2,127.324,465.7z"
/>
</g>
</svg>
}
onClick={() => this.vote(true)}
/>
<RaisedButton
style={{marginLeft: '9px'}}
label={__('Bad') + ` (${this.torrent.bad})`}
labelColor="white"
backgroundColor="#D50000"
icon={
<svg viewBox="0 0 487.643 487.643" fill="white">
<g>
<path d="M113.869,209.443l46-200.1c0,0,71.2-9.3,142.6-7.8c28.5-2.3,58.9-2.5,83,2.7c81.9,17.4,61.4,74.5,61.4,74.5
<path d="M127.324,465.7l-35-166.3c-2-9.5-11.6-17.3-21.3-17.3h-66.8l-0.1,200.8h109.1C123.024,483,129.324,475.2,127.324,465.7z"
/>
</g>
</svg>
}
onClick={() => this.vote(true)}
/>
<RaisedButton
style={{marginLeft: '9px'}}
label={__('Bad') + ` (${this.torrent.bad})`}
labelColor="white"
backgroundColor="#D50000"
icon={
<svg viewBox="0 0 487.643 487.643" fill="white">
<g>
<path d="M113.869,209.443l46-200.1c0,0,71.2-9.3,142.6-7.8c28.5-2.3,58.9-2.5,83,2.7c81.9,17.4,61.4,74.5,61.4,74.5
c44.2,33.2,19,74.6,19,74.6c39.2,41,0.7,75.3,0.7,75.3s21.2,33-6.1,58c-34.2,31.2-126.9,10.5-126.9,10.5l0,0
c-6.4-1.1-13.3-2.5-20.7-4.2c0,0-32.1-15,0,82.4s-21.4,112.3-43.9,112.3s-15-48.1-15-48.1s-48.1-132.7-94.1-168
c-9.9-10.4-16.1-21.8-19.9-31.2l0,0l0,0c-0.9-2.3-1.7-4.5-2.4-6.5C134.469,227.543,127.869,217.843,113.869,209.443z
M70.869,206.643c9.7,0,19.2-7.7,21.2-17.2l34.8-165.6c2-9.5-4.3-17.2-14-17.2H4.169l0.1,200H70.869z"/>
</g>
</svg>
}
onClick={() => this.vote(false)}
/>
</div>
:
this.state.voting
?
<div>voting...</div>
:
<div>Thank you for voting!</div>
}
{
this.torrent.good > 0 || this.torrent.bad > 0
?
<div className='w100p' style={{padding: '7px 35px', marginTop: '10px'}}>
<LinearProgress
mode="determinate"
value={torrentRating}
color={torrentRating >= 50 ? '#00E676' : '#FF3D00'}
style={{
height: '5px',
}}
/>
<div className='row center pad0-75 fs0-85' style={{color: torrentRating >= 50 ? '#00E676' : '#FF3D00'}}>{__('Torrent rating')}: {torrentRating}%</div>
</div>
:
null
}
</div>
</div>
</div>
</Tab>
<Tab label={__('Files')} value="files" >
<TorrentFiles torrent={this.torrent} />
</Tab>
</Tabs>
:
null
}
</div>
);
</g>
</svg>
}
onClick={() => this.vote(false)}
/>
</div>
:
this.state.voting
?
<div>voting...</div>
:
<div>Thank you for voting!</div>
}
{
this.torrent.good > 0 || this.torrent.bad > 0
?
<div className='w100p' style={{padding: '7px 35px', marginTop: '10px'}}>
<LinearProgress
mode="determinate"
value={torrentRating}
color={torrentRating >= 50 ? '#00E676' : '#FF3D00'}
style={{
height: '5px',
}}
/>
<div className='row center pad0-75 fs0-85' style={{color: torrentRating >= 50 ? '#00E676' : '#FF3D00'}}>{__('Torrent rating')}: {torrentRating}%</div>
</div>
:
null
}
</div>
</div>
</div>
</Tab>
<Tab label={__('Files')} value="files" >
<TorrentFiles torrent={this.torrent} />
</Tab>
</Tabs>
:
null
}
</div>
);
}
}

View File

@ -2,37 +2,37 @@ import React, { Component } from 'react';
import formatBytes from './format-bytes'
export default class TorrentsStatistic extends Component {
constructor(props)
{
super(props)
constructor(props)
{
super(props)
this.stats = props.stats || {}
}
componentDidMount()
{
this.newTorrentFunc = (torrent) => {
this.stats.size += torrent.size;
this.stats.torrents++;
this.stats.files += torrent.files;
this.forceUpdate()
}
this.stats = props.stats || {}
}
componentDidMount()
{
this.newTorrentFunc = (torrent) => {
this.stats.size += torrent.size;
this.stats.torrents++;
this.stats.files += torrent.files;
this.forceUpdate()
}
window.torrentSocket.on('newTorrent', this.newTorrentFunc);
}
componentWillUnmount()
{
if(this.newTorrentFunc)
window.torrentSocket.off('newTorrent', this.newTorrentFunc);
}
render()
{
return (
<div style={{position: 'relative', width: '100%', height: 0}}>
<div className='column w100p counter-statistic' style={{backgroundColor: 'rgba(0,0,0,0.7)', padding: 8, borderRadius: 4, marginTop: 2}}>
<div className='row w100p' style={{backgroundColor: 'rgba(0,0,0,0.7)', padding: 8, borderRadius: 4}}>
<div className='row inline' style={{color: '#e5f442', fontSize: '1.15em', fill: '#e5f442'}}>
<svg viewBox="0 0 60 60">
<path d="M35,0C23.849,0,14.43,2.588,11.215,6.475C4.669,8.077,0.884,10.775,0.146,13.51C0.062,13.657,0,13.818,0,14v0.5V26v0.5V27
window.torrentSocket.on('newTorrent', this.newTorrentFunc);
}
componentWillUnmount()
{
if(this.newTorrentFunc)
window.torrentSocket.off('newTorrent', this.newTorrentFunc);
}
render()
{
return (
<div style={{position: 'relative', width: '100%', height: 0}}>
<div className='column w100p counter-statistic' style={{backgroundColor: 'rgba(0,0,0,0.7)', padding: 8, borderRadius: 4, marginTop: 2}}>
<div className='row w100p' style={{backgroundColor: 'rgba(0,0,0,0.7)', padding: 8, borderRadius: 4}}>
<div className='row inline' style={{color: '#e5f442', fontSize: '1.15em', fill: '#e5f442'}}>
<svg viewBox="0 0 60 60">
<path d="M35,0C23.849,0,14.43,2.588,11.215,6.475C4.669,8.077,0.884,10.775,0.146,13.51C0.062,13.657,0,13.818,0,14v0.5V26v0.5V27
v11v0.5V39v12c0,0.162,0.043,0.315,0.117,0.451C1.298,56.346,11.864,60,25,60c11.24,0,20.579-2.68,23.786-6.518
c6.359-1.546,10.366-4.076,11.09-7C59.955,46.34,60,46.175,60,46V34v-0.5V22v-0.5v-12C60,4.895,51.238,0,35,0z M47.805,39.348
c-0.04,0.099-0.089,0.198-0.143,0.297c-0.067,0.123-0.142,0.246-0.231,0.369c-0.066,0.093-0.141,0.185-0.219,0.277
@ -128,115 +128,115 @@ export default class TorrentsStatistic extends Component {
c-0.317,1.941-3.314,3.891-7.972,5.255C49.999,51.063,50,51.031,50,51v-9.828c0.043-0.012,0.083-0.025,0.126-0.037
c0.4-0.112,0.79-0.227,1.168-0.346c0.004-0.001,0.009-0.003,0.013-0.004c2.961-0.936,5.22-2.099,6.693-3.427v8.346
C57.986,45.747,57.976,45.792,57.968,45.838z"/>
</svg>
<div style={{marginLeft: '5px'}}>{ formatBytes(this.stats.size, 1) } {__('data')}</div>
</div>
</svg>
<div style={{marginLeft: '5px'}}>{ formatBytes(this.stats.size, 1) } {__('data')}</div>
</div>
<div className='row inline' style={{color: '#f48641', fontSize: '1.15em', fill: '#f48641', marginLeft: '20px'}}>
<svg viewBox="0 0 60 60">
<g>
<path d="M60,8.311c0-0.199-0.052-0.382-0.131-0.551c-0.027-0.209-0.112-0.412-0.254-0.579L53.46,0H6.54L0.384,7.182
<div className='row inline' style={{color: '#f48641', fontSize: '1.15em', fill: '#f48641', marginLeft: '20px'}}>
<svg viewBox="0 0 60 60">
<g>
<path d="M60,8.311c0-0.199-0.052-0.382-0.131-0.551c-0.027-0.209-0.112-0.412-0.254-0.579L53.46,0H6.54L0.384,7.182
C0.242,7.348,0.157,7.55,0.131,7.76C0.052,7.929,0,8.112,0,8.311V19h3v41h54V19h3V8.311z M7.46,2h45.08l4.286,5H3.174L7.46,2z
M55,58H5V19h50V58z M58,17h-1H3H2V9h56V17z"/>
<path d="M42,23H18v10h24V23z M40,31H20v-6h20V31z"/>
<path d="M45,38H15v14h30V38z M43,50H17V40h26V50z"/>
<path d="M22,48h5c0.552,0,1-0.447,1-1s-0.448-1-1-1h-5c-0.552,0-1,0.447-1,1S21.448,48,22,48z"/>
<path d="M27,44h11c0.552,0,1-0.447,1-1s-0.448-1-1-1H27c-0.552,0-1,0.447-1,1S26.448,44,27,44z"/>
<path d="M22,44c0.26,0,0.52-0.11,0.71-0.29C22.89,43.52,23,43.26,23,43c0-0.261-0.11-0.521-0.29-0.71c-0.38-0.37-1.04-0.37-1.42,0
<path d="M42,23H18v10h24V23z M40,31H20v-6h20V31z"/>
<path d="M45,38H15v14h30V38z M43,50H17V40h26V50z"/>
<path d="M22,48h5c0.552,0,1-0.447,1-1s-0.448-1-1-1h-5c-0.552,0-1,0.447-1,1S21.448,48,22,48z"/>
<path d="M27,44h11c0.552,0,1-0.447,1-1s-0.448-1-1-1H27c-0.552,0-1,0.447-1,1S26.448,44,27,44z"/>
<path d="M22,44c0.26,0,0.52-0.11,0.71-0.29C22.89,43.52,23,43.26,23,43c0-0.261-0.11-0.521-0.29-0.71c-0.38-0.37-1.04-0.37-1.42,0
C21.11,42.479,21,42.739,21,43c0,0.27,0.11,0.52,0.29,0.71C21.48,43.89,21.73,44,22,44z"/>
<path d="M31.29,46.29C31.11,46.479,31,46.739,31,47c0,0.26,0.11,0.52,0.29,0.71C31.48,47.89,31.74,48,32,48
<path d="M31.29,46.29C31.11,46.479,31,46.739,31,47c0,0.26,0.11,0.52,0.29,0.71C31.48,47.89,31.74,48,32,48
c0.26,0,0.52-0.11,0.71-0.29C32.89,47.52,33,47.26,33,47c0-0.261-0.11-0.521-0.29-0.71C32.34,45.92,31.66,45.92,31.29,46.29z"/>
<path d="M38,46h-1c-0.552,0-1,0.447-1,1s0.448,1,1,1h1c0.552,0,1-0.447,1-1S38.552,46,38,46z"/>
</g>
</svg>
<div style={{marginLeft: '5px'}}>{this.stats.torrents} {__('torrents')}</div>
</div>
<path d="M38,46h-1c-0.552,0-1,0.447-1,1s0.448,1,1,1h1c0.552,0,1-0.447,1-1S38.552,46,38,46z"/>
</g>
</svg>
<div style={{marginLeft: '5px'}}>{this.stats.torrents} {__('torrents')}</div>
</div>
<div className='row inline' style={{color: '#f441e2', fontSize: '1.15em', fill: '#f441e2', marginLeft: '20px'}}>
<svg viewBox="0 0 60 60">
<g>
<path d="M42.5,22h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S43.052,22,42.5,22z"/>
<path d="M17.5,16h10c0.552,0,1-0.447,1-1s-0.448-1-1-1h-10c-0.552,0-1,0.447-1,1S16.948,16,17.5,16z"/>
<path d="M42.5,30h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S43.052,30,42.5,30z"/>
<path d="M42.5,38h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S43.052,38,42.5,38z"/>
<path d="M42.5,46h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S43.052,46,42.5,46z"/>
<path d="M38.914,0H6.5v60h47V14.586L38.914,0z M39.5,3.414L50.086,14H39.5V3.414z M8.5,58V2h29v14h14v42H8.5z"/>
</g>
</svg>
<div style={{marginLeft: '5px'}}>{this.stats.files} {__('files')}</div>
</div>
</div>
<div className='row inline' style={{color: '#f441e2', fontSize: '1.15em', fill: '#f441e2', marginLeft: '20px'}}>
<svg viewBox="0 0 60 60">
<g>
<path d="M42.5,22h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S43.052,22,42.5,22z"/>
<path d="M17.5,16h10c0.552,0,1-0.447,1-1s-0.448-1-1-1h-10c-0.552,0-1,0.447-1,1S16.948,16,17.5,16z"/>
<path d="M42.5,30h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S43.052,30,42.5,30z"/>
<path d="M42.5,38h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S43.052,38,42.5,38z"/>
<path d="M42.5,46h-25c-0.552,0-1,0.447-1,1s0.448,1,1,1h25c0.552,0,1-0.447,1-1S43.052,46,42.5,46z"/>
<path d="M38.914,0H6.5v60h47V14.586L38.914,0z M39.5,3.414L50.086,14H39.5V3.414z M8.5,58V2h29v14h14v42H8.5z"/>
</g>
</svg>
<div style={{marginLeft: '5px'}}>{this.stats.files} {__('files')}</div>
</div>
</div>
<div className='row w100p' style={{backgroundColor: 'rgba(0,0,0,0.7)', padding: 8, borderRadius: 4, marginTop: 5}}>
<div className='row inline' style={{color: window.peers > 0 ? '#19c632' : 'white', fontSize: '1.15em', fill: window.peers > 0 ? '#19c632' : 'white'}}>
<svg viewBox="0 0 47 47">
<g>
<path d="M17.567,15.938l-2.859-2.702c0.333-0.605,0.539-1.29,0.539-2.029c0-2.342-1.897-4.239-4.24-4.239
<div className='row w100p' style={{backgroundColor: 'rgba(0,0,0,0.7)', padding: 8, borderRadius: 4, marginTop: 5}}>
<div className='row inline' style={{color: window.peers > 0 ? '#19c632' : 'white', fontSize: '1.15em', fill: window.peers > 0 ? '#19c632' : 'white'}}>
<svg viewBox="0 0 47 47">
<g>
<path d="M17.567,15.938l-2.859-2.702c0.333-0.605,0.539-1.29,0.539-2.029c0-2.342-1.897-4.239-4.24-4.239
c-2.343,0-4.243,1.896-4.243,4.239c0,2.343,1.9,4.241,4.243,4.241c0.826,0,1.59-0.246,2.242-0.654l2.855,2.699
C16.536,16.922,17.023,16.399,17.567,15.938z"/>
<path d="M29.66,15.6l3.799-6.393c0.374,0.107,0.762,0.184,1.169,0.184c2.347,0,4.244-1.898,4.244-4.241
<path d="M29.66,15.6l3.799-6.393c0.374,0.107,0.762,0.184,1.169,0.184c2.347,0,4.244-1.898,4.244-4.241
c0-2.342-1.897-4.239-4.244-4.239c-2.343,0-4.239,1.896-4.239,4.239c0,1.163,0.469,2.214,1.227,2.981l-3.787,6.375
C28.48,14.801,29.094,15.169,29.66,15.6z"/>
<path d="M42.762,20.952c-1.824,0-3.369,1.159-3.968,2.775l-5.278-0.521c0,0.04,0.006,0.078,0.006,0.117
<path d="M42.762,20.952c-1.824,0-3.369,1.159-3.968,2.775l-5.278-0.521c0,0.04,0.006,0.078,0.006,0.117
c0,0.688-0.076,1.36-0.213,2.009l5.276,0.521c0.319,2.024,2.062,3.576,4.177,3.576c2.342,0,4.238-1.896,4.238-4.238
C47,22.85,45.104,20.952,42.762,20.952z"/>
<path d="M28.197,37.624l-1.18-5.156c-0.666,0.232-1.359,0.398-2.082,0.481l1.182,5.157c-1.355,0.709-2.29,2.11-2.29,3.746
<path d="M28.197,37.624l-1.18-5.156c-0.666,0.232-1.359,0.398-2.082,0.481l1.182,5.157c-1.355,0.709-2.29,2.11-2.29,3.746
c0,2.342,1.896,4.237,4.243,4.237c2.342,0,4.238-1.896,4.238-4.237C32.311,39.553,30.479,37.692,28.197,37.624z"/>
<path d="M14.357,25.37l-6.57,2.201c-0.758-1.158-2.063-1.926-3.548-1.926C1.896,25.645,0,27.542,0,29.884
<path d="M14.357,25.37l-6.57,2.201c-0.758-1.158-2.063-1.926-3.548-1.926C1.896,25.645,0,27.542,0,29.884
c0,2.345,1.896,4.242,4.239,4.242c2.341,0,4.242-1.897,4.242-4.242c0-0.098-0.021-0.188-0.029-0.284l6.591-2.207
C14.746,26.752,14.51,26.077,14.357,25.37z"/>
<circle cx="23.83" cy="23.323" r="7.271"/>
</g>
</svg>
<div style={{marginLeft: '5px'}}>{window.peers} {__('peers')}</div>
</div>
<circle cx="23.83" cy="23.323" r="7.271"/>
</g>
</svg>
<div style={{marginLeft: '5px'}}>{window.peers} {__('peers')}</div>
</div>
<div className='row inline' style={{color: window.peersTorrents > 0 ? '#19c687' : 'white', fontSize: '1.15em', fill: window.peersTorrents > 0 ? '#19c687' : 'white', marginLeft: '20px'}}>
<svg viewBox="0 0 481.6 481.6">
<g>
<path d="M381.6,309.4c-27.7,0-52.4,13.2-68.2,33.6l-132.3-73.9c3.1-8.9,4.8-18.5,4.8-28.4c0-10-1.7-19.5-4.9-28.5l132.2-73.8
<div className='row inline' style={{color: window.peersTorrents > 0 ? '#19c687' : 'white', fontSize: '1.15em', fill: window.peersTorrents > 0 ? '#19c687' : 'white', marginLeft: '20px'}}>
<svg viewBox="0 0 481.6 481.6">
<g>
<path d="M381.6,309.4c-27.7,0-52.4,13.2-68.2,33.6l-132.3-73.9c3.1-8.9,4.8-18.5,4.8-28.4c0-10-1.7-19.5-4.9-28.5l132.2-73.8
c15.7,20.5,40.5,33.8,68.3,33.8c47.4,0,86.1-38.6,86.1-86.1S429,0,381.5,0s-86.1,38.6-86.1,86.1c0,10,1.7,19.6,4.9,28.5
l-132.1,73.8c-15.7-20.6-40.5-33.8-68.3-33.8c-47.4,0-86.1,38.6-86.1,86.1s38.7,86.1,86.2,86.1c27.8,0,52.6-13.3,68.4-33.9
l132.2,73.9c-3.2,9-5,18.7-5,28.7c0,47.4,38.6,86.1,86.1,86.1s86.1-38.6,86.1-86.1S429.1,309.4,381.6,309.4z M381.6,27.1
c32.6,0,59.1,26.5,59.1,59.1s-26.5,59.1-59.1,59.1s-59.1-26.5-59.1-59.1S349.1,27.1,381.6,27.1z M100,299.8
c-32.6,0-59.1-26.5-59.1-59.1s26.5-59.1,59.1-59.1s59.1,26.5,59.1,59.1S132.5,299.8,100,299.8z M381.6,454.5
c-32.6,0-59.1-26.5-59.1-59.1c0-32.6,26.5-59.1,59.1-59.1s59.1,26.5,59.1,59.1C440.7,428,414.2,454.5,381.6,454.5z"/>
</g>
</svg>
<div style={{marginLeft: '5px'}}>{window.peersTorrents} {__('remote torrents')}</div>
</div>
</g>
</svg>
<div style={{marginLeft: '5px'}}>{window.peersTorrents} {__('remote torrents')}</div>
</div>
<div className='row inline' style={{color: window.p2pStatus == 2 ? '#78c619' : (window.p2pStatus == 1 ? '#c68319' : '#c6194a'), fontSize: '1.15em', fill: window.p2pStatus == 2 ? '#78c619' : (window.p2pStatus == 1 ? '#c68319' : '#c6194a'), marginLeft: '20px'}}>
{
window.p2pStatus == 0
<div className='row inline' style={{color: window.p2pStatus == 2 ? '#78c619' : (window.p2pStatus == 1 ? '#c68319' : '#c6194a'), fontSize: '1.15em', fill: window.p2pStatus == 2 ? '#78c619' : (window.p2pStatus == 1 ? '#c68319' : '#c6194a'), marginLeft: '20px'}}>
{
window.p2pStatus == 0
&&
<div className='row inline w100p'>
<svg viewBox="0 0 54.908 54.908">
<g>
<path d="M54.615,19.123c-7.243-7.244-16.89-11.233-27.161-11.233S7.537,11.878,0.293,19.123c-0.391,0.391-0.391,1.023,0,1.414
<svg viewBox="0 0 54.908 54.908">
<g>
<path d="M54.615,19.123c-7.243-7.244-16.89-11.233-27.161-11.233S7.537,11.878,0.293,19.123c-0.391,0.391-0.391,1.023,0,1.414
s1.023,0.391,1.414,0C8.573,13.67,17.717,9.889,27.454,9.889s18.881,3.781,25.747,10.647c0.195,0.195,0.451,0.293,0.707,0.293
s0.512-0.098,0.707-0.293C55.006,20.146,55.006,19.513,54.615,19.123z"/>
<path d="M6.171,25c-0.391,0.391-0.391,1.023,0,1.414c0.195,0.195,0.451,0.293,0.707,0.293s0.512-0.098,0.707-0.293
<path d="M6.171,25c-0.391,0.391-0.391,1.023,0,1.414c0.195,0.195,0.451,0.293,0.707,0.293s0.512-0.098,0.707-0.293
c10.955-10.956,28.781-10.956,39.737,0c0.391,0.391,1.023,0.391,1.414,0s0.391-1.023,0-1.414C37.002,13.266,17.907,13.264,6.171,25
z"/>
<path d="M27.454,24.508c-5.825,0-11.295,2.263-15.404,6.371c-0.391,0.391-0.391,1.023,0,1.414s1.023,0.391,1.414,0
<path d="M27.454,24.508c-5.825,0-11.295,2.263-15.404,6.371c-0.391,0.391-0.391,1.023,0,1.414s1.023,0.391,1.414,0
c3.731-3.73,8.699-5.785,13.99-5.785c5.291,0,10.259,2.055,13.99,5.785c0.195,0.195,0.451,0.293,0.707,0.293
s0.512-0.098,0.707-0.293c0.391-0.391,0.391-1.023,0-1.414C38.75,26.771,33.279,24.508,27.454,24.508z"/>
<path d="M27.454,33.916c-3.612,0-6.551,2.939-6.551,6.552s2.939,6.552,6.551,6.552c3.613,0,6.552-2.939,6.552-6.552
<path d="M27.454,33.916c-3.612,0-6.551,2.939-6.551,6.552s2.939,6.552,6.551,6.552c3.613,0,6.552-2.939,6.552-6.552
S31.067,33.916,27.454,33.916z M27.454,45.019c-2.51,0-4.551-2.042-4.551-4.552s2.042-4.552,4.551-4.552s4.552,2.042,4.552,4.552
S29.964,45.019,27.454,45.019z"/>
</g>
</svg>
<div style={{marginLeft: '5px'}}>{__('not available')}</div>
</g>
</svg>
<div style={{marginLeft: '5px'}}>{__('not available')}</div>
</div>
}
{
window.p2pStatus == 1
}
{
window.p2pStatus == 1
&&
<div className='row inline w100p'>
<svg viewBox="0 0 611.989 611.988">
<g>
<path d="M305.994,417.769c-30.85,0-55.887,25.037-55.887,55.887s25.038,55.887,55.887,55.887s55.887-25.037,55.887-55.887
<svg viewBox="0 0 611.989 611.988">
<g>
<path d="M305.994,417.769c-30.85,0-55.887,25.037-55.887,55.887s25.038,55.887,55.887,55.887s55.887-25.037,55.887-55.887
S336.843,417.769,305.994,417.769z M605.436,222.369C530.697,133.434,421.549,82.446,305.994,82.446
S81.309,133.434,6.551,222.369c-9.93,11.811-8.402,29.434,3.428,39.363c5.234,4.396,11.587,6.558,17.939,6.558
c7.973,0,15.891-3.391,21.423-9.967c64.084-76.248,157.639-119.989,256.652-119.989c99.013,0,192.568,43.741,256.651,119.971
@ -249,18 +249,18 @@ export default class TorrentsStatistic extends Component {
c7.973,0,15.891-3.39,21.405-9.966c21.368-25.429,52.552-40.016,85.544-40.016s64.177,14.587,85.544,40.016
c5.533,6.595,13.45,9.966,21.405,9.966c6.353,0,12.724-2.142,17.958-6.557c11.83-9.93,13.357-27.553,3.428-39.363
C402.324,327.846,355.546,305.994,305.994,305.994z"/>
</g>
</svg>
<div style={{marginLeft: '5px'}}>{__('redirect')}</div>
</g>
</svg>
<div style={{marginLeft: '5px'}}>{__('redirect')}</div>
</div>
}
{
window.p2pStatus == 2
}
{
window.p2pStatus == 2
&&
<div className='row inline w100p'>
<svg viewBox="0 0 611.989 611.988">
<g>
<path d="M305.994,417.769c-30.85,0-55.887,25.037-55.887,55.887s25.038,55.887,55.887,55.887s55.887-25.037,55.887-55.887
<svg viewBox="0 0 611.989 611.988">
<g>
<path d="M305.994,417.769c-30.85,0-55.887,25.037-55.887,55.887s25.038,55.887,55.887,55.887s55.887-25.037,55.887-55.887
S336.843,417.769,305.994,417.769z M605.436,222.369C530.697,133.434,421.549,82.446,305.994,82.446
S81.309,133.434,6.551,222.369c-9.93,11.811-8.402,29.434,3.428,39.363c5.234,4.396,11.587,6.558,17.939,6.558
c7.973,0,15.891-3.391,21.423-9.967c64.084-76.248,157.639-119.989,256.652-119.989c99.013,0,192.568,43.741,256.651,119.971
@ -273,16 +273,16 @@ export default class TorrentsStatistic extends Component {
c7.973,0,15.891-3.39,21.405-9.966c21.368-25.429,52.552-40.016,85.544-40.016s64.177,14.587,85.544,40.016
c5.533,6.595,13.45,9.966,21.405,9.966c6.353,0,12.724-2.142,17.958-6.557c11.83-9.93,13.357-27.553,3.428-39.363
C402.324,327.846,355.546,305.994,305.994,305.994z"/>
</g>
</svg>
<div style={{marginLeft: '5px'}}>{__('direct')}</div>
</g>
</svg>
<div style={{marginLeft: '5px'}}>{__('direct')}</div>
</div>
}
</div>
</div>
}
</div>
</div>
</div>
</div>
)
}
</div>
</div>
)
}
}

View File

@ -11,12 +11,12 @@ import LinearProgress from 'material-ui/LinearProgress';
let rating = require('./rating');
const contentIcon = (type, category, fill = 'grey') => {
if(category == 'xxx')
{
return (
<svg viewBox="0 0 18.282 18.282" fill={fill}>
<g>
<path d="M16.435,3.832H1.847C0.827,3.832,0,4.659,0,5.678v6.925c0,1.021,0.827,1.848,1.847,1.848h14.588
if(category == 'xxx')
{
return (
<svg viewBox="0 0 18.282 18.282" fill={fill}>
<g>
<path d="M16.435,3.832H1.847C0.827,3.832,0,4.659,0,5.678v6.925c0,1.021,0.827,1.848,1.847,1.848h14.588
c1.021,0,1.847-0.827,1.847-1.848V5.678C18.282,4.659,17.455,3.832,16.435,3.832z M3.194,7.123H2.583v4.042h0.611v0.54H1.876V6.583
h1.318V7.123z M6.197,10.986l-0.392-0.784C5.644,9.9,5.541,9.676,5.419,9.425H5.406c-0.09,0.251-0.199,0.476-0.334,0.777
l-0.36,0.784H3.593l1.254-2.191L3.638,6.654h1.125l0.379,0.791C5.27,7.709,5.367,7.921,5.47,8.165h0.013
@ -27,87 +27,87 @@ const contentIcon = (type, category, fill = 'grey') => {
c-0.16-0.302-0.263-0.526-0.386-0.777h-0.012c-0.091,0.251-0.2,0.476-0.335,0.777l-0.36,0.784h-1.117l1.253-2.191l-1.209-2.141
h1.125l0.379,0.791c0.129,0.264,0.226,0.476,0.328,0.72h0.013c0.104-0.276,0.187-0.47,0.296-0.72l0.366-0.791h1.118l-1.221,2.114
l1.286,2.218C15.071,10.986,13.94,10.986,13.94,10.986z M16.756,11.705h-1.311v-0.54h0.611V7.116h-0.611v-0.54h1.311V11.705z"/>
</g>
</svg>
)
}
</g>
</svg>
)
}
switch(type)
{
case 'video':
return (
<svg viewBox="0 0 491.858 491.858" fill={fill}>
<path d="M357.714,423.331c0,9.328-10.676,16.891-23.847,16.891H23.847C10.676,440.222,0,432.659,0,423.331V203.735
switch(type)
{
case 'video':
return (
<svg viewBox="0 0 491.858 491.858" fill={fill}>
<path d="M357.714,423.331c0,9.328-10.676,16.891-23.847,16.891H23.847C10.676,440.222,0,432.659,0,423.331V203.735
c0-9.33,10.676-16.892,23.847-16.892h310.02c13.171,0,23.847,7.564,23.847,16.892V423.331L357.714,423.331z"/>
<circle cx="89.428" cy="118.706" r="59.619"/>
<circle cx="253.381" cy="103.801" r="74.524"/>
<path d="M491.858,447.677c0,0-1.986,14.904-15.899,14.904c-13.912,0-103.34-83.42-103.34-94.397V258.882
<circle cx="89.428" cy="118.706" r="59.619"/>
<circle cx="253.381" cy="103.801" r="74.524"/>
<path d="M491.858,447.677c0,0-1.986,14.904-15.899,14.904c-13.912,0-103.34-83.42-103.34-94.397V258.882
c0-10.976,87.443-94.398,103.34-94.398c15.899,0,15.899,14.905,15.899,14.905V447.677z"/>
</svg>
)
case 'audio':
return (
<svg viewBox="0 0 46 46" fill={fill}>
<path d="M28.38,0c-0.551,0-1.097,0.153-1.579,0.444c-0.046,0.027-0.09,0.059-0.13,0.093L13.121,12H2.487c-0.553,0-1,0.447-1,1v19
</svg>
)
case 'audio':
return (
<svg viewBox="0 0 46 46" fill={fill}>
<path d="M28.38,0c-0.551,0-1.097,0.153-1.579,0.444c-0.046,0.027-0.09,0.059-0.13,0.093L13.121,12H2.487c-0.553,0-1,0.447-1,1v19
c0,0.553,0.447,1,1,1h10.61L26.64,45.436c0.05,0.046,0.104,0.086,0.161,0.12C27.284,45.847,27.83,46,28.38,46
c1.713,0,3.106-1.416,3.106-3.156V3.156C31.487,1.416,30.093,0,28.38,0z M14.487,31c0,0.553-0.447,1-1,1s-1-0.447-1-1v-4
c0-0.553,0.447-1,1-1s1,0.447,1,1V31z M14.487,18c0,0.553-0.447,1-1,1s-1-0.447-1-1v-4c0-0.553,0.447-1,1-1s1,0.447,1,1V18z"/>
<path d="M44.513,22.5c0-5.972-4.009-11.302-9.749-12.962c-0.533-0.151-1.084,0.152-1.238,0.684
<path d="M44.513,22.5c0-5.972-4.009-11.302-9.749-12.962c-0.533-0.151-1.084,0.152-1.238,0.684
c-0.153,0.53,0.152,1.085,0.684,1.238c4.889,1.413,8.304,5.953,8.304,11.04s-3.415,9.627-8.304,11.04
c-0.531,0.153-0.837,0.708-0.684,1.238c0.127,0.438,0.526,0.723,0.961,0.723c0.092,0,0.185-0.013,0.277-0.039
C40.504,33.802,44.513,28.472,44.513,22.5z"/>
</svg>
</svg>
)
case 'pictures':
return (
<svg viewBox="0 0 58 58" fill={fill}>
<path d="M57,6H1C0.448,6,0,6.447,0,7v44c0,0.553,0.448,1,1,1h56c0.552,0,1-0.447,1-1V7C58,6.447,57.552,6,57,6z M16,17
)
case 'pictures':
return (
<svg viewBox="0 0 58 58" fill={fill}>
<path d="M57,6H1C0.448,6,0,6.447,0,7v44c0,0.553,0.448,1,1,1h56c0.552,0,1-0.447,1-1V7C58,6.447,57.552,6,57,6z M16,17
c3.071,0,5.569,2.498,5.569,5.569c0,3.07-2.498,5.568-5.569,5.568s-5.569-2.498-5.569-5.568C10.431,19.498,12.929,17,16,17z
M52.737,35.676c-0.373,0.406-1.006,0.435-1.413,0.062L40.063,25.414l-9.181,10.054l4.807,4.807c0.391,0.391,0.391,1.023,0,1.414
s-1.023,0.391-1.414,0L23.974,31.389L7.661,45.751C7.471,45.918,7.235,46,7,46c-0.277,0-0.553-0.114-0.751-0.339
c-0.365-0.415-0.325-1.047,0.09-1.412l17.017-14.982c0.396-0.348,0.994-0.329,1.368,0.044l4.743,4.743l9.794-10.727
c0.179-0.196,0.429-0.313,0.694-0.325c0.264-0.006,0.524,0.083,0.72,0.262l12,11C53.083,34.636,53.11,35.269,52.737,35.676z"/>
</svg>
)
case 'application':
return (
<svg viewBox="0 0 483.85 483.85" fill={fill}>
<path d="M471.325,211.856l-56.9-56.9c-23.4-23.4-9.1-48.1,16.4-49.6c42-2.6,65.6-47.4,31.3-84.7c-37.3-34.2-81.9-10.7-84.5,31.2
</svg>
)
case 'application':
return (
<svg viewBox="0 0 483.85 483.85" fill={fill}>
<path d="M471.325,211.856l-56.9-56.9c-23.4-23.4-9.1-48.1,16.4-49.6c42-2.6,65.6-47.4,31.3-84.7c-37.3-34.2-81.9-10.7-84.5,31.2
c-1.6,25.5-26.5,39.9-49.8,16.6l-55.7-55.7c-16.7-16.7-43.8-16.7-60.5,0l-56.4,56.4c-23.4,23.4-48.2,8.9-49.8-16.6
c-2.6-42-47.6-65.9-84.9-31.6c-34.4,37.4-10.5,82.4,31.5,85c25.5,1.6,40,26.5,16.7,49.9l-56.2,56.1c-16.7,16.7-16.7,43.8,0,60.5
l55.7,55.7c23.4,23.3,9.5,47.6-16,49.2c-42,2.6-65.5,47.3-31.2,84.6c37.3,34.3,81.8,10.9,84.4-31.1c1.6-25.5,26-39.5,49.4-16.2
l56.2,56.2c17,17,44.8,17,61.8,0.1l39.4-39.4l16.9-16.9c22.1-23.1,7.8-47.4-17.4-49c-42-2.6-65.8-47.6-31.5-84.9
c37.3-34.3,82.3-10.4,84.9,31.6c1.6,25.2,25.8,39.4,48.9,17.3l15.3-15.3l41.2-41.2c0.1-0.1,0.1-0.1,0.2-0.2l0.6-0.6
C488.025,255.656,488.025,228.556,471.325,211.856z"/>
</svg>
)
case 'books':
return (
<svg viewBox="0 0 296.999 296.999" fill={fill}>
<g>
<path d="M45.432,35.049c-0.008,0-0.017,0-0.025,0c-2.809,0-5.451,1.095-7.446,3.085c-2.017,2.012-3.128,4.691-3.128,7.543
</svg>
)
case 'books':
return (
<svg viewBox="0 0 296.999 296.999" fill={fill}>
<g>
<path d="M45.432,35.049c-0.008,0-0.017,0-0.025,0c-2.809,0-5.451,1.095-7.446,3.085c-2.017,2.012-3.128,4.691-3.128,7.543
v159.365c0,5.844,4.773,10.61,10.641,10.625c24.738,0.059,66.184,5.215,94.776,35.136V84.023c0-1.981-0.506-3.842-1.461-5.382
C115.322,40.849,70.226,35.107,45.432,35.049z"/>
<path d="M262.167,205.042V45.676c0-2.852-1.111-5.531-3.128-7.543c-1.995-1.99-4.639-3.085-7.445-3.085c-0.009,0-0.018,0-0.026,0
<path d="M262.167,205.042V45.676c0-2.852-1.111-5.531-3.128-7.543c-1.995-1.99-4.639-3.085-7.445-3.085c-0.009,0-0.018,0-0.026,0
c-24.793,0.059-69.889,5.801-93.357,43.593c-0.955,1.54-1.46,3.401-1.46,5.382v166.779
c28.592-29.921,70.038-35.077,94.776-35.136C257.394,215.651,262.167,210.885,262.167,205.042z"/>
<path d="M286.373,71.801h-7.706v133.241c0,14.921-12.157,27.088-27.101,27.125c-20.983,0.05-55.581,4.153-80.084,27.344
<path d="M286.373,71.801h-7.706v133.241c0,14.921-12.157,27.088-27.101,27.125c-20.983,0.05-55.581,4.153-80.084,27.344
c42.378-10.376,87.052-3.631,112.512,2.171c3.179,0.724,6.464-0.024,9.011-2.054c2.538-2.025,3.994-5.052,3.994-8.301V82.427
C297,76.568,292.232,71.801,286.373,71.801z"/>
<path d="M18.332,205.042V71.801h-7.706C4.768,71.801,0,76.568,0,82.427v168.897c0,3.25,1.456,6.276,3.994,8.301
<path d="M18.332,205.042V71.801h-7.706C4.768,71.801,0,76.568,0,82.427v168.897c0,3.25,1.456,6.276,3.994,8.301
c2.545,2.029,5.827,2.78,9.011,2.054c25.46-5.803,70.135-12.547,112.511-2.171c-24.502-23.19-59.1-27.292-80.083-27.342
C30.49,232.13,18.332,219.963,18.332,205.042z"/>
</g>
</svg>
)
case 'archive':
return (
<svg viewBox="0 0 390 390" fill={fill}>
<g>
<path d="M182.681,205.334c0,5.21,4.227,9.436,9.436,9.436h5.765c5.21,0,9.436-4.226,9.436-9.436c0-5.211-4.226-9.438-9.436-9.438
</g>
</svg>
)
case 'archive':
return (
<svg viewBox="0 0 390 390" fill={fill}>
<g>
<path d="M182.681,205.334c0,5.21,4.227,9.436,9.436,9.436h5.765c5.21,0,9.436-4.226,9.436-9.436c0-5.211-4.226-9.438-9.436-9.438
h-5.765C186.908,195.897,182.681,200.123,182.681,205.334z"/>
<path d="M383.889,126.058c-4.478-5.191-10.868-8.314-17.674-8.686V64.899c0-25.562-20.797-46.359-46.361-46.359h-75.278
<path d="M383.889,126.058c-4.478-5.191-10.868-8.314-17.674-8.686V64.899c0-25.562-20.797-46.359-46.361-46.359h-75.278
c-25.052,0-38.351,10.578-48.062,18.303c-7.807,6.208-12.518,9.955-22.626,9.955H65.099c-22.78,0-41.313,18.062-41.313,40.264
v30.311c-6.806,0.371-13.196,3.494-17.674,8.686c-4.78,5.541-6.912,12.888-5.839,20.125l30.194,203.803
c1.828,12.338,12.417,21.475,24.89,21.475h279.286c12.473,0,23.063-9.137,24.891-21.475l30.195-203.802
@ -123,115 +123,115 @@ const contentIcon = (type, category, fill = 'grey') => {
h-32.443c-2.399,0-4.345-1.944-4.345-4.345V279.28c0-2.4,1.946-4.346,4.345-4.346h32.443c2.399,0,4.345,1.945,4.345,4.346V289.708z
M335.919,117.333H54.081V87.062c0-6.239,5.602-9.968,11.019-9.968h108.788c20.687,0,32.219-9.173,41.484-16.542
c8.552-6.803,14.732-11.717,29.204-11.717h75.278c8.858,0,16.066,7.206,16.066,16.064V117.333z"/>
</g>
</svg>
)
case 'disc':
return (
<svg viewBox="0 0 49.652 49.652" fill={fill}>
<g>
<circle cx="24.826" cy="24.825" r="3.529"/>
<path d="M42.381,7.271C37.693,2.582,31.458,0,24.826,0C18.195,0,11.96,2.583,7.271,7.271c-9.68,9.68-9.68,25.43,0,35.11
</g>
</svg>
)
case 'disc':
return (
<svg viewBox="0 0 49.652 49.652" fill={fill}>
<g>
<circle cx="24.826" cy="24.825" r="3.529"/>
<path d="M42.381,7.271C37.693,2.582,31.458,0,24.826,0C18.195,0,11.96,2.583,7.271,7.271c-9.68,9.68-9.68,25.43,0,35.11
c4.689,4.688,10.923,7.271,17.555,7.271c6.632,0,12.867-2.582,17.555-7.271C52.061,32.701,52.061,16.951,42.381,7.271z
M24.86,45.002l0.039-12.587c-1.967,0.019-3.941-0.719-5.442-2.22c-2.965-2.965-2.964-7.772,0-10.737
c0.022-0.022,0.047-0.04,0.069-0.062l-8.935-8.936c4.059-4.072,9.234-6.027,14.363-5.91l-0.039,12.689
c1.915,0.022,3.82,0.759,5.28,2.219c2.942,2.942,2.96,7.699,0.063,10.668l8.967,8.968C35.166,43.164,29.99,45.119,24.86,45.002z"
/>
</g>
</svg>
)
default:
return (
<svg viewBox="0 0 123.769 123.769" fill={fill}>
<g>
<path d="M76.05,1.568l-10.101,9.3c-2.3,2.1-5.8,2.1-8.1,0l-10.2-9.2c-3.1-2.8-8-1.7-9.6,2.1l-8.3,20h64.2l-8.3-20.1
/>
</g>
</svg>
)
default:
return (
<svg viewBox="0 0 123.769 123.769" fill={fill}>
<g>
<path d="M76.05,1.568l-10.101,9.3c-2.3,2.1-5.8,2.1-8.1,0l-10.2-9.2c-3.1-2.8-8-1.7-9.6,2.1l-8.3,20h64.2l-8.3-20.1
C84.05-0.131,79.149-1.231,76.05,1.568z"/>
<path d="M10.749,42.068c-2.9,1.4-1.8,5.7,1.3,5.7h49.8h49.701c3.199,0,4.199-4.3,1.399-5.7l-12.2-6.3h-77.8L10.749,42.068z"/>
<path d="M0.549,90.168l5.3,28.801c0.5,2.899,3,4.8,5.9,4.8h50.1h50.201c2.899,0,5.399-2,5.899-4.8l5.3-28.801
<path d="M10.749,42.068c-2.9,1.4-1.8,5.7,1.3,5.7h49.8h49.701c3.199,0,4.199-4.3,1.399-5.7l-12.2-6.3h-77.8L10.749,42.068z"/>
<path d="M0.549,90.168l5.3,28.801c0.5,2.899,3,4.8,5.9,4.8h50.1h50.201c2.899,0,5.399-2,5.899-4.8l5.3-28.801
c0.5-2.8-1-5.6-3.699-6.699c-12.801-5-26.2-7.7-36.801-9.301c-2.699-0.399-5.3,1.101-6.3,3.5l-10.1,22.9c-1.8,4-7.5,4-9.201-0.1
l-9.8-22.7c-1.1-2.5-3.7-4-6.4-3.601c-10.6,1.5-24,4.301-36.7,9.301C1.549,84.469-0.051,87.269,0.549,90.168z"/>
</g>
</svg>
)
}
</g>
</svg>
)
}
};
export {contentIcon}
export default class Torrent extends Component {
state = {
downloading: false,
askDownloading: false,
downloadProgress: {}
downloading: false,
askDownloading: false,
downloadProgress: {}
}
constructor(props)
{
super(props)
if(props.download)
{
const { progress, downloaded, speed } = props.download
this.state.downloadProgress = {
progress, downloaded, speed
}
}
super(props)
if(props.download)
{
const { progress, downloaded, speed } = props.download
this.state.downloadProgress = {
progress, downloaded, speed
}
}
}
componentDidMount()
{
this.downloading = (hash) => {
if(this.props.torrent.hash != hash)
return;
this.downloading = (hash) => {
if(this.props.torrent.hash != hash)
return;
this.setState({downloading: true})
}
window.torrentSocket.on('downloading', this.downloading);
this.setState({downloading: true})
}
window.torrentSocket.on('downloading', this.downloading);
this.downloadDone = (hash, canceled) => {
if(this.props.torrent.hash != hash)
return;
this.downloadDone = (hash, canceled) => {
if(this.props.torrent.hash != hash)
return;
this.setState({
downloading: false,
askDownloading: !canceled
})
}
window.torrentSocket.on('downloadDone', this.downloadDone);
this.setState({
downloading: false,
askDownloading: !canceled
})
}
window.torrentSocket.on('downloadDone', this.downloadDone);
this.downloadProgress = (hash, progress) => {
if(this.props.torrent.hash != hash)
return;
this.downloadProgress = (hash, progress) => {
if(this.props.torrent.hash != hash)
return;
this.setState({
downloading: true,
askDownloading: true,
downloadProgress: progress
})
}
window.torrentSocket.on('downloadProgress', this.downloadProgress);
this.setState({
downloading: true,
askDownloading: true,
downloadProgress: progress
})
}
window.torrentSocket.on('downloadProgress', this.downloadProgress);
}
componentWillUnmount()
{
if(this.downloading)
window.torrentSocket.off('downloading', this.downloading);
if(this.downloadDone)
window.torrentSocket.off('downloadDone', this.downloadDone);
if(this.downloadProgress)
window.torrentSocket.off('downloadProgress', this.downloadProgress);
if(this.downloading)
window.torrentSocket.off('downloading', this.downloading);
if(this.downloadDone)
window.torrentSocket.off('downloadDone', this.downloadDone);
if(this.downloadProgress)
window.torrentSocket.off('downloadProgress', this.downloadProgress);
}
render()
{
const torrent = this.props.torrent;
let torrentRating = -1
if(torrent.good > 0 || torrent.bad > 0)
torrentRating = Math.round(rating(torrent.good, torrent.bad) * 100);
const torrent = this.props.torrent;
let torrentRating = -1
if(torrent.good > 0 || torrent.bad > 0)
torrentRating = Math.round(rating(torrent.good, torrent.bad) * 100);
return (
<div>
<ListItem
onClick={(e) => {
const link = '/torrent/' + torrent.hash;
if(e.button === 1)
return false;
return (
<div>
<ListItem
onClick={(e) => {
const link = '/torrent/' + torrent.hash;
if(e.button === 1)
return false;
/*
/*
if(e.ctrlKey && e.button === 0) {
let win = window.open(link, '_blank');
//win.focus();
@ -239,105 +239,105 @@ export default class Torrent extends Component {
}
*/
PagesPie.instance().open(TorrentPage, {replace: 'all', hash: torrent.hash, peer: torrent.peer})
}}
primaryText={
<a href={'/torrent/' + torrent.hash} ref={(node) => {
if(node)
node.onclick = () => { return false }
}}>
<span className='break-word' style={{
color: torrent.contentCategory != 'xxx' ? (torrent.peer ? '#5643db' : 'black') : (torrent.peer ? '#9083e2' : 'grey')
}}>
{torrent.name}
</span>
</a>
}
secondaryText={
<a href={'/torrent/' + torrent.hash} ref={(node) => {
if(node)
node.onclick = () => { return false }
}}>
<div className='column' style={{height: 'auto', whiteSpace: 'normal', paddingTop: '0.30em'}}>
<div className='row w100p inline'>
<div style={{color: torrent.contentCategory != 'xxx' ? (torrent.peer ? '#5252d1' : 'black') : (torrent.peer ? '#9083e2' : 'grey')}}>
{
formatBytes(torrent.size, 1) + ' (' + torrent.files + ' files)'
}
</div>
{
this.state.downloading
PagesPie.instance().open(TorrentPage, {replace: 'all', hash: torrent.hash, peer: torrent.peer})
}}
primaryText={
<a href={'/torrent/' + torrent.hash} ref={(node) => {
if(node)
node.onclick = () => { return false }
}}>
<span className='break-word' style={{
color: torrent.contentCategory != 'xxx' ? (torrent.peer ? '#5643db' : 'black') : (torrent.peer ? '#9083e2' : 'grey')
}}>
{torrent.name}
</span>
</a>
}
secondaryText={
<a href={'/torrent/' + torrent.hash} ref={(node) => {
if(node)
node.onclick = () => { return false }
}}>
<div className='column' style={{height: 'auto', whiteSpace: 'normal', paddingTop: '0.30em'}}>
<div className='row w100p inline'>
<div style={{color: torrent.contentCategory != 'xxx' ? (torrent.peer ? '#5252d1' : 'black') : (torrent.peer ? '#9083e2' : 'grey')}}>
{
formatBytes(torrent.size, 1) + ' (' + torrent.files + ' files)'
}
</div>
{
this.state.downloading
&&
<LinearProgress
style={{width: '44%', marginLeft: 20}}
mode="determinate"
value={this.state.downloadProgress && (this.state.downloadProgress.progress ? this.state.downloadProgress.progress : 0) * 100}
/>
}
</div>
{
torrent.path && torrent.path.length > 0
?
torrent.path.map((path, index) => {
return <div key={index} className='break-word fs0-75' style={{paddingTop: '0.3em', marginLeft: '0.6em'}}>{path}</div>
})
:
null
}
{
torrent.seeders || torrent.leechers || torrent.completed
?
<div className='break-word fs0-85' style={{paddingTop: '0.35em'}}>
<span style={{color: (torrent.seeders > 0 ? '#00C853' : 'grey')}}>{torrent.seeders} {__('seeders')}</span>
<span style={{color: (torrent.leechers > 0 ? '#AA00FF' : 'grey'), marginLeft: '12px'}}>{torrent.leechers} {__('leechers')}</span>
<span style={{color: (torrent.completed > 0 ? '#FF6D00' : 'grey'), marginLeft: '12px'}}>{torrent.completed} {__('completed')}</span>
</div>
:
null
}
{
(torrent.good > 0 || torrent.bad > 0)
style={{width: '44%', marginLeft: 20}}
mode="determinate"
value={this.state.downloadProgress && (this.state.downloadProgress.progress ? this.state.downloadProgress.progress : 0) * 100}
/>
}
</div>
{
torrent.path && torrent.path.length > 0
?
torrent.path.map((path, index) => {
return <div key={index} className='break-word fs0-75' style={{paddingTop: '0.3em', marginLeft: '0.6em'}}>{path}</div>
})
:
null
}
{
torrent.seeders || torrent.leechers || torrent.completed
?
<div className='break-word fs0-85' style={{paddingTop: '0.35em'}}>
<span style={{color: (torrent.seeders > 0 ? '#00C853' : 'grey')}}>{torrent.seeders} {__('seeders')}</span>
<span style={{color: (torrent.leechers > 0 ? '#AA00FF' : 'grey'), marginLeft: '12px'}}>{torrent.leechers} {__('leechers')}</span>
<span style={{color: (torrent.completed > 0 ? '#FF6D00' : 'grey'), marginLeft: '12px'}}>{torrent.completed} {__('completed')}</span>
</div>
:
null
}
{
(torrent.good > 0 || torrent.bad > 0)
&&
<div className='row w100p inline' style={{maxWidth: 600}}>
<LinearProgress
mode="determinate"
value={torrentRating}
color={torrentRating >= 50 ? '#00E676' : '#FF3D00'}
style={{
height: '5px',
}}
/>
<div className='row center pad0-5 fs0-85 text-nowrap' style={{color: torrentRating >= 50 ? '#00E676' : '#FF3D00', width: '190px'}}>{__('Torrent rating')}: {torrentRating}%</div>
</div>
}
</div>
</a>
}
leftIcon={contentIcon(torrent.contentType, torrent.contentCategory, torrent.contentCategory != 'xxx' ? (torrent.peer ? '#6f5ee0' : 'grey') : (torrent.peer ? '#9083e2' : '#d3d3d3'))}
rightIcon={
<div className='row inline' style={{width: 63}}>
{
!this.state.askDownloading && !this.state.downloading
?
<a href={`magnet:?xt=urn:btih:${torrent.hash}`}>
<svg style={{
height: '24px',
marginRight: 12,
fill: torrent.contentCategory != 'xxx' ? (torrent.peer ? '#5643db' : 'black') : (torrent.peer ? '#9083e2' : 'grey')
}} onClick={(e) => {
e.preventDefault();
e.stopPropagation();
this.setState({askDownloading: true})
window.torrentSocket.emit('download', torrent)
}} viewBox="0 0 56 56">
<g>
<path d="M35.586,41.586L31,46.172V28c0-1.104-0.896-2-2-2s-2,0.896-2,2v18.172l-4.586-4.586c-0.781-0.781-2.047-0.781-2.828,0
<LinearProgress
mode="determinate"
value={torrentRating}
color={torrentRating >= 50 ? '#00E676' : '#FF3D00'}
style={{
height: '5px',
}}
/>
<div className='row center pad0-5 fs0-85 text-nowrap' style={{color: torrentRating >= 50 ? '#00E676' : '#FF3D00', width: '190px'}}>{__('Torrent rating')}: {torrentRating}%</div>
</div>
}
</div>
</a>
}
leftIcon={contentIcon(torrent.contentType, torrent.contentCategory, torrent.contentCategory != 'xxx' ? (torrent.peer ? '#6f5ee0' : 'grey') : (torrent.peer ? '#9083e2' : '#d3d3d3'))}
rightIcon={
<div className='row inline' style={{width: 63}}>
{
!this.state.askDownloading && !this.state.downloading
?
<a href={`magnet:?xt=urn:btih:${torrent.hash}`}>
<svg style={{
height: '24px',
marginRight: 12,
fill: torrent.contentCategory != 'xxx' ? (torrent.peer ? '#5643db' : 'black') : (torrent.peer ? '#9083e2' : 'grey')
}} onClick={(e) => {
e.preventDefault();
e.stopPropagation();
this.setState({askDownloading: true})
window.torrentSocket.emit('download', torrent)
}} viewBox="0 0 56 56">
<g>
<path d="M35.586,41.586L31,46.172V28c0-1.104-0.896-2-2-2s-2,0.896-2,2v18.172l-4.586-4.586c-0.781-0.781-2.047-0.781-2.828,0
s-0.781,2.047,0,2.828l7.999,7.999c0.093,0.094,0.196,0.177,0.307,0.251c0.047,0.032,0.099,0.053,0.148,0.081
c0.065,0.036,0.127,0.075,0.196,0.103c0.065,0.027,0.133,0.042,0.2,0.062c0.058,0.017,0.113,0.04,0.173,0.051
C28.738,52.986,28.869,53,29,53s0.262-0.014,0.392-0.04c0.06-0.012,0.115-0.034,0.173-0.051c0.067-0.02,0.135-0.035,0.2-0.062
c0.069-0.028,0.131-0.067,0.196-0.103c0.05-0.027,0.101-0.049,0.148-0.081c0.11-0.074,0.213-0.157,0.307-0.251l7.999-7.999
c0.781-0.781,0.781-2.047,0-2.828S36.367,40.805,35.586,41.586z"/>
<path d="M47.835,18.986c-0.137-0.019-2.457-0.335-4.684,0.002C43.1,18.996,43.049,19,42.999,19c-0.486,0-0.912-0.354-0.987-0.85
<path d="M47.835,18.986c-0.137-0.019-2.457-0.335-4.684,0.002C43.1,18.996,43.049,19,42.999,19c-0.486,0-0.912-0.354-0.987-0.85
c-0.083-0.546,0.292-1.056,0.838-1.139c1.531-0.233,3.062-0.196,4.083-0.124C46.262,9.135,39.83,3,32.085,3
C27.388,3,22.667,5.379,19.8,9.129C21.754,10.781,23,13.246,23,16c0,0.553-0.447,1-1,1s-1-0.447-1-1
c0-2.462-1.281-4.627-3.209-5.876c-0.227-0.147-0.462-0.277-0.702-0.396c-0.069-0.034-0.139-0.069-0.21-0.101
@ -346,39 +346,39 @@ export default class Torrent extends Component {
l0.012,0.21l-0.009,0.16C7.008,16.744,7,16.873,7,17v0.63l-0.567,0.271C2.705,19.688,0,24,0,28.154C0,34.135,4.865,39,10.845,39H25
V28c0-2.209,1.791-4,4-4s4,1.791,4,4v11h2.353c0.059,0,0.116-0.005,0.174-0.009l0.198-0.011l0.271,0.011
C36.053,38.995,36.11,39,36.169,39h9.803C51.501,39,56,34.501,56,28.972C56,24.161,52.49,19.872,47.835,18.986z"/>
</g>
</g>
</svg>
</a>
:
this.state.askDownloading && !this.state.downloading
?
<img src={Spinner24} />
:
this.state.askDownloading && this.state.downloading
</svg>
</a>
:
this.state.askDownloading && !this.state.downloading
?
<img src={Spinner24} />
:
this.state.askDownloading && this.state.downloading
&&
<a href={`magnet:?xt=urn:btih:${torrent.hash}`}>
<svg style={{
height: '24px',
fill: torrent.contentCategory != 'xxx' ? (torrent.peer ? '#5643db' : 'black') : (torrent.peer ? '#9083e2' : 'grey')
}} onClick={(e) => {
e.preventDefault();
e.stopPropagation();
<svg style={{
height: '24px',
fill: torrent.contentCategory != 'xxx' ? (torrent.peer ? '#5643db' : 'black') : (torrent.peer ? '#9083e2' : 'grey')
}} onClick={(e) => {
e.preventDefault();
e.stopPropagation();
window.torrentSocket.emit('downloadCancel', torrent.hash)
}} viewBox="0 0 18 18"><path d="M9 1C4.58 1 1 4.58 1 9s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zm4 10.87L11.87 13 9 10.13 6.13 13 5 11.87 7.87 9 5 6.13 6.13 5 9 7.87 11.87 5 13 6.13 10.13 9 13 11.87z"/></svg>
</a>
}
<a style={{float: 'right'}} href={`magnet:?xt=urn:btih:${torrent.hash}`}>
<svg style={{
height: '24px',
fill: torrent.contentCategory != 'xxx' ? (torrent.peer ? '#5643db' : 'black') : (torrent.peer ? '#9083e2' : 'grey')
}} onClick={(e) => {
e.preventDefault();
e.stopPropagation();
var win = window.open(`magnet:?xt=urn:btih:${torrent.hash}`, '_self');
}} viewBox="0 0 24 24">
<path d="M15.82 10.736l-5.451 6.717c-.561.691-1.214 1.042-1.94 1.042-1.144
window.torrentSocket.emit('downloadCancel', torrent.hash)
}} viewBox="0 0 18 18"><path d="M9 1C4.58 1 1 4.58 1 9s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zm4 10.87L11.87 13 9 10.13 6.13 13 5 11.87 7.87 9 5 6.13 6.13 5 9 7.87 11.87 5 13 6.13 10.13 9 13 11.87z"/></svg>
</a>
}
<a style={{float: 'right'}} href={`magnet:?xt=urn:btih:${torrent.hash}`}>
<svg style={{
height: '24px',
fill: torrent.contentCategory != 'xxx' ? (torrent.peer ? '#5643db' : 'black') : (torrent.peer ? '#9083e2' : 'grey')
}} onClick={(e) => {
e.preventDefault();
e.stopPropagation();
var win = window.open(`magnet:?xt=urn:btih:${torrent.hash}`, '_self');
}} viewBox="0 0 24 24">
<path d="M15.82 10.736l-5.451 6.717c-.561.691-1.214 1.042-1.94 1.042-1.144
0-2.327-.899-2.753-2.091-.214-.6-.386-1.76.865-2.784 3.417-2.794 6.716-5.446
6.716-5.446l-3.363-4.174s-4.532 3.657-6.771 5.487c-2.581 2.108-3.123 4.468-3.123
6.075 0 4.416 4.014 8.438 8.42 8.438 1.604 0 3.963-.543 6.084-3.128 1.835-2.237
@ -390,12 +390,12 @@ export default class Torrent extends Component {
4.151zm6.152-7.934l4.318-2.88-1.575-.638 1.889-2.414-4.421 2.788 1.716.695-1.927
2.449zm-7.292-7.186l4.916-1.667-1.356-1.022 2.448-2.006-4.991 1.712
1.478 1.114-2.495 1.869z"/></svg>
</a>
</div>
}
/>
<Divider />
</div>
)
</a>
</div>
}
/>
<Divider />
</div>
)
}
}

View File

@ -2,94 +2,94 @@ import ReactDOM from 'react-dom';
let listenSwipe = (component, handlers) =>
{
let element = ReactDOM.findDOMNode(component),
startX,
startY,
distanceX,
distanceY,
threshold = 110,
allowedTime = 280,
thresholdAlternativeAxis = 145,
elapsedTime,
startTime;
let element = ReactDOM.findDOMNode(component),
startX,
startY,
distanceX,
distanceY,
threshold = 110,
allowedTime = 280,
thresholdAlternativeAxis = 145,
elapsedTime,
startTime;
let touchFunctions = {
touchstart : (e) => {
let touchObject = e.changedTouches[0]
let touchFunctions = {
touchstart : (e) => {
let touchObject = e.changedTouches[0]
distanceX = 0;
distanceY = 0;
distanceX = 0;
distanceY = 0;
startX = touchObject.pageX
startY = touchObject.pageY
startX = touchObject.pageX
startY = touchObject.pageY
startTime = new Date().getTime()
startTime = new Date().getTime()
if(handlers && handlers.preventDefault)
e.preventDefault();
if(handlers && handlers.preventDefault)
e.preventDefault();
if(handlers && handlers.initSwipe)
handlers.initSwipe.call(component);
},
touchmove : (e) => {
if(handlers && handlers.preventDefault)
e.preventDefault();
},
touchend : (e) => {
let touchObject = e.changedTouches[0];
if(handlers && handlers.initSwipe)
handlers.initSwipe.call(component);
},
touchmove : (e) => {
if(handlers && handlers.preventDefault)
e.preventDefault();
},
touchend : (e) => {
let touchObject = e.changedTouches[0];
distanceX = touchObject.pageX - startX;
distanceY = touchObject.pageY - startY;
distanceX = touchObject.pageX - startX;
distanceY = touchObject.pageY - startY;
elapsedTime = new Date().getTime() - startTime; // get time elapsed
elapsedTime = new Date().getTime() - startTime; // get time elapsed
let params = {
startX,
startY,
endX: touchObject.pageX,
endY: touchObject.pageY,
distanceX,
distanceY
};
let params = {
startX,
startY,
endX: touchObject.pageX,
endY: touchObject.pageY,
distanceX,
distanceY
};
if (elapsedTime <= allowedTime)
{
if(distanceX >= threshold && Math.abs(distanceY) <= thresholdAlternativeAxis)
{
if(handlers && handlers.left)
handlers.left.call(component, params);
} else
if(-distanceX >= threshold && Math.abs(distanceY) <= thresholdAlternativeAxis) {
if(handlers && handlers.right)
handlers.right.call(component, params);
} else
if(distanceY >= threshold && Math.abs(distanceX) <= thresholdAlternativeAxis) {
if(handlers && handlers.top)
handlers.top.call(component, params);
} else
if(-distanceY >= threshold && Math.abs(distanceX) <= thresholdAlternativeAxis) {
if(handlers && handlers.bottom)
handlers.bottom.call(component, params);
}
}
if (elapsedTime <= allowedTime)
{
if(distanceX >= threshold && Math.abs(distanceY) <= thresholdAlternativeAxis)
{
if(handlers && handlers.left)
handlers.left.call(component, params);
} else
if(-distanceX >= threshold && Math.abs(distanceY) <= thresholdAlternativeAxis) {
if(handlers && handlers.right)
handlers.right.call(component, params);
} else
if(distanceY >= threshold && Math.abs(distanceX) <= thresholdAlternativeAxis) {
if(handlers && handlers.top)
handlers.top.call(component, params);
} else
if(-distanceY >= threshold && Math.abs(distanceX) <= thresholdAlternativeAxis) {
if(handlers && handlers.bottom)
handlers.bottom.call(component, params);
}
}
if(handlers && handlers.preventDefault)
e.preventDefault();
}
};
if(handlers && handlers.preventDefault)
e.preventDefault();
}
};
element.addEventListener('touchstart', touchFunctions.touchstart, false);
element.addEventListener('touchmove', touchFunctions.touchmove, false);
element.addEventListener('touchend', touchFunctions.touchend, false);
element.addEventListener('touchstart', touchFunctions.touchstart, false);
element.addEventListener('touchmove', touchFunctions.touchmove, false);
element.addEventListener('touchend', touchFunctions.touchend, false);
return touchFunctions;
return touchFunctions;
}
let removeSwipeListener = (component, touchFunctions) => {
let element = ReactDOM.findDOMNode(component);
element.removeEventListener('touchstart', touchFunctions.touchstart);
element.removeEventListener('touchmove', touchFunctions.touchmove);
element.removeEventListener('touchend', touchFunctions.touchend);
let element = ReactDOM.findDOMNode(component);
element.removeEventListener('touchstart', touchFunctions.touchstart);
element.removeEventListener('touchmove', touchFunctions.touchmove);
element.removeEventListener('touchend', touchFunctions.touchend);
}
export { listenSwipe, removeSwipeListener }