Compare commits
13 Commits
release-2.
...
release-2.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
df60032134 | ||
![]() |
bb0b3ead41 | ||
![]() |
6d5c16e10f | ||
![]() |
6ee385bdd8 | ||
![]() |
083cab3118 | ||
![]() |
30b7927ee5 | ||
![]() |
b09f377e25 | ||
![]() |
54fb4cd758 | ||
![]() |
adaa2c1ddb | ||
![]() |
f0da72c071 | ||
![]() |
83932a86a4 | ||
![]() |
644a4d0062 | ||
![]() |
3b0f80e219 |
@@ -1,3 +1,11 @@
|
||||
* Mon Sep 12 2011 - Christophe Dumez <chris@qbittorrent.org> - v2.8.5
|
||||
- BUGFIX: Fix encryption setting saving in Web UI
|
||||
- BUGFIX: Fix "Copy torrents to" setting saving in Web UI
|
||||
- BUGFIX: Announce to all trackers in a tier to avoid stalling issues
|
||||
- BUGFIX: Make search engine work with Python 3.x
|
||||
- BUGFIX: Fix search engine plugins update tool
|
||||
- BUGFIX: Fix KickAssTorrents search plugin
|
||||
|
||||
* Tue Aug 09 2011 - Christophe Dumez <chris@qbittorrent.org> - v2.8.4
|
||||
- BUGFIX: Added back ability to reorder trackers
|
||||
- BUGFIX: Do not announce to all trackers in the same tier to comply
|
||||
|
@@ -47,7 +47,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>2.8.4</string>
|
||||
<string>2.8.5</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
|
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;
|
||||
}
|
||||
|
@@ -392,8 +392,9 @@ void QBtSession::configureSession() {
|
||||
//sessionSettings.announce_to_all_trackers = true;
|
||||
sessionSettings.auto_scrape_interval = 1200; // 20 minutes
|
||||
#if LIBTORRENT_VERSION_MINOR > 14
|
||||
// Comply with the multi-tracker specification
|
||||
sessionSettings.announce_to_all_trackers = false;
|
||||
// XXX: Does not comply with the multi-tracker specification
|
||||
// but avoids stalling issues with libtorrent
|
||||
sessionSettings.announce_to_all_trackers = true;
|
||||
sessionSettings.announce_to_all_tiers = false;
|
||||
sessionSettings.auto_scrape_min_interval = 900; // 15 minutes
|
||||
#endif
|
||||
|
@@ -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);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#VERSION: 1.2
|
||||
#VERSION: 1.21
|
||||
#AUTHORS: Christophe Dumez (chris@qbittorrent.org)
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@@ -31,9 +31,9 @@ from helpers import retrieve_url, download_file
|
||||
import json
|
||||
|
||||
class kickasstorrents(object):
|
||||
url = 'http://www.kickasstorrents.com'
|
||||
url = 'http://www.kat.ph'
|
||||
name = 'kickasstorrents'
|
||||
supported_categories = {'all': 'all', 'movies': 'movies', 'tv': 'tv', 'music': 'music', 'games': 'games', 'software': 'applications'}
|
||||
supported_categories = {'all': '', 'movies': 'Movies', 'tv': 'TV', 'music': 'Music', 'games': 'Games', 'software': 'Applications'}
|
||||
|
||||
def __init__(self):
|
||||
self.results = []
|
||||
@@ -46,16 +46,17 @@ class kickasstorrents(object):
|
||||
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]))
|
||||
json_data = retrieve_url(self.url+'/json.php?q=%s&page=%d'%(what, i))
|
||||
try:
|
||||
json_dict = json.loads(json_data)
|
||||
except:
|
||||
i += 1
|
||||
continue
|
||||
if json_dict['total_results'] <= 0: return
|
||||
if int(json_dict['total_results']) <= 0: return
|
||||
results = json_dict['list']
|
||||
for r in results:
|
||||
try:
|
||||
if cat != 'all' and self.supported_categories[cat] != r['category']: continue
|
||||
res_dict = dict()
|
||||
res_dict['name'] = r['title']
|
||||
res_dict['size'] = str(r['size'])
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#VERSION: 1.40
|
||||
#VERSION: 1.41
|
||||
#AUTHORS: Fabien Devaux (fab@gnux.info)
|
||||
#CONTRIBUTORS: Christophe Dumez (chris@qbittorrent.org)
|
||||
|
||||
@@ -105,8 +105,7 @@ class piratebay(object):
|
||||
while True and i<11:
|
||||
results = []
|
||||
parser = self.SimpleSGMLParser(results, self.url)
|
||||
dat = retrieve_url(self.url+'/search/%s/%u/7/%s' % (what, i, self.supported_categories[cat]))
|
||||
print self.url+'/search/%s/%u/7/%s' % (what, i, self.supported_categories[cat])
|
||||
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:
|
||||
|
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 |
72
src/searchengine/nova3/engines/kickasstorrents.py
Executable file
@@ -0,0 +1,72 @@
|
||||
#VERSION: 1.21
|
||||
#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.kat.ph'
|
||||
name = 'kickasstorrents'
|
||||
supported_categories = {'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+'/json.php?q=%s&page=%d'%(what, i))
|
||||
try:
|
||||
json_dict = json.loads(json_data)
|
||||
except:
|
||||
i += 1
|
||||
continue
|
||||
if int(json_dict['total_results']) <= 0: return
|
||||
results = json_dict['list']
|
||||
for r in results:
|
||||
try:
|
||||
if cat != 'all' and self.supported_categories[cat] != r['category']: continue
|
||||
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, "wb")
|
||||
# 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] == b'\x1f\x8b':
|
||||
# Data is gzip encoded, decode it
|
||||
compressedstream = io.BytesIO(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
|