Search: Initial support for Python 3.x
30
src/misc.cpp
@ -36,6 +36,7 @@
|
||||
#include <QDateTime>
|
||||
#include <QByteArray>
|
||||
#include <QDebug>
|
||||
#include <QProcess>
|
||||
|
||||
#ifdef DISABLE_GUI
|
||||
#include <QCoreApplication>
|
||||
@ -548,9 +549,36 @@ QPoint misc::screenCenter(QWidget *win) {
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Detects the version of python by calling
|
||||
* "python --version" and parsing the output.
|
||||
*/
|
||||
int misc::pythonVersion() {
|
||||
static int version = -1;
|
||||
if (version < 0) {
|
||||
QProcess python_proc;
|
||||
python_proc.start("python", QStringList() << "--version", QIODevice::ReadOnly);
|
||||
if (!python_proc.waitForFinished()) return -1;
|
||||
if (python_proc.exitCode() < 0) return -1;
|
||||
QByteArray output = python_proc.readAllStandardOutput();
|
||||
if (output.isEmpty())
|
||||
output = python_proc.readAllStandardError();
|
||||
const QByteArray version_str = output.split(' ').last();
|
||||
qDebug() << "Python version is:" << version_str.trimmed();
|
||||
if (version_str.startsWith("3."))
|
||||
version = 3;
|
||||
else
|
||||
version = 2;
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
QString misc::searchEngineLocation() {
|
||||
QString folder = "nova";
|
||||
if (pythonVersion() >= 3)
|
||||
folder = "nova3";
|
||||
const QString location = QDir::cleanPath(QDesktopServicesDataLocation()
|
||||
+ QDir::separator() + "nova");
|
||||
+ QDir::separator() + folder);
|
||||
QDir locationDir(location);
|
||||
if(!locationDir.exists())
|
||||
locationDir.mkpath(locationDir.absolutePath());
|
||||
|
@ -151,6 +151,7 @@ public:
|
||||
// Get screen center
|
||||
static QPoint screenCenter(QWidget *win);
|
||||
#endif
|
||||
static int pythonVersion();
|
||||
static QString searchEngineLocation();
|
||||
static QString BTBackupLocation();
|
||||
static QString cacheLocation();
|
||||
|
@ -1056,7 +1056,7 @@ public:
|
||||
QSettings reg_python("HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore", QIniSettings::NativeFormat);
|
||||
QStringList versions = reg_python.childGroups();
|
||||
qDebug("Python versions nb: %d", versions.size());
|
||||
versions = versions.filter(QRegExp("2\\..*"));
|
||||
//versions = versions.filter(QRegExp("2\\..*"));
|
||||
versions.sort();
|
||||
while(!versions.empty()) {
|
||||
const QString version = versions.takeLast();
|
||||
@ -1067,17 +1067,14 @@ public:
|
||||
return path;
|
||||
}
|
||||
}
|
||||
if(QFile::exists("C:/Python27/python.exe")) {
|
||||
reg_python.setValue("2.7/InstallPath/Default", "C:\\Python27");
|
||||
return "C:\\Python27";
|
||||
}
|
||||
if(QFile::exists("C:/Python26/python.exe")) {
|
||||
reg_python.setValue("2.6/InstallPath/Default", "C:\\Python26");
|
||||
return "C:\\Python26";
|
||||
}
|
||||
if(QFile::exists("C:/Python25/python.exe")) {
|
||||
reg_python.setValue("2.5/InstallPath/Default", "C:\\Python25");
|
||||
return "C:\\Python25";
|
||||
// Fallback: Detect python from default locations
|
||||
QStringList supported_versions;
|
||||
supported_versions << "32" << "31" << "30" << "27" << "26" << "25";
|
||||
foreach(const v, supported_versions) {
|
||||
if(QFile::exists("C:/Python"+v+"/python.exe")) {
|
||||
reg_python.setValue(v[0]+"."+v[1]+"/InstallPath/Default", "C:\\Python"+v);
|
||||
return "C:\\Python"+v;
|
||||
}
|
||||
}
|
||||
return QString::null;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@
|
||||
#include <QTemporaryFile>
|
||||
|
||||
enum EngineColumns {ENGINE_NAME, ENGINE_URL, ENGINE_STATE, ENGINE_ID};
|
||||
#define UPDATE_URL "http://qbittorrent.svn.sourceforge.net/viewvc/qbittorrent/trunk/src/searchengine/nova/engines/"
|
||||
const QString UPDATE_URL = QString("https://gitorious.org/qbittorrent/qbittorrent/blobs/raw/master/src/searchengine/") + (misc::pythonVersion() >= 3 ? "nova3" : "nova") + "/engines/";
|
||||
|
||||
engineSelectDlg::engineSelectDlg(QWidget *parent, SupportedEngines *supported_engines) : QDialog(parent), supported_engines(supported_engines) {
|
||||
setupUi(this);
|
||||
|
0
src/searchengine/nova3/__init__.py
Normal file
0
src/searchengine/nova3/engines/__init__.py
Normal file
BIN
src/searchengine/nova3/engines/btdigg.png
Normal file
After Width: | Height: | Size: 692 B |
66
src/searchengine/nova3/engines/btdigg.py
Normal file
@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
#VERSION: 1.1
|
||||
#AUTHORS: BTDigg team (research@btdigg.org)
|
||||
#
|
||||
# GNU GENERAL PUBLIC LICENSE
|
||||
# Version 3, 29 June 2007
|
||||
#
|
||||
# <http://www.gnu.org/licenses/>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
import urllib.request, urllib.parse, urllib.error
|
||||
import urllib.request, urllib.error, urllib.parse
|
||||
import sys
|
||||
|
||||
from novaprinter import prettyPrinter
|
||||
|
||||
class btdigg(object):
|
||||
url = 'http://btdigg.org'
|
||||
name = 'BTDigg'
|
||||
|
||||
supported_categories = {'all': ''}
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def search(self, what, cat='all'):
|
||||
req = what.replace('+', ' ')
|
||||
u = urllib.request.urlopen('http://api.btdigg.org/api/public-8e9a50f8335b964f/s01?%s' % (urllib.parse.urlencode(dict(q = req)),))
|
||||
|
||||
try:
|
||||
for line in u:
|
||||
if line.startswith('#'):
|
||||
continue
|
||||
|
||||
info_hash, name, files, size, dl, seen = line.strip().split('\t')[:6]
|
||||
|
||||
res = dict(link = 'magnet:?xt=urn:btih:%s' % (info_hash,),
|
||||
name = name.translate(None, '|'),
|
||||
size = size,
|
||||
seeds = int(dl),
|
||||
leech = int(dl),
|
||||
engine_url = self.url,
|
||||
desc_link = 'http://btdigg.org/search?%s' % (urllib.parse.urlencode(dict(info_hash = info_hash, q = req)),))
|
||||
|
||||
prettyPrinter(res)
|
||||
finally:
|
||||
u.close()
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
s = btdigg()
|
||||
s.search(sys.argv[1])
|
BIN
src/searchengine/nova3/engines/btjunkie.png
Normal file
After Width: | Height: | Size: 622 B |
122
src/searchengine/nova3/engines/btjunkie.py
Normal file
@ -0,0 +1,122 @@
|
||||
#VERSION: 2.31
|
||||
#AUTHORS: Christophe Dumez (chris@qbittorrent.org)
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
from novaprinter import prettyPrinter
|
||||
from helpers import retrieve_url, download_file
|
||||
import sgmllib3
|
||||
import re
|
||||
|
||||
class btjunkie(object):
|
||||
url = 'http://btjunkie.org'
|
||||
name = 'btjunkie'
|
||||
supported_categories = {'all': '0', 'movies': '6', 'tv': '4', 'music': '1', 'games': '2', 'anime': '7', 'software': '3'}
|
||||
|
||||
def __init__(self):
|
||||
self.results = []
|
||||
self.parser = self.SimpleSGMLParser(self.results, self.url)
|
||||
|
||||
def download_torrent(self, info):
|
||||
print(download_file(info))
|
||||
|
||||
class SimpleSGMLParser(sgmllib3.SGMLParser):
|
||||
def __init__(self, results, url, *args):
|
||||
sgmllib3.SGMLParser.__init__(self)
|
||||
self.url = url
|
||||
self.th_counter = None
|
||||
self.current_item = None
|
||||
self.results = results
|
||||
|
||||
def start_a(self, attr):
|
||||
params = dict(attr)
|
||||
#print params
|
||||
if 'href' in params:
|
||||
if params['href'].startswith("http://dl.btjunkie.org/torrent"):
|
||||
self.current_item = {}
|
||||
self.th_counter = 0
|
||||
self.current_item['link']=params['href'].strip()
|
||||
elif self.th_counter == 0 and params['href'].startswith("/torrent/") and params['href'].find('/files/') == -1:
|
||||
self.current_item['desc_link'] = 'http://btjunkie.org'+params['href'].strip()
|
||||
|
||||
def handle_data(self, data):
|
||||
if self.th_counter == 0:
|
||||
if 'name' not in self.current_item:
|
||||
self.current_item['name'] = ''
|
||||
self.current_item['name']+= data.strip()
|
||||
elif self.th_counter == 3:
|
||||
if 'size' not in self.current_item:
|
||||
self.current_item['size'] = ''
|
||||
self.current_item['size']+= data.strip()
|
||||
elif self.th_counter == 5:
|
||||
if 'seeds' not in self.current_item:
|
||||
self.current_item['seeds'] = ''
|
||||
self.current_item['seeds']+= data.strip()
|
||||
elif self.th_counter == 6:
|
||||
if 'leech' not in self.current_item:
|
||||
self.current_item['leech'] = ''
|
||||
self.current_item['leech']+= data.strip()
|
||||
|
||||
def start_th(self,attr):
|
||||
if isinstance(self.th_counter,int):
|
||||
self.th_counter += 1
|
||||
if self.th_counter > 6:
|
||||
self.th_counter = None
|
||||
# Display item
|
||||
if self.current_item:
|
||||
self.current_item['engine_url'] = self.url
|
||||
if not self.current_item['seeds'].isdigit():
|
||||
self.current_item['seeds'] = 0
|
||||
if not self.current_item['leech'].isdigit():
|
||||
self.current_item['leech'] = 0
|
||||
prettyPrinter(self.current_item)
|
||||
self.results.append('a')
|
||||
|
||||
def search(self, what, cat='all'):
|
||||
# Remove {} since btjunkie does not seem
|
||||
# to handle those very well
|
||||
what = what.replace('{', '').replace('}', '')
|
||||
ret = []
|
||||
i = 1
|
||||
while True and i<11:
|
||||
results = []
|
||||
parser = self.SimpleSGMLParser(results, self.url)
|
||||
dat = retrieve_url(self.url+'/search?q=%s&c=%s&o=52&p=%d'%(what, self.supported_categories[cat], i))
|
||||
# Remove <font> tags from page
|
||||
p = re.compile( '<[/]?font.*?>')
|
||||
dat = p.sub('', dat)
|
||||
#print dat
|
||||
#return
|
||||
results_re = re.compile('(?s)class="tab_results">.*')
|
||||
for match in results_re.finditer(dat):
|
||||
res_tab = match.group(0)
|
||||
parser.feed(res_tab)
|
||||
parser.close()
|
||||
break
|
||||
if len(results) <= 0:
|
||||
break
|
||||
i += 1
|
||||
|
BIN
src/searchengine/nova3/engines/extratorrent.png
Normal file
After Width: | Height: | Size: 605 B |
116
src/searchengine/nova3/engines/extratorrent.py
Executable file
@ -0,0 +1,116 @@
|
||||
#VERSION: 1.1
|
||||
#AUTHORS: Christophe Dumez (chris@qbittorrent.org)
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
from novaprinter import prettyPrinter
|
||||
from helpers import retrieve_url, download_file
|
||||
import sgmllib3
|
||||
import re
|
||||
|
||||
class extratorrent(object):
|
||||
url = 'http://extratorrent.com'
|
||||
name = 'extratorrent'
|
||||
supported_categories = {'all': '', 'movies': '4', 'tv': '8', 'music': '5', 'games': '3', 'anime': '1', 'software': '7', 'books': '2', 'pictures': '6'}
|
||||
|
||||
def __init__(self):
|
||||
self.results = []
|
||||
self.parser = self.SimpleSGMLParser(self.results, self.url)
|
||||
|
||||
def download_torrent(self, info):
|
||||
print(download_file(info))
|
||||
|
||||
class SimpleSGMLParser(sgmllib3.SGMLParser):
|
||||
def __init__(self, results, url, *args):
|
||||
sgmllib3.SGMLParser.__init__(self)
|
||||
self.url = url
|
||||
self.td_counter = None
|
||||
self.current_item = None
|
||||
self.start_name = False
|
||||
self.results = results
|
||||
|
||||
def start_a(self, attr):
|
||||
params = dict(attr)
|
||||
#print params
|
||||
if 'href' in params and params['href'].startswith("/torrent_download/"):
|
||||
self.current_item = {}
|
||||
self.td_counter = 0
|
||||
self.start_name = False
|
||||
torrent_id = '/'.join(params['href'].split('/')[2:])
|
||||
self.current_item['link']=self.url+'/download/'+torrent_id
|
||||
elif 'href' in params and params['href'].startswith("/torrent/") and params['href'].endswith(".html"):
|
||||
self.current_item['desc_link'] = self.url + params['href'].strip()
|
||||
self.start_name = True
|
||||
|
||||
def handle_data(self, data):
|
||||
if self.td_counter == 2:
|
||||
if 'name' not in self.current_item and self.start_name:
|
||||
self.current_item['name'] = data.strip()
|
||||
elif self.td_counter == 3:
|
||||
if 'size' not in self.current_item:
|
||||
self.current_item['size'] = ''
|
||||
self.current_item['size']+= data.replace(" ", " ").strip()
|
||||
elif self.td_counter == 4:
|
||||
if 'seeds' not in self.current_item:
|
||||
self.current_item['seeds'] = ''
|
||||
self.current_item['seeds']+= data.strip()
|
||||
elif self.td_counter == 5:
|
||||
if 'leech' not in self.current_item:
|
||||
self.current_item['leech'] = ''
|
||||
self.current_item['leech']+= data.strip()
|
||||
|
||||
def start_td(self,attr):
|
||||
if isinstance(self.td_counter,int):
|
||||
self.td_counter += 1
|
||||
if self.td_counter > 5:
|
||||
self.td_counter = None
|
||||
# Display item
|
||||
if self.current_item:
|
||||
self.current_item['engine_url'] = self.url
|
||||
if not self.current_item['seeds'].isdigit():
|
||||
self.current_item['seeds'] = 0
|
||||
if not self.current_item['leech'].isdigit():
|
||||
self.current_item['leech'] = 0
|
||||
prettyPrinter(self.current_item)
|
||||
self.results.append('a')
|
||||
|
||||
def search(self, what, cat='all'):
|
||||
ret = []
|
||||
i = 1
|
||||
while True and i<11:
|
||||
results = []
|
||||
parser = self.SimpleSGMLParser(results, self.url)
|
||||
dat = retrieve_url(self.url+'/advanced_search/?with=%s&s_cat=%s&page=%d'%(what, self.supported_categories[cat], i))
|
||||
results_re = re.compile('(?s)<table class="tl"><thead>.*')
|
||||
for match in results_re.finditer(dat):
|
||||
res_tab = match.group(0)
|
||||
parser.feed(res_tab)
|
||||
parser.close()
|
||||
break
|
||||
if len(results) <= 0:
|
||||
break
|
||||
i += 1
|
||||
|
BIN
src/searchengine/nova3/engines/isohunt.png
Normal file
After Width: | Height: | Size: 633 B |
69
src/searchengine/nova3/engines/isohunt.py
Normal file
@ -0,0 +1,69 @@
|
||||
#VERSION: 1.4
|
||||
#AUTHORS: Christophe Dumez (chris@qbittorrent.org)
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from novaprinter import prettyPrinter
|
||||
import re
|
||||
from helpers import retrieve_url, download_file
|
||||
|
||||
class isohunt(object):
|
||||
url = 'http://isohunt.com'
|
||||
name = 'isoHunt'
|
||||
supported_categories = {'all': '', 'movies': '1', 'tv': '3', 'music': '2', 'games': '4', 'anime': '7', 'software': '5', 'pictures': '6', 'books': '9'}
|
||||
|
||||
def download_torrent(self, info):
|
||||
print(download_file(info))
|
||||
|
||||
def search(self, what, cat='all'):
|
||||
# Remove {} since isohunt does not seem
|
||||
# to handle those very well
|
||||
what = what.replace('{', '').replace('}', '')
|
||||
i = 1
|
||||
while True and i<11:
|
||||
res = 0
|
||||
dat = retrieve_url(self.url+'/torrents.php?ihq=%s&iht=%s&ihp=%s&ihs1=2&iho1=d'%(what, self.supported_categories[cat],i))
|
||||
# I know it's not very readable, but the SGML parser feels in pain
|
||||
section_re = re.compile('(?s)id=link.*?</tr><tr')
|
||||
torrent_re = re.compile('(?s)torrent_details/(?P<link>.*?[^/]+).*?'
|
||||
'>(?P<name>.*?)</a>.*?'
|
||||
'>(?P<size>[\d,\.]+\s+MB)</td>.*?'
|
||||
'>(?P<seeds>\d+)</td>.*?'
|
||||
'>(?P<leech>\d+)</td>')
|
||||
for match in section_re.finditer(dat):
|
||||
txt = match.group(0)
|
||||
m = torrent_re.search(txt)
|
||||
if m:
|
||||
torrent_infos = m.groupdict()
|
||||
torrent_infos['name'] = re.sub('<.*?>', '', torrent_infos['name'])
|
||||
torrent_infos['engine_url'] = self.url
|
||||
torrent_code = torrent_infos['link']
|
||||
torrent_infos['link'] = 'http://isohunt.com/download/'+torrent_code
|
||||
torrent_infos['desc_link'] = 'http://isohunt.com/torrent_details/'+torrent_code+'/dvdrip?tab=summary'
|
||||
prettyPrinter(torrent_infos)
|
||||
res = res + 1
|
||||
if res == 0:
|
||||
break
|
||||
i = i + 1
|
BIN
src/searchengine/nova3/engines/kickasstorrents.png
Normal file
After Width: | Height: | Size: 787 B |
71
src/searchengine/nova3/engines/kickasstorrents.py
Executable file
@ -0,0 +1,71 @@
|
||||
#VERSION: 1.2
|
||||
#AUTHORS: Christophe Dumez (chris@qbittorrent.org)
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
from novaprinter import prettyPrinter
|
||||
from helpers import retrieve_url, download_file
|
||||
import json
|
||||
|
||||
class kickasstorrents(object):
|
||||
url = 'http://www.kickasstorrents.com'
|
||||
name = 'kickasstorrents'
|
||||
supported_categories = {'all': 'all', 'movies': 'movies', 'tv': 'tv', 'music': 'music', 'games': 'games', 'software': 'applications'}
|
||||
|
||||
def __init__(self):
|
||||
self.results = []
|
||||
|
||||
def download_torrent(self, info):
|
||||
print(download_file(info))
|
||||
|
||||
def search(self, what, cat='all'):
|
||||
ret = []
|
||||
i = 1
|
||||
while True and i<11:
|
||||
results = []
|
||||
json_data = retrieve_url(self.url+'/search/%s/%d/?categories[]=%s&field=seeders&sorder=desc&json=1'%(what, i, self.supported_categories[cat]))
|
||||
try:
|
||||
json_dict = json.loads(json_data)
|
||||
except:
|
||||
i += 1
|
||||
continue
|
||||
if json_dict['total_results'] <= 0: return
|
||||
results = json_dict['list']
|
||||
for r in results:
|
||||
try:
|
||||
res_dict = dict()
|
||||
res_dict['name'] = r['title']
|
||||
res_dict['size'] = str(r['size'])
|
||||
res_dict['seeds'] = r['seeds']
|
||||
res_dict['leech'] = r['leechs']
|
||||
res_dict['link'] = r['torrentLink']
|
||||
res_dict['desc_link'] = r['link']
|
||||
res_dict['engine_url'] = self.url
|
||||
prettyPrinter(res_dict)
|
||||
except:
|
||||
pass
|
||||
i += 1
|
||||
|
BIN
src/searchengine/nova3/engines/mininova.png
Normal file
After Width: | Height: | Size: 365 B |
114
src/searchengine/nova3/engines/mininova.py
Normal file
@ -0,0 +1,114 @@
|
||||
#VERSION: 1.50
|
||||
#AUTHORS: Christophe Dumez (chris@qbittorrent.org)
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from novaprinter import prettyPrinter
|
||||
from helpers import retrieve_url, download_file
|
||||
import sgmllib3
|
||||
import re
|
||||
|
||||
class mininova(object):
|
||||
# Mandatory properties
|
||||
url = 'http://www.mininova.org'
|
||||
name = 'Mininova'
|
||||
supported_categories = {'all': '0', 'movies': '4', 'tv': '8', 'music': '5', 'games': '3', 'anime': '1', 'software': '7', 'pictures': '6', 'books': '2'}
|
||||
|
||||
def __init__(self):
|
||||
self.results = []
|
||||
self.parser = self.SimpleSGMLParser(self.results, self.url)
|
||||
|
||||
def download_torrent(self, info):
|
||||
print(download_file(info))
|
||||
|
||||
class SimpleSGMLParser(sgmllib3.SGMLParser):
|
||||
def __init__(self, results, url, *args):
|
||||
sgmllib3.SGMLParser.__init__(self)
|
||||
self.url = url
|
||||
self.td_counter = None
|
||||
self.current_item = None
|
||||
self.results = results
|
||||
|
||||
def start_a(self, attr):
|
||||
params = dict(attr)
|
||||
#print params
|
||||
if 'href' in params:
|
||||
if params['href'].startswith("/get/"):
|
||||
self.current_item = {}
|
||||
self.td_counter = 0
|
||||
self.current_item['link']=self.url+params['href'].strip()
|
||||
elif params['href'].startswith("/tor/"):
|
||||
self.current_item['desc_link']=self.url+params['href'].strip()
|
||||
|
||||
def handle_data(self, data):
|
||||
if self.td_counter == 0:
|
||||
if 'name' not in self.current_item:
|
||||
self.current_item['name'] = ''
|
||||
self.current_item['name']+= data
|
||||
elif self.td_counter == 1:
|
||||
if 'size' not in self.current_item:
|
||||
self.current_item['size'] = ''
|
||||
self.current_item['size']+= data.strip()
|
||||
elif self.td_counter == 2:
|
||||
if 'seeds' not in self.current_item:
|
||||
self.current_item['seeds'] = ''
|
||||
self.current_item['seeds']+= data.strip()
|
||||
elif self.td_counter == 3:
|
||||
if 'leech' not in self.current_item:
|
||||
self.current_item['leech'] = ''
|
||||
self.current_item['leech']+= data.strip()
|
||||
|
||||
def start_td(self,attr):
|
||||
if isinstance(self.td_counter,int):
|
||||
self.td_counter += 1
|
||||
if self.td_counter > 4:
|
||||
self.td_counter = None
|
||||
# Display item
|
||||
if self.current_item:
|
||||
self.current_item['engine_url'] = self.url
|
||||
if not self.current_item['seeds'].isdigit():
|
||||
self.current_item['seeds'] = 0
|
||||
if not self.current_item['leech'].isdigit():
|
||||
self.current_item['leech'] = 0
|
||||
prettyPrinter(self.current_item)
|
||||
self.results.append('a')
|
||||
|
||||
def search(self, what, cat='all'):
|
||||
ret = []
|
||||
i = 1
|
||||
while True and i<11:
|
||||
results = []
|
||||
parser = self.SimpleSGMLParser(results, self.url)
|
||||
dat = retrieve_url(self.url+'/search/%s/%s/seeds/%d'%(what, self.supported_categories[cat], i))
|
||||
results_re = re.compile('(?s)<h1>Search results for.*')
|
||||
for match in results_re.finditer(dat):
|
||||
res_tab = match.group(0)
|
||||
parser.feed(res_tab)
|
||||
parser.close()
|
||||
break
|
||||
if len(results) <= 0:
|
||||
break
|
||||
i += 1
|
||||
|
BIN
src/searchengine/nova3/engines/piratebay.png
Normal file
After Width: | Height: | Size: 609 B |
113
src/searchengine/nova3/engines/piratebay.py
Normal file
@ -0,0 +1,113 @@
|
||||
#VERSION: 1.40
|
||||
#AUTHORS: Fabien Devaux (fab@gnux.info)
|
||||
#CONTRIBUTORS: Christophe Dumez (chris@qbittorrent.org)
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from novaprinter import prettyPrinter
|
||||
import sgmllib3
|
||||
from helpers import retrieve_url, download_file
|
||||
|
||||
class piratebay(object):
|
||||
url = 'http://thepiratebay.org'
|
||||
name = 'The Pirate Bay'
|
||||
supported_categories = {'all': '0', 'movies': '200', 'music': '100', 'games': '400', 'software': '300'}
|
||||
|
||||
def __init__(self):
|
||||
self.results = []
|
||||
self.parser = self.SimpleSGMLParser(self.results, self.url)
|
||||
|
||||
def download_torrent(self, info):
|
||||
print(download_file(info))
|
||||
|
||||
class SimpleSGMLParser(sgmllib3.SGMLParser):
|
||||
def __init__(self, results, url, *args):
|
||||
sgmllib3.SGMLParser.__init__(self)
|
||||
self.td_counter = None
|
||||
self.current_item = None
|
||||
self.results = results
|
||||
self.url = url
|
||||
self.code = 0
|
||||
self.in_name = None
|
||||
|
||||
def start_a(self, attr):
|
||||
params = dict(attr)
|
||||
if params['href'].startswith('/torrent/'):
|
||||
self.current_item = {}
|
||||
self.td_counter = 0
|
||||
self.code = params['href'].split('/')[2]
|
||||
self.current_item['desc_link'] = 'http://thepiratebay.org'+params['href'].strip()
|
||||
self.in_name = True
|
||||
elif params['href'].startswith('http://torrents.thepiratebay.org/%s'%self.code):
|
||||
self.current_item['link']=params['href'].strip()
|
||||
self.in_name = False
|
||||
|
||||
def handle_data(self, data):
|
||||
if self.td_counter == 0:
|
||||
if self.in_name:
|
||||
if 'name' not in self.current_item:
|
||||
self.current_item['name'] = ''
|
||||
self.current_item['name']+= data.strip()
|
||||
else:
|
||||
#Parse size
|
||||
if 'Size' in data:
|
||||
self.current_item['size'] = data[data.index("Size")+5:]
|
||||
self.current_item['size'] = self.current_item['size'][:self.current_item['size'].index(',')]
|
||||
elif self.td_counter == 1:
|
||||
if 'seeds' not in self.current_item:
|
||||
self.current_item['seeds'] = ''
|
||||
self.current_item['seeds']+= data.strip()
|
||||
elif self.td_counter == 2:
|
||||
if 'leech' not in self.current_item:
|
||||
self.current_item['leech'] = ''
|
||||
self.current_item['leech']+= data.strip()
|
||||
|
||||
def start_td(self,attr):
|
||||
if isinstance(self.td_counter,int):
|
||||
self.td_counter += 1
|
||||
if self.td_counter > 3:
|
||||
self.td_counter = None
|
||||
# Display item
|
||||
if self.current_item:
|
||||
self.current_item['engine_url'] = self.url
|
||||
if not self.current_item['seeds'].isdigit():
|
||||
self.current_item['seeds'] = 0
|
||||
if not self.current_item['leech'].isdigit():
|
||||
self.current_item['leech'] = 0
|
||||
prettyPrinter(self.current_item)
|
||||
self.results.append('a')
|
||||
def search(self, what, cat='all'):
|
||||
ret = []
|
||||
i = 0
|
||||
order = 'se'
|
||||
while True and i<11:
|
||||
results = []
|
||||
parser = self.SimpleSGMLParser(results, self.url)
|
||||
dat = retrieve_url(self.url+'/search/%s/%d/7/%s' % (what, i, self.supported_categories[cat]))
|
||||
parser.feed(dat)
|
||||
parser.close()
|
||||
if len(results) <= 0:
|
||||
break
|
||||
i += 1
|
BIN
src/searchengine/nova3/engines/torrentdownloads.png
Normal file
After Width: | Height: | Size: 423 B |
141
src/searchengine/nova3/engines/torrentdownloads.py
Normal file
@ -0,0 +1,141 @@
|
||||
#VERSION: 1.1
|
||||
#AUTHORS: Christophe Dumez (chris@qbittorrent.org)
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
from novaprinter import prettyPrinter
|
||||
from helpers import retrieve_url
|
||||
import io, gzip, urllib.request, urllib.error, urllib.parse, tempfile
|
||||
import sgmllib3
|
||||
import re
|
||||
import os
|
||||
|
||||
class torrentdownloads(object):
|
||||
url = 'http://www.torrentdownloads.net'
|
||||
name = 'TorrentDownloads'
|
||||
supported_categories = {'all': '0', 'movies': '4', 'tv': '8', 'music': '5', 'games': '3', 'anime': '1', 'software': '7', 'books': '2'}
|
||||
|
||||
def __init__(self):
|
||||
self.results = []
|
||||
#self.parser = self.SimpleSGMLParser(self.results, self.url)
|
||||
|
||||
def download_torrent(self, url):
|
||||
""" Download file at url and write it to a file, return the path to the file and the url """
|
||||
file, path = tempfile.mkstemp()
|
||||
file = os.fdopen(file, "w")
|
||||
# Download url
|
||||
req = urllib.request.Request(url)
|
||||
response = urllib.request.urlopen(req)
|
||||
dat = response.read()
|
||||
# Check if it is gzipped
|
||||
if dat[:2] == '\037\213':
|
||||
# Data is gzip encoded, decode it
|
||||
compressedstream = io.StringIO(dat)
|
||||
gzipper = gzip.GzipFile(fileobj=compressedstream)
|
||||
extracted_data = gzipper.read()
|
||||
dat = extracted_data
|
||||
|
||||
# Write it to a file
|
||||
file.write(dat.strip())
|
||||
file.close()
|
||||
# return file path
|
||||
print(path+" "+url)
|
||||
|
||||
class SimpleSGMLParser(sgmllib3.SGMLParser):
|
||||
def __init__(self, results, url, what, *args):
|
||||
sgmllib3.SGMLParser.__init__(self)
|
||||
self.url = url
|
||||
self.li_counter = None
|
||||
self.current_item = None
|
||||
self.results = results
|
||||
self.what = what.upper().split('+')
|
||||
if len(self.what) == 0:
|
||||
self.what = None
|
||||
|
||||
def start_a(self, attr):
|
||||
params = dict(attr)
|
||||
#print params
|
||||
if 'href' in params and params['href'].startswith("http://www.torrentdownloads.net/torrent/"):
|
||||
self.current_item = {}
|
||||
self.li_counter = 0
|
||||
self.current_item['desc_link'] = params['href'].strip()
|
||||
self.current_item['link']=params['href'].strip().replace('/torrent', '/download', 1)
|
||||
|
||||
def handle_data(self, data):
|
||||
if self.li_counter == 0:
|
||||
if 'name' not in self.current_item:
|
||||
self.current_item['name'] = ''
|
||||
self.current_item['name']+= data
|
||||
elif self.li_counter == 1:
|
||||
if 'size' not in self.current_item:
|
||||
self.current_item['size'] = ''
|
||||
self.current_item['size']+= data.strip().replace(" ", " ")
|
||||
elif self.li_counter == 2:
|
||||
if 'seeds' not in self.current_item:
|
||||
self.current_item['seeds'] = ''
|
||||
self.current_item['seeds']+= data.strip()
|
||||
elif self.li_counter == 3:
|
||||
if 'leech' not in self.current_item:
|
||||
self.current_item['leech'] = ''
|
||||
self.current_item['leech']+= data.strip()
|
||||
|
||||
def start_li(self,attr):
|
||||
if isinstance(self.li_counter,int):
|
||||
self.li_counter += 1
|
||||
if self.li_counter > 3:
|
||||
self.li_counter = None
|
||||
# Display item
|
||||
if self.current_item:
|
||||
self.current_item['engine_url'] = self.url
|
||||
if not self.current_item['seeds'].isdigit():
|
||||
self.current_item['seeds'] = 0
|
||||
if not self.current_item['leech'].isdigit():
|
||||
self.current_item['leech'] = 0
|
||||
# Search should use AND operator as a default
|
||||
tmp = self.current_item['name'].upper();
|
||||
if self.what is not None:
|
||||
for w in self.what:
|
||||
if tmp.find(w) < 0: return
|
||||
prettyPrinter(self.current_item)
|
||||
self.results.append('a')
|
||||
|
||||
def search(self, what, cat='all'):
|
||||
ret = []
|
||||
i = 1
|
||||
while i<11:
|
||||
results = []
|
||||
parser = self.SimpleSGMLParser(results, self.url, what)
|
||||
dat = retrieve_url(self.url+'/search/?page=%d&search=%s&s_cat=%s&srt=seeds&pp=50&order=desc'%(i, what, self.supported_categories[cat]))
|
||||
results_re = re.compile('(?s)<div class="torrentlist">.*')
|
||||
for match in results_re.finditer(dat):
|
||||
res_tab = match.group(0)
|
||||
parser.feed(res_tab)
|
||||
parser.close()
|
||||
break
|
||||
if len(results) <= 0:
|
||||
break
|
||||
i += 1
|
||||
|
BIN
src/searchengine/nova3/engines/torrentreactor.png
Normal file
After Width: | Height: | Size: 529 B |
107
src/searchengine/nova3/engines/torrentreactor.py
Normal file
@ -0,0 +1,107 @@
|
||||
#VERSION: 1.31
|
||||
#AUTHORS: Gekko Dam Beer (gekko04@users.sourceforge.net)
|
||||
#CONTRIBUTORS: Christophe Dumez (chris@qbittorrent.org)
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from novaprinter import prettyPrinter
|
||||
import sgmllib3
|
||||
from helpers import retrieve_url, download_file
|
||||
|
||||
class torrentreactor(object):
|
||||
url = 'http://www.torrentreactor.net'
|
||||
name = 'TorrentReactor.Net'
|
||||
supported_categories = {'all': '', 'movies': '5', 'tv': '8', 'music': '6', 'games': '3', 'anime': '1', 'software': '2'}
|
||||
|
||||
def download_torrent(self, info):
|
||||
print(download_file(info))
|
||||
|
||||
class SimpleSGMLParser(sgmllib3.SGMLParser):
|
||||
def __init__(self, results, url, *args):
|
||||
sgmllib3.SGMLParser.__init__(self)
|
||||
self.td_counter = None
|
||||
self.current_item = None
|
||||
self.results = results
|
||||
self.id = None
|
||||
self.url = url
|
||||
|
||||
def start_a(self, attr):
|
||||
params = dict(attr)
|
||||
if 'torrentreactor.net/download.php' in params['href']:
|
||||
self.current_item = {}
|
||||
self.td_counter = 0
|
||||
self.current_item['link'] = params['href'].strip()
|
||||
elif params['href'].startswith('/torrents/'):
|
||||
self.current_item['desc_link'] = 'http://www.torrentreactor.net'+params['href'].strip()
|
||||
|
||||
def handle_data(self, data):
|
||||
if self.td_counter == 0:
|
||||
if 'name' not in self.current_item:
|
||||
self.current_item['name'] = ''
|
||||
self.current_item['name']+= data.strip()
|
||||
if self.td_counter == 1:
|
||||
if 'size' not in self.current_item:
|
||||
self.current_item['size'] = ''
|
||||
self.current_item['size']+= data.strip()
|
||||
elif self.td_counter == 2:
|
||||
if 'seeds' not in self.current_item:
|
||||
self.current_item['seeds'] = ''
|
||||
self.current_item['seeds']+= data.strip()
|
||||
elif self.td_counter == 3:
|
||||
if 'leech' not in self.current_item:
|
||||
self.current_item['leech'] = ''
|
||||
self.current_item['leech']+= data.strip()
|
||||
|
||||
def start_td(self,attr):
|
||||
if isinstance(self.td_counter,int):
|
||||
self.td_counter += 1
|
||||
if self.td_counter > 3:
|
||||
self.td_counter = None
|
||||
# add item to results
|
||||
if self.current_item:
|
||||
self.current_item['engine_url'] = self.url
|
||||
if not self.current_item['seeds'].isdigit():
|
||||
self.current_item['seeds'] = 0
|
||||
if not self.current_item['leech'].isdigit():
|
||||
self.current_item['leech'] = 0
|
||||
prettyPrinter(self.current_item)
|
||||
self.has_results = True
|
||||
self.results.append('a')
|
||||
|
||||
def __init__(self):
|
||||
self.results = []
|
||||
self.parser = self.SimpleSGMLParser(self.results, self.url)
|
||||
|
||||
def search(self, what, cat='all'):
|
||||
i = 0
|
||||
while True and i<11:
|
||||
results = []
|
||||
parser = self.SimpleSGMLParser(results, self.url)
|
||||
dat = retrieve_url(self.url+'/search.php?search=&words=%s&cid=%s&sid=&type=1&orderby=a.seeds&asc=0&skip=%s'%(what, self.supported_categories[cat], (i*35)))
|
||||
parser.feed(dat)
|
||||
parser.close()
|
||||
if len(results) <= 0:
|
||||
break
|
||||
i += 1
|
BIN
src/searchengine/nova3/engines/vertor.png
Normal file
After Width: | Height: | Size: 643 B |
129
src/searchengine/nova3/engines/vertor.py
Executable file
@ -0,0 +1,129 @@
|
||||
#VERSION: 1.3
|
||||
#AUTHORS: Christophe Dumez (chris@qbittorrent.org)
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
from novaprinter import prettyPrinter
|
||||
from helpers import retrieve_url, download_file
|
||||
import sgmllib3
|
||||
import re
|
||||
|
||||
class vertor(object):
|
||||
url = 'http://www.vertor.com'
|
||||
name = 'vertor'
|
||||
supported_categories = {'all': '0', 'movies': '5', 'tv': '8', 'music': '6', 'games': '3', 'anime': '1', 'software': '2'}
|
||||
|
||||
def __init__(self):
|
||||
self.results = []
|
||||
self.parser = self.SimpleSGMLParser(self.results, self.url)
|
||||
|
||||
def download_torrent(self, info):
|
||||
print(download_file(info))
|
||||
|
||||
class SimpleSGMLParser(sgmllib3.SGMLParser):
|
||||
def __init__(self, results, url, *args):
|
||||
sgmllib3.SGMLParser.__init__(self)
|
||||
self.url = url
|
||||
self.td_counter = None
|
||||
self.current_item = None
|
||||
self.results = results
|
||||
self.in_name = False
|
||||
self.outside_td = True;
|
||||
|
||||
def start_tr(self, attr):
|
||||
self.td_counter = -1
|
||||
self.current_item = {}
|
||||
|
||||
def end_tr(self):
|
||||
if self.td_counter == 5:
|
||||
self.td_counter = None
|
||||
# Display item
|
||||
if self.current_item and 'link' in self.current_item:
|
||||
self.current_item['engine_url'] = self.url
|
||||
if not self.current_item['seeds'].isdigit():
|
||||
self.current_item['seeds'] = 0
|
||||
if not self.current_item['leech'].isdigit():
|
||||
self.current_item['leech'] = 0
|
||||
prettyPrinter(self.current_item)
|
||||
self.results.append('a')
|
||||
|
||||
def start_a(self, attr):
|
||||
#if self.td_counter is None or self.td_counter < 0: return
|
||||
params = dict(attr)
|
||||
if 'href' in params and params['href'].startswith("http://www.vertor.com/index.php?mod=download"):
|
||||
self.current_item['link']=params['href'].strip()
|
||||
elif self.td_counter == 0 and 'href' in params and params['href'].startswith("/torrents/") \
|
||||
and 'name' not in self.current_item:
|
||||
self.current_item['desc_link']='http://www.vertor.com'+params['href'].strip()
|
||||
self.in_name = True
|
||||
|
||||
def end_a(self):
|
||||
if self.in_name:
|
||||
self.in_name = False
|
||||
|
||||
def handle_data(self, data):
|
||||
if self.in_name:
|
||||
if 'name' not in self.current_item:
|
||||
self.current_item['name'] = ''
|
||||
self.current_item['name']+= data.strip()
|
||||
elif self.td_counter == 2 and not self.outside_td:
|
||||
if 'size' not in self.current_item:
|
||||
self.current_item['size'] = ''
|
||||
self.current_item['size']+= data.strip()
|
||||
elif self.td_counter == 4 and not self.outside_td:
|
||||
if 'seeds' not in self.current_item:
|
||||
self.current_item['seeds'] = ''
|
||||
self.current_item['seeds']+= data.strip()
|
||||
elif self.td_counter == 5 and not self.outside_td:
|
||||
if 'leech' not in self.current_item:
|
||||
self.current_item['leech'] = ''
|
||||
self.current_item['leech']+= data.strip()
|
||||
|
||||
def end_td(self):
|
||||
self.outside_td = True
|
||||
|
||||
def start_td(self,attr):
|
||||
if isinstance(self.td_counter,int):
|
||||
self.outside_td = False
|
||||
self.td_counter += 1
|
||||
|
||||
def search(self, what, cat='all'):
|
||||
ret = []
|
||||
i = 0
|
||||
while True and i<11:
|
||||
results = []
|
||||
parser = self.SimpleSGMLParser(results, self.url)
|
||||
dat = retrieve_url(self.url+'/index.php?mod=search&words=%s&cid=%s&orderby=a.seeds&asc=0&search=&exclude=&p=%d'%(what, self.supported_categories[cat], i))
|
||||
results_re = re.compile('(?s)<table class="listing">.*')
|
||||
for match in results_re.finditer(dat):
|
||||
res_tab = match.group(0)
|
||||
parser.feed(res_tab)
|
||||
parser.close()
|
||||
break
|
||||
if len(results) <= 0:
|
||||
break
|
||||
i += 1
|
||||
|
99
src/searchengine/nova3/helpers.py
Normal file
@ -0,0 +1,99 @@
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#VERSION: 1.31
|
||||
|
||||
# Author:
|
||||
# Christophe DUMEZ (chris@qbittorrent.org)
|
||||
|
||||
import re, html.entities
|
||||
import tempfile
|
||||
import os
|
||||
import io, gzip, urllib.request, urllib.error, urllib.parse
|
||||
import socket
|
||||
import socks
|
||||
import re
|
||||
|
||||
# SOCKS5 Proxy support
|
||||
if "sock_proxy" in os.environ and len(os.environ["sock_proxy"].strip()) > 0:
|
||||
proxy_str = os.environ["sock_proxy"].strip()
|
||||
m=re.match(r"^(?:(?P<username>[^:]+):(?P<password>[^@]+)@)?(?P<host>[^:]+):(?P<port>\w+)$", proxy_str)
|
||||
if m is not None:
|
||||
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, m.group('host'), int(m.group('port')), True, m.group('username'), m.group('password'))
|
||||
socket.socket = socks.socksocket
|
||||
|
||||
def htmlentitydecode(s):
|
||||
# First convert alpha entities (such as é)
|
||||
# (Inspired from http://mail.python.org/pipermail/python-list/2007-June/443813.html)
|
||||
def entity2char(m):
|
||||
entity = m.group(1)
|
||||
if entity in html.entities.name2codepoint:
|
||||
return chr(html.entities.name2codepoint[entity])
|
||||
return " " # Unknown entity: We replace with a space.
|
||||
t = re.sub('&(%s);' % '|'.join(html.entities.name2codepoint), entity2char, s)
|
||||
|
||||
# Then convert numerical entities (such as é)
|
||||
t = re.sub('&#(\d+);', lambda x: chr(int(x.group(1))), t)
|
||||
|
||||
# Then convert hexa entities (such as é)
|
||||
return re.sub('&#x(\w+);', lambda x: chr(int(x.group(1),16)), t)
|
||||
|
||||
def retrieve_url(url):
|
||||
""" Return the content of the url page as a string """
|
||||
response = urllib.request.urlopen(url)
|
||||
dat = response.read()
|
||||
info = response.info()
|
||||
charset = 'utf-8'
|
||||
try:
|
||||
ignore, charset = info['Content-Type'].split('charset=')
|
||||
except:
|
||||
pass
|
||||
dat = dat.decode(charset, 'replace')
|
||||
dat = htmlentitydecode(dat)
|
||||
#return dat.encode('utf-8', 'replace')
|
||||
return dat
|
||||
|
||||
def download_file(url, referer=None):
|
||||
""" Download file at url and write it to a file, return the path to the file and the url """
|
||||
file, path = tempfile.mkstemp()
|
||||
file = os.fdopen(file, "w")
|
||||
# Download url
|
||||
req = urllib.request.Request(url)
|
||||
if referer is not None:
|
||||
req.add_header('referer', referer)
|
||||
response = urllib.request.urlopen(req)
|
||||
dat = response.read()
|
||||
# Check if it is gzipped
|
||||
if dat[:2] == '\037\213':
|
||||
# Data is gzip encoded, decode it
|
||||
compressedstream = io.StringIO(dat)
|
||||
gzipper = gzip.GzipFile(fileobj=compressedstream)
|
||||
extracted_data = gzipper.read()
|
||||
dat = extracted_data
|
||||
|
||||
# Write it to a file
|
||||
file.write(dat)
|
||||
file.close()
|
||||
# return file path
|
||||
return path+" "+url
|
158
src/searchengine/nova3/nova2.py
Executable file
@ -0,0 +1,158 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
#VERSION: 1.23
|
||||
|
||||
# Author:
|
||||
# Fabien Devaux <fab AT gnux DOT info>
|
||||
# Contributors:
|
||||
# Christophe Dumez <chris@qbittorrent.org> (qbittorrent integration)
|
||||
# Thanks to gab #gcu @ irc.freenode.net (multipage support on PirateBay)
|
||||
# Thanks to Elias <gekko04@users.sourceforge.net> (torrentreactor and isohunt search engines)
|
||||
#
|
||||
# Licence: BSD
|
||||
|
||||
import sys
|
||||
import threading
|
||||
import os
|
||||
import glob
|
||||
import urllib.parse
|
||||
|
||||
THREADED = True
|
||||
CATEGORIES = ('all', 'movies', 'tv', 'music', 'games', 'anime', 'software', 'pictures', 'books')
|
||||
|
||||
################################################################################
|
||||
# Every engine should have a "search" method taking
|
||||
# a space-free string as parameter (ex. "family+guy")
|
||||
# it should call prettyPrinter() with a dict as parameter.
|
||||
# The keys in the dict must be: link,name,size,seeds,leech,engine_url
|
||||
# As a convention, try to list results by decrasing number of seeds or similar
|
||||
################################################################################
|
||||
|
||||
supported_engines = []
|
||||
|
||||
engines = glob.glob(os.path.join(os.path.dirname(__file__), 'engines','*.py'))
|
||||
for engine in engines:
|
||||
e = engine.split(os.sep)[-1][:-3]
|
||||
if len(e.strip()) == 0: continue
|
||||
if e.startswith('_'): continue
|
||||
try:
|
||||
exec("from engines.%s import %s"%(e,e))
|
||||
supported_engines.append(e)
|
||||
except:
|
||||
pass
|
||||
|
||||
def engineToXml(short_name):
|
||||
xml = "<%s>\n"%short_name
|
||||
exec("search_engine = %s()"%short_name, globals())
|
||||
xml += "<name>%s</name>\n"%search_engine.name
|
||||
xml += "<url>%s</url>\n"%search_engine.url
|
||||
xml += "<categories>"
|
||||
if hasattr(search_engine, 'supported_categories'):
|
||||
supported_categories = list(search_engine.supported_categories.keys())
|
||||
supported_categories.remove('all')
|
||||
xml += " ".join(supported_categories)
|
||||
xml += "</categories>\n"
|
||||
xml += "</%s>\n"%short_name
|
||||
return xml
|
||||
|
||||
def displayCapabilities():
|
||||
"""
|
||||
Display capabilities in XML format
|
||||
<capabilities>
|
||||
<engine_short_name>
|
||||
<name>long name</name>
|
||||
<url>http://example.com</url>
|
||||
<categories>movies music games</categories>
|
||||
</engine_short_name>
|
||||
</capabilities>
|
||||
"""
|
||||
xml = "<capabilities>"
|
||||
for short_name in supported_engines:
|
||||
xml += engineToXml(short_name)
|
||||
xml += "</capabilities>"
|
||||
print(xml)
|
||||
|
||||
class EngineLauncher(threading.Thread):
|
||||
def __init__(self, engine, what, cat='all'):
|
||||
threading.Thread.__init__(self)
|
||||
self.engine = engine
|
||||
self.what = what
|
||||
self.cat = cat
|
||||
def run(self):
|
||||
if hasattr(self.engine, 'supported_categories'):
|
||||
if self.cat == 'all' or self.cat in list(self.engine.supported_categories.keys()):
|
||||
self.engine.search(self.what, self.cat)
|
||||
elif self.cat == 'all':
|
||||
self.engine.search(self.what)
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 2:
|
||||
raise SystemExit('./nova2.py [all|engine1[,engine2]*] <category> <keywords>\navailable engines: %s'%
|
||||
(','.join(supported_engines)))
|
||||
|
||||
if len(sys.argv) == 2:
|
||||
if sys.argv[1] == "--capabilities":
|
||||
displayCapabilities()
|
||||
sys.exit(0)
|
||||
else:
|
||||
raise SystemExit('./nova.py [all|engine1[,engine2]*] <category> <keywords>\navailable engines: %s'%
|
||||
(','.join(supported_engines)))
|
||||
|
||||
engines_list = [e.lower() for e in sys.argv[1].strip().split(',')]
|
||||
|
||||
if 'all' in engines_list:
|
||||
engines_list = supported_engines
|
||||
|
||||
cat = sys.argv[2].lower()
|
||||
|
||||
if cat not in CATEGORIES:
|
||||
raise SystemExit('Invalid category!')
|
||||
|
||||
what = urllib.parse.quote('+'.join(sys.argv[3:]))
|
||||
|
||||
threads = []
|
||||
for engine in engines_list:
|
||||
try:
|
||||
if THREADED:
|
||||
exec("l = EngineLauncher(%s(), what, cat)"%engine)
|
||||
threads.append(l)
|
||||
l.start()
|
||||
else:
|
||||
exec("e = %s()"%engine)
|
||||
if hasattr(engine, 'supported_categories'):
|
||||
if cat == 'all' or cat in list(e.supported_categories.keys()):
|
||||
e.search(what, cat)
|
||||
elif self.cat == 'all':
|
||||
e.search(what)
|
||||
engine().search(what, cat)
|
||||
except:
|
||||
pass
|
||||
if THREADED:
|
||||
for t in threads:
|
||||
t.join()
|
64
src/searchengine/nova3/nova2dl.py
Executable file
@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#VERSION: 1.10
|
||||
|
||||
# Author:
|
||||
# Christophe DUMEZ (chris@qbittorrent.org)
|
||||
|
||||
import sys
|
||||
import os
|
||||
import glob
|
||||
from .helpers import download_file
|
||||
|
||||
supported_engines = dict()
|
||||
|
||||
engines = glob.glob(os.path.join(os.path.dirname(__file__), 'engines','*.py'))
|
||||
for engine in engines:
|
||||
e = engine.split(os.sep)[-1][:-3]
|
||||
if len(e.strip()) == 0: continue
|
||||
if e.startswith('_'): continue
|
||||
try:
|
||||
exec("from engines.%s import %s"%(e,e))
|
||||
exec("engine_url = %s.url"%e)
|
||||
supported_engines[engine_url] = e
|
||||
except:
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 3:
|
||||
raise SystemExit('./nova2dl.py engine_url download_parameter')
|
||||
engine_url = sys.argv[1].strip()
|
||||
download_param = sys.argv[2].strip()
|
||||
if engine_url not in list(supported_engines.keys()):
|
||||
raise SystemExit('./nova2dl.py: this engine_url was not recognized')
|
||||
exec("engine = %s()"%supported_engines[engine_url])
|
||||
if hasattr(engine, 'download_torrent'):
|
||||
engine.download_torrent(download_param)
|
||||
else:
|
||||
print(download_file(download_param))
|
||||
sys.exit(0)
|
70
src/searchengine/nova3/novaprinter.py
Normal file
@ -0,0 +1,70 @@
|
||||
#VERSION: 1.43
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author nor the names of its contributors may be
|
||||
# used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import sys
|
||||
#import codecs
|
||||
|
||||
# Force UTF-8 printing
|
||||
#sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
|
||||
|
||||
def prettyPrinter(dictionary):
|
||||
# Convert everything to unicode for safe printing
|
||||
#for key,value in list(dictionary.items()):
|
||||
#if isinstance(dictionary[key], str):
|
||||
# dictionary[key] = str(dictionary[key], 'utf-8')
|
||||
dictionary['size'] = anySizeToBytes(dictionary['size'])
|
||||
if 'desc_link' in dictionary:
|
||||
print("%s|%s|%s|%s|%s|%s|%s"%(dictionary['link'],dictionary['name'].replace('|',' '),dictionary['size'],dictionary['seeds'],dictionary['leech'],dictionary['engine_url'],dictionary['desc_link']))
|
||||
else:
|
||||
print("%s|%s|%s|%s|%s|%s"%(dictionary['link'],dictionary['name'].replace('|',' '),dictionary['size'],dictionary['seeds'],dictionary['leech'],dictionary['engine_url']))
|
||||
|
||||
def anySizeToBytes(size_string):
|
||||
"""
|
||||
Convert a string like '1 KB' to '1024' (bytes)
|
||||
"""
|
||||
# separate integer from unit
|
||||
try:
|
||||
size, unit = size_string.split()
|
||||
except:
|
||||
try:
|
||||
size = size_string.strip()
|
||||
unit = ''.join([c for c in size if c.isalpha()])
|
||||
if len(unit) > 0:
|
||||
size = size[:-len(unit)]
|
||||
except:
|
||||
return -1
|
||||
if len(size) == 0:
|
||||
return -1
|
||||
size = float(size)
|
||||
if len(unit) == 0:
|
||||
return int(size)
|
||||
short_unit = unit.upper()[0]
|
||||
|
||||
# convert
|
||||
units_dict = { 'T': 40, 'G': 30, 'M': 20, 'K': 10 }
|
||||
if short_unit in units_dict:
|
||||
size = size * 2**units_dict[short_unit]
|
||||
return int(size)
|
391
src/searchengine/nova3/socks.py
Normal file
@ -0,0 +1,391 @@
|
||||
"""SocksiPy - Python SOCKS module.
|
||||
Version 1.01
|
||||
|
||||
Copyright 2006 Dan-Haim. All rights reserved.
|
||||
Various fixes by Christophe DUMEZ <chris@qbittorrent.org> - 2010
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
3. Neither the name of Dan Haim nor the names of his contributors may be used
|
||||
to endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY DAN HAIM "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
EVENT SHALL DAN HAIM OR HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMANGE.
|
||||
|
||||
|
||||
This module provides a standard socket-like interface for Python
|
||||
for tunneling connections through SOCKS proxies.
|
||||
|
||||
"""
|
||||
|
||||
import socket
|
||||
import struct
|
||||
|
||||
PROXY_TYPE_SOCKS4 = 1
|
||||
PROXY_TYPE_SOCKS5 = 2
|
||||
PROXY_TYPE_HTTP = 3
|
||||
|
||||
_defaultproxy = None
|
||||
_orgsocket = socket.socket
|
||||
|
||||
class ProxyError(Exception):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
||||
|
||||
class GeneralProxyError(ProxyError):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
||||
|
||||
class Socks5AuthError(ProxyError):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
||||
|
||||
class Socks5Error(ProxyError):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
||||
|
||||
class Socks4Error(ProxyError):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
||||
|
||||
class HTTPError(ProxyError):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
||||
|
||||
_generalerrors = ("success",
|
||||
"invalid data",
|
||||
"not connected",
|
||||
"not available",
|
||||
"bad proxy type",
|
||||
"bad input")
|
||||
|
||||
_socks5errors = ("succeeded",
|
||||
"general SOCKS server failure",
|
||||
"connection not allowed by ruleset",
|
||||
"Network unreachable",
|
||||
"Host unreachable",
|
||||
"Connection refused",
|
||||
"TTL expired",
|
||||
"Command not supported",
|
||||
"Address type not supported",
|
||||
"Unknown error")
|
||||
|
||||
_socks5autherrors = ("succeeded",
|
||||
"authentication is required",
|
||||
"all offered authentication methods were rejected",
|
||||
"unknown username or invalid password",
|
||||
"unknown error")
|
||||
|
||||
_socks4errors = ("request granted",
|
||||
"request rejected or failed",
|
||||
"request rejected because SOCKS server cannot connect to identd on the client",
|
||||
"request rejected because the client program and identd report different user-ids",
|
||||
"unknown error")
|
||||
|
||||
def setdefaultproxy(proxytype=None,addr=None,port=None,rdns=True,username=None,password=None):
|
||||
"""setdefaultproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
|
||||
Sets a default proxy which all further socksocket objects will use,
|
||||
unless explicitly changed.
|
||||
"""
|
||||
global _defaultproxy
|
||||
_defaultproxy = (proxytype,addr,port,rdns,username,password)
|
||||
|
||||
class socksocket(socket.socket):
|
||||
"""socksocket([family[, type[, proto]]]) -> socket object
|
||||
|
||||
Open a SOCKS enabled socket. The parameters are the same as
|
||||
those of the standard socket init. In order for SOCKS to work,
|
||||
you must specify family=AF_INET, type=SOCK_STREAM and proto=0.
|
||||
"""
|
||||
|
||||
def __init__(self, family=socket.AF_INET, type=socket.SOCK_STREAM, proto=0, _sock=None):
|
||||
_orgsocket.__init__(self,family,type,proto,_sock)
|
||||
if _defaultproxy != None:
|
||||
self.__proxy = _defaultproxy
|
||||
else:
|
||||
self.__proxy = (None, None, None, None, None, None)
|
||||
self.__proxysockname = None
|
||||
self.__proxypeername = None
|
||||
|
||||
def __recvall(self, bytes):
|
||||
"""__recvall(bytes) -> data
|
||||
Receive EXACTLY the number of bytes requested from the socket.
|
||||
Blocks until the required number of bytes have been received.
|
||||
"""
|
||||
data = ""
|
||||
while len(data) < bytes:
|
||||
d = self.recv(bytes-len(data))
|
||||
if not d:
|
||||
raise GeneralProxyError("connection closed unexpectedly")
|
||||
data = data + d
|
||||
return data
|
||||
|
||||
def setproxy(self,proxytype=None,addr=None,port=None,rdns=True,username=None,password=None):
|
||||
"""setproxy(proxytype, addr[, port[, rdns[, username[, password]]]])
|
||||
Sets the proxy to be used.
|
||||
proxytype - The type of the proxy to be used. Three types
|
||||
are supported: PROXY_TYPE_SOCKS4 (including socks4a),
|
||||
PROXY_TYPE_SOCKS5 and PROXY_TYPE_HTTP
|
||||
addr - The address of the server (IP or DNS).
|
||||
port - The port of the server. Defaults to 1080 for SOCKS
|
||||
servers and 8080 for HTTP proxy servers.
|
||||
rdns - Should DNS queries be preformed on the remote side
|
||||
(rather than the local side). The default is True.
|
||||
Note: This has no effect with SOCKS4 servers.
|
||||
username - Username to authenticate with to the server.
|
||||
The default is no authentication.
|
||||
password - Password to authenticate with to the server.
|
||||
Only relevant when username is also provided.
|
||||
"""
|
||||
self.__proxy = (proxytype,addr,port,rdns,username,password)
|
||||
|
||||
def __negotiatesocks5(self,destaddr,destport):
|
||||
"""__negotiatesocks5(self,destaddr,destport)
|
||||
Negotiates a connection through a SOCKS5 server.
|
||||
"""
|
||||
# First we'll send the authentication packages we support.
|
||||
if (self.__proxy[4]!=None) and (self.__proxy[5]!=None):
|
||||
# The username/password details were supplied to the
|
||||
# setproxy method so we support the USERNAME/PASSWORD
|
||||
# authentication (in addition to the standard none).
|
||||
self.sendall("\x05\x02\x00\x02")
|
||||
else:
|
||||
# No username/password were entered, therefore we
|
||||
# only support connections with no authentication.
|
||||
self.sendall("\x05\x01\x00")
|
||||
# We'll receive the server's response to determine which
|
||||
# method was selected
|
||||
chosenauth = self.__recvall(2)
|
||||
if chosenauth[0] != "\x05":
|
||||
self.close()
|
||||
raise GeneralProxyError((1,_generalerrors[1]))
|
||||
# Check the chosen authentication method
|
||||
if chosenauth[1] == "\x00":
|
||||
# No authentication is required
|
||||
pass
|
||||
elif chosenauth[1] == "\x02":
|
||||
# Okay, we need to perform a basic username/password
|
||||
# authentication.
|
||||
self.sendall("\x01" + chr(len(self.__proxy[4])) + self.__proxy[4] + chr(len(self.__proxy[5])) + self.__proxy[5])
|
||||
authstat = self.__recvall(2)
|
||||
if authstat[0] != "\x01":
|
||||
# Bad response
|
||||
self.close()
|
||||
raise GeneralProxyError((1,_generalerrors[1]))
|
||||
if authstat[1] != "\x00":
|
||||
# Authentication failed
|
||||
self.close()
|
||||
raise Socks5AuthError((3,_socks5autherrors[3]))
|
||||
# Authentication succeeded
|
||||
else:
|
||||
# Reaching here is always bad
|
||||
self.close()
|
||||
if chosenauth[1] == "\xFF":
|
||||
raise Socks5AuthError((2,_socks5autherrors[2]))
|
||||
else:
|
||||
raise GeneralProxyError((1,_generalerrors[1]))
|
||||
# Now we can request the actual connection
|
||||
req = "\x05\x01\x00"
|
||||
# If the given destination address is an IP address, we'll
|
||||
# use the IPv4 address request even if remote resolving was specified.
|
||||
try:
|
||||
ipaddr = socket.inet_aton(destaddr)
|
||||
req = req + "\x01" + ipaddr
|
||||
except socket.error:
|
||||
# Well it's not an IP number, so it's probably a DNS name.
|
||||
if self.__proxy[3]==True:
|
||||
# Resolve remotely
|
||||
ipaddr = None
|
||||
req = req + "\x03" + chr(len(destaddr)) + destaddr
|
||||
else:
|
||||
# Resolve locally
|
||||
ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
|
||||
req = req + "\x01" + ipaddr
|
||||
req = req + struct.pack(">H",destport)
|
||||
self.sendall(req)
|
||||
# Get the response
|
||||
resp = self.__recvall(4)
|
||||
if resp[0] != "\x05":
|
||||
self.close()
|
||||
raise GeneralProxyError((1,_generalerrors[1]))
|
||||
elif resp[1] != "\x00":
|
||||
# Connection failed
|
||||
self.close()
|
||||
if ord(resp[1])<=8:
|
||||
raise Socks5Error((ord(resp[1]),_generalerrors[ord(resp[1])]))
|
||||
else:
|
||||
raise Socks5Error((9,_generalerrors[9]))
|
||||
# Get the bound address/port
|
||||
elif resp[3] == "\x01":
|
||||
boundaddr = self.__recvall(4)
|
||||
elif resp[3] == "\x03":
|
||||
resp = resp + self.recv(1)
|
||||
boundaddr = self.__recvall(ord(resp[4]))
|
||||
else:
|
||||
self.close()
|
||||
raise GeneralProxyError((1,_generalerrors[1]))
|
||||
boundport = struct.unpack(">H",self.__recvall(2))[0]
|
||||
self.__proxysockname = (boundaddr,boundport)
|
||||
if ipaddr != None:
|
||||
self.__proxypeername = (socket.inet_ntoa(ipaddr),destport)
|
||||
else:
|
||||
self.__proxypeername = (destaddr,destport)
|
||||
|
||||
def getproxysockname(self):
|
||||
"""getsockname() -> address info
|
||||
Returns the bound IP address and port number at the proxy.
|
||||
"""
|
||||
return self.__proxysockname
|
||||
|
||||
def getproxypeername(self):
|
||||
"""getproxypeername() -> address info
|
||||
Returns the IP and port number of the proxy.
|
||||
"""
|
||||
return _orgsocket.getpeername(self)
|
||||
|
||||
def getpeername(self):
|
||||
"""getpeername() -> address info
|
||||
Returns the IP address and port number of the destination
|
||||
machine (note: getproxypeername returns the proxy)
|
||||
"""
|
||||
return self.__proxypeername
|
||||
|
||||
def __negotiatesocks4(self,destaddr,destport):
|
||||
"""__negotiatesocks4(self,destaddr,destport)
|
||||
Negotiates a connection through a SOCKS4 server.
|
||||
"""
|
||||
# Check if the destination address provided is an IP address
|
||||
rmtrslv = False
|
||||
try:
|
||||
ipaddr = socket.inet_aton(destaddr)
|
||||
except socket.error:
|
||||
# It's a DNS name. Check where it should be resolved.
|
||||
if self.__proxy[3]==True:
|
||||
ipaddr = "\x00\x00\x00\x01"
|
||||
rmtrslv = True
|
||||
else:
|
||||
ipaddr = socket.inet_aton(socket.gethostbyname(destaddr))
|
||||
# Construct the request packet
|
||||
req = "\x04\x01" + struct.pack(">H",destport) + ipaddr
|
||||
# The username parameter is considered userid for SOCKS4
|
||||
if self.__proxy[4] != None:
|
||||
req = req + self.__proxy[4]
|
||||
req = req + "\x00"
|
||||
# DNS name if remote resolving is required
|
||||
# NOTE: This is actually an extension to the SOCKS4 protocol
|
||||
# called SOCKS4A and may not be supported in all cases.
|
||||
if rmtrslv==True:
|
||||
req = req + destaddr + "\x00"
|
||||
self.sendall(req)
|
||||
# Get the response from the server
|
||||
resp = self.__recvall(8)
|
||||
if resp[0] != "\x00":
|
||||
# Bad data
|
||||
self.close()
|
||||
raise GeneralProxyError((1,_generalerrors[1]))
|
||||
if resp[1] != "\x5A":
|
||||
# Server returned an error
|
||||
self.close()
|
||||
if ord(resp[1]) in (91,92,93):
|
||||
self.close()
|
||||
raise Socks4Error((ord(resp[1]),_socks4errors[ord(resp[1])-90]))
|
||||
else:
|
||||
raise Socks4Error((94,_socks4errors[4]))
|
||||
# Get the bound address/port
|
||||
self.__proxysockname = (socket.inet_ntoa(resp[4:]),struct.unpack(">H",resp[2:4])[0])
|
||||
if rmtrslv != None:
|
||||
self.__proxypeername = (socket.inet_ntoa(ipaddr),destport)
|
||||
else:
|
||||
self.__proxypeername = (destaddr,destport)
|
||||
|
||||
def __negotiatehttp(self,destaddr,destport):
|
||||
"""__negotiatehttp(self,destaddr,destport)
|
||||
Negotiates a connection through an HTTP server.
|
||||
"""
|
||||
# If we need to resolve locally, we do this now
|
||||
if self.__proxy[3] == False:
|
||||
addr = socket.gethostbyname(destaddr)
|
||||
else:
|
||||
addr = destaddr
|
||||
self.sendall("CONNECT " + addr + ":" + str(destport) + " HTTP/1.1\r\n" + "Host: " + destaddr + "\r\n\r\n")
|
||||
# We read the response until we get the string "\r\n\r\n"
|
||||
resp = self.recv(1)
|
||||
while resp.find("\r\n\r\n")==-1:
|
||||
resp = resp + self.recv(1)
|
||||
# We just need the first line to check if the connection
|
||||
# was successful
|
||||
statusline = resp.splitlines()[0].split(" ",2)
|
||||
if statusline[0] not in ("HTTP/1.0","HTTP/1.1"):
|
||||
self.close()
|
||||
raise GeneralProxyError((1,_generalerrors[1]))
|
||||
try:
|
||||
statuscode = int(statusline[1])
|
||||
except ValueError:
|
||||
self.close()
|
||||
raise GeneralProxyError((1,_generalerrors[1]))
|
||||
if statuscode != 200:
|
||||
self.close()
|
||||
raise HTTPError((statuscode,statusline[2]))
|
||||
self.__proxysockname = ("0.0.0.0",0)
|
||||
self.__proxypeername = (addr,destport)
|
||||
|
||||
def connect(self,destpair):
|
||||
"""connect(self,despair)
|
||||
Connects to the specified destination through a proxy.
|
||||
destpar - A tuple of the IP/DNS address and the port number.
|
||||
(identical to socket's connect).
|
||||
To select the proxy server use setproxy().
|
||||
"""
|
||||
# Do a minimal input check first
|
||||
if (type(destpair) in (list,tuple)==False) or (len(destpair)<2) or (type(destpair[0])!=str) or (type(destpair[1])!=int):
|
||||
raise GeneralProxyError((5,_generalerrors[5]))
|
||||
if self.__proxy[0] == PROXY_TYPE_SOCKS5:
|
||||
if self.__proxy[2] != None:
|
||||
portnum = self.__proxy[2]
|
||||
else:
|
||||
portnum = 1080
|
||||
_orgsocket.connect(self,(self.__proxy[1],portnum))
|
||||
self.__negotiatesocks5(destpair[0],destpair[1])
|
||||
elif self.__proxy[0] == PROXY_TYPE_SOCKS4:
|
||||
if self.__proxy[2] != None:
|
||||
portnum = self.__proxy[2]
|
||||
else:
|
||||
portnum = 1080
|
||||
_orgsocket.connect(self,(self.__proxy[1],portnum))
|
||||
self.__negotiatesocks4(destpair[0],destpair[1])
|
||||
elif self.__proxy[0] == PROXY_TYPE_HTTP:
|
||||
if self.__proxy[2] != None:
|
||||
portnum = self.__proxy[2]
|
||||
else:
|
||||
portnum = 8080
|
||||
_orgsocket.connect(self,(self.__proxy[1],portnum))
|
||||
self.__negotiatehttp(destpair[0],destpair[1])
|
||||
elif self.__proxy[0] == None:
|
||||
_orgsocket.connect(self,(destpair[0],destpair[1]))
|
||||
else:
|
||||
raise GeneralProxyError((4,_generalerrors[4]))
|
@ -25,5 +25,31 @@
|
||||
<file>nova/engines/piratebay.png</file>
|
||||
<file>nova/engines/vertor.py</file>
|
||||
<file>nova/engines/btdigg.png</file>
|
||||
<file>nova3/nova2.py</file>
|
||||
<file>nova3/novaprinter.py</file>
|
||||
<file>nova3/socks.py</file>
|
||||
<file>nova3/nova2dl.py</file>
|
||||
<file>nova3/helpers.py</file>
|
||||
<file>nova3/sgmllib3.py</file>
|
||||
<file>nova3/engines/vertor.png</file>
|
||||
<file>nova3/engines/kickasstorrents.png</file>
|
||||
<file>nova3/engines/mininova.png</file>
|
||||
<file>nova3/engines/mininova.py</file>
|
||||
<file>nova3/engines/torrentdownloads.png</file>
|
||||
<file>nova3/engines/isohunt.png</file>
|
||||
<file>nova3/engines/torrentreactor.py</file>
|
||||
<file>nova3/engines/btjunkie.png</file>
|
||||
<file>nova3/engines/extratorrent.py</file>
|
||||
<file>nova3/engines/piratebay.py</file>
|
||||
<file>nova3/engines/torrentdownloads.py</file>
|
||||
<file>nova3/engines/torrentreactor.png</file>
|
||||
<file>nova3/engines/isohunt.py</file>
|
||||
<file>nova3/engines/btdigg.py</file>
|
||||
<file>nova3/engines/btjunkie.py</file>
|
||||
<file>nova3/engines/kickasstorrents.py</file>
|
||||
<file>nova3/engines/extratorrent.png</file>
|
||||
<file>nova3/engines/piratebay.png</file>
|
||||
<file>nova3/engines/vertor.py</file>
|
||||
<file>nova3/engines/btdigg.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
</RCC>
|
||||
|
@ -144,7 +144,7 @@ void SearchEngine::installPython() {
|
||||
DownloadThread *pydownloader = new DownloadThread(this);
|
||||
connect(pydownloader, SIGNAL(downloadFinished(QString,QString)), this, SLOT(pythonDownloadSuccess(QString,QString)));
|
||||
connect(pydownloader, SIGNAL(downloadFailure(QString,QString)), this, SLOT(pythonDownloadFailure(QString,QString)));
|
||||
pydownloader->downloadUrl("http://python.org/ftp/python/2.7.1/python-2.7.1.msi");
|
||||
pydownloader->downloadUrl("http://python.org/ftp/python/2.7.2/python-2.7.2.msi");
|
||||
}
|
||||
|
||||
void SearchEngine::pythonDownloadSuccess(QString url, QString file_path) {
|
||||
@ -487,6 +487,7 @@ void SearchEngine::updateNova() {
|
||||
qDebug("Updating nova");
|
||||
// create nova directory if necessary
|
||||
QDir search_dir(misc::searchEngineLocation());
|
||||
QString nova_folder = misc::pythonVersion() >= 3 ? "nova3" : "nova";
|
||||
QFile package_file(search_dir.absoluteFilePath("__init__.py"));
|
||||
package_file.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
package_file.close();
|
||||
@ -498,39 +499,39 @@ void SearchEngine::updateNova() {
|
||||
package_file2.close();
|
||||
// Copy search plugin files (if necessary)
|
||||
QString filePath = search_dir.absoluteFilePath("nova2.py");
|
||||
if(getPluginVersion(":/nova/nova2.py") > getPluginVersion(filePath)) {
|
||||
if(getPluginVersion(":/"+nova_folder+"/nova2.py") > getPluginVersion(filePath)) {
|
||||
if(QFile::exists(filePath)) {
|
||||
misc::safeRemove(filePath);
|
||||
misc::safeRemove(filePath+"c");
|
||||
}
|
||||
QFile::copy(":/nova/nova2.py", filePath);
|
||||
QFile::copy(":/"+nova_folder+"/nova2.py", filePath);
|
||||
}
|
||||
|
||||
filePath = search_dir.absoluteFilePath("nova2dl.py");
|
||||
if(getPluginVersion(":/nova/nova2dl.py") > getPluginVersion(filePath)) {
|
||||
if(getPluginVersion(":/"+nova_folder+"/nova2dl.py") > getPluginVersion(filePath)) {
|
||||
if(QFile::exists(filePath)){
|
||||
misc::safeRemove(filePath);
|
||||
misc::safeRemove(filePath+"c");
|
||||
}
|
||||
QFile::copy(":/nova/nova2dl.py", filePath);
|
||||
QFile::copy(":/"+nova_folder+"/nova2dl.py", filePath);
|
||||
}
|
||||
|
||||
filePath = search_dir.absoluteFilePath("novaprinter.py");
|
||||
if(getPluginVersion(":/nova/novaprinter.py") > getPluginVersion(filePath)) {
|
||||
if(getPluginVersion(":/"+nova_folder+"/novaprinter.py") > getPluginVersion(filePath)) {
|
||||
if(QFile::exists(filePath)){
|
||||
misc::safeRemove(filePath);
|
||||
misc::safeRemove(filePath+"c");
|
||||
}
|
||||
QFile::copy(":/nova/novaprinter.py", filePath);
|
||||
QFile::copy(":/"+nova_folder+"/novaprinter.py", filePath);
|
||||
}
|
||||
|
||||
filePath = search_dir.absoluteFilePath("helpers.py");
|
||||
if(getPluginVersion(":/nova/helpers.py") > getPluginVersion(filePath)) {
|
||||
if(getPluginVersion(":/"+nova_folder+"/helpers.py") > getPluginVersion(filePath)) {
|
||||
if(QFile::exists(filePath)){
|
||||
misc::safeRemove(filePath);
|
||||
misc::safeRemove(filePath+"c");
|
||||
}
|
||||
QFile::copy(":/nova/helpers.py", filePath);
|
||||
QFile::copy(":/"+nova_folder+"/helpers.py", filePath);
|
||||
}
|
||||
|
||||
filePath = search_dir.absoluteFilePath("socks.py");
|
||||
@ -538,9 +539,18 @@ void SearchEngine::updateNova() {
|
||||
misc::safeRemove(filePath);
|
||||
misc::safeRemove(filePath+"c");
|
||||
}
|
||||
QFile::copy(":/nova/socks.py", filePath);
|
||||
QFile::copy(":/"+nova_folder+"/socks.py", filePath);
|
||||
|
||||
if (nova_folder == "nova3") {
|
||||
filePath = search_dir.absoluteFilePath("sgmllib3.py");
|
||||
if(QFile::exists(filePath)){
|
||||
misc::safeRemove(filePath);
|
||||
misc::safeRemove(filePath+"c");
|
||||
}
|
||||
QFile::copy(":/"+nova_folder+"/sgmllib3.py", filePath);
|
||||
}
|
||||
QDir destDir(QDir(misc::searchEngineLocation()).absoluteFilePath("engines"));
|
||||
QDir shipped_subDir(":/nova/engines/");
|
||||
QDir shipped_subDir(":/"+nova_folder+"/engines/");
|
||||
QStringList files = shipped_subDir.entryList();
|
||||
foreach(const QString &file, files){
|
||||
QString shipped_file = shipped_subDir.absoluteFilePath(file);
|
||||
|