From f4c2fdd2fdc41d81b6af1cd9bae4d2cde58fbea9 Mon Sep 17 00:00:00 2001 From: Christophe Dumez Date: Sun, 11 Sep 2011 20:22:54 +0300 Subject: [PATCH] Search: Initial support for Python 3.x --- src/misc.cpp | 30 +- src/misc.h | 1 + src/preferences/preferences.h | 21 +- src/searchengine/engineselectdlg.cpp | 2 +- src/searchengine/nova3/__init__.py | 0 src/searchengine/nova3/engines/__init__.py | 0 src/searchengine/nova3/engines/btdigg.png | Bin 0 -> 692 bytes src/searchengine/nova3/engines/btdigg.py | 66 +++ src/searchengine/nova3/engines/btjunkie.png | Bin 0 -> 622 bytes src/searchengine/nova3/engines/btjunkie.py | 122 ++++++ .../nova3/engines/extratorrent.png | Bin 0 -> 605 bytes .../nova3/engines/extratorrent.py | 116 ++++++ src/searchengine/nova3/engines/isohunt.png | Bin 0 -> 633 bytes src/searchengine/nova3/engines/isohunt.py | 69 ++++ .../nova3/engines/kickasstorrents.png | Bin 0 -> 787 bytes .../nova3/engines/kickasstorrents.py | 71 ++++ src/searchengine/nova3/engines/mininova.png | Bin 0 -> 365 bytes src/searchengine/nova3/engines/mininova.py | 114 +++++ src/searchengine/nova3/engines/piratebay.png | Bin 0 -> 609 bytes src/searchengine/nova3/engines/piratebay.py | 113 +++++ .../nova3/engines/torrentdownloads.png | Bin 0 -> 423 bytes .../nova3/engines/torrentdownloads.py | 141 +++++++ .../nova3/engines/torrentreactor.png | Bin 0 -> 529 bytes .../nova3/engines/torrentreactor.py | 107 +++++ src/searchengine/nova3/engines/vertor.png | Bin 0 -> 643 bytes src/searchengine/nova3/engines/vertor.py | 129 ++++++ src/searchengine/nova3/helpers.py | 99 +++++ src/searchengine/nova3/nova2.py | 158 +++++++ src/searchengine/nova3/nova2dl.py | 64 +++ src/searchengine/nova3/novaprinter.py | 70 ++++ src/searchengine/nova3/socks.py | 391 ++++++++++++++++++ src/searchengine/search.qrc | 28 +- src/searchengine/searchengine.cpp | 32 +- 33 files changed, 1918 insertions(+), 26 deletions(-) create mode 100644 src/searchengine/nova3/__init__.py create mode 100644 src/searchengine/nova3/engines/__init__.py create mode 100644 src/searchengine/nova3/engines/btdigg.png create mode 100644 src/searchengine/nova3/engines/btdigg.py create mode 100644 src/searchengine/nova3/engines/btjunkie.png create mode 100644 src/searchengine/nova3/engines/btjunkie.py create mode 100644 src/searchengine/nova3/engines/extratorrent.png create mode 100755 src/searchengine/nova3/engines/extratorrent.py create mode 100644 src/searchengine/nova3/engines/isohunt.png create mode 100644 src/searchengine/nova3/engines/isohunt.py create mode 100644 src/searchengine/nova3/engines/kickasstorrents.png create mode 100755 src/searchengine/nova3/engines/kickasstorrents.py create mode 100644 src/searchengine/nova3/engines/mininova.png create mode 100644 src/searchengine/nova3/engines/mininova.py create mode 100644 src/searchengine/nova3/engines/piratebay.png create mode 100644 src/searchengine/nova3/engines/piratebay.py create mode 100644 src/searchengine/nova3/engines/torrentdownloads.png create mode 100644 src/searchengine/nova3/engines/torrentdownloads.py create mode 100644 src/searchengine/nova3/engines/torrentreactor.png create mode 100644 src/searchengine/nova3/engines/torrentreactor.py create mode 100644 src/searchengine/nova3/engines/vertor.png create mode 100755 src/searchengine/nova3/engines/vertor.py create mode 100644 src/searchengine/nova3/helpers.py create mode 100755 src/searchengine/nova3/nova2.py create mode 100755 src/searchengine/nova3/nova2dl.py create mode 100644 src/searchengine/nova3/novaprinter.py create mode 100644 src/searchengine/nova3/socks.py diff --git a/src/misc.cpp b/src/misc.cpp index d345460a4..eeaf93b2e 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #ifdef DISABLE_GUI #include @@ -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()); diff --git a/src/misc.h b/src/misc.h index df4f30190..f7865734d 100644 --- a/src/misc.h +++ b/src/misc.h @@ -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(); diff --git a/src/preferences/preferences.h b/src/preferences/preferences.h index 57849b8b2..ef672e199 100644 --- a/src/preferences/preferences.h +++ b/src/preferences/preferences.h @@ -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; } diff --git a/src/searchengine/engineselectdlg.cpp b/src/searchengine/engineselectdlg.cpp index fe4142000..7977dae92 100644 --- a/src/searchengine/engineselectdlg.cpp +++ b/src/searchengine/engineselectdlg.cpp @@ -45,7 +45,7 @@ #include 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); diff --git a/src/searchengine/nova3/__init__.py b/src/searchengine/nova3/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/searchengine/nova3/engines/__init__.py b/src/searchengine/nova3/engines/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/searchengine/nova3/engines/btdigg.png b/src/searchengine/nova3/engines/btdigg.png new file mode 100644 index 0000000000000000000000000000000000000000..2823b9ae1ad69834125d46848676bff6e6ebf75b GIT binary patch literal 692 zcmV;l0!#ggP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2ipV+ z2qQ99eRr_{00J~gL_t(I%Z-vxXp(Ul$G`9E+}?}EDhOGyV{{2Zs6j+sVj(pI!9yUl z%OF^h$I&5Jbr>WJy420ZTXd>mMX*4Y1g7mYA(UJp%r*5z(fM!l_deea#$aT#&*cX{ zcs|dU=Xt_XHx(XYt_a1f0Ka;>+hXKm!IQ*r~NZM;(#-9@Mvh$1nEq=wTR_ zPbDyNa&B+}*AXX%oyX8%NQJ>j-UIdUhdgD$@QV5D_jcEHIzXGndP8Wo3n?X)>8i zG8pjfXX*z?G8T)mTCFml&oh(BaAszPu~>}3U~s>*-QMjh&;g)Qsi4#8Kt!;-yo~AT zX_%%7zu%8<>sg2>IGv8ZThdag6un+A3WWkh1dT=m>2w<7~y>Iy#C%p@4F^3|-d|3WfgJ^?;z&YSAzZlu9LNng+Mq4Z|?- azx)O#+0=-^3MYa90000 +# +# 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]) diff --git a/src/searchengine/nova3/engines/btjunkie.png b/src/searchengine/nova3/engines/btjunkie.png new file mode 100644 index 0000000000000000000000000000000000000000..a3161758079ca0ccee7452294e9945d17c22949c GIT binary patch literal 622 zcmV-!0+IcRP)FyLY`R z6Qf`U*`ahZ)7%6vL3RqlgH}X(C=j|-l2N3W2s*S&6c~{aK^LJ2PpNcmRHrT@rUP+X zlbFgJ8#8rfxZnG9kiE{5-t1JcVG zN|>CQ0-#td{?R&`UHFBH;DI8e%B zwXlK1S+!-M{p@|Y^JqduM7nzV1WmV7_BA!xatDL2#DD6poF9I^Gek=F0468KsdD=< z_EZB9kHu(g^Z}rhs<5F07>3SNC_>{Qf z-r5@44|@nSd-zsZV}E-;rfZ))I6V_(E|~%CHwu&`bar?dyLXY-qXYDxIf|t=IdtL% z{wN@O3a~;b424$SY)w&?)5m30=D^L(03v6T5rt;^(6Zzm6L^&uiymc`@I zCoH9NtgL+}mQ3$Z@y9n+mSy)=SuY~e-QBbO$|AyU5Dtg=2^-y+reRWb_y7O^07*qo IM6N<$g2v?%s{jB1 literal 0 HcmV?d00001 diff --git a/src/searchengine/nova3/engines/btjunkie.py b/src/searchengine/nova3/engines/btjunkie.py new file mode 100644 index 000000000..7df399a78 --- /dev/null +++ b/src/searchengine/nova3/engines/btjunkie.py @@ -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 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 + diff --git a/src/searchengine/nova3/engines/extratorrent.png b/src/searchengine/nova3/engines/extratorrent.png new file mode 100644 index 0000000000000000000000000000000000000000..e5e84571881547a82934e344287255c6e94adc06 GIT binary patch literal 605 zcmV-j0;2tiP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igo8 z3MmdNO!z4P00G@eL_t(I%bk-wXjDNIg}*!V^5Vu71!E(TR2sDii4dckuqhNlD|@?u zsD%=26txy@G{z#@2tq)ER#q0-hze^2MFsH(Nns&m-9X%EcKylDwV3yw`}_gf3j;HA zFXx^)=Z;JlW>nPyf{5@xp{jr)VhB_no40h*OuTvho&W@63;_2Z7J2&QG1eHIDpmwU zqK-a#dixm4=c1Dk#2Zkazk18zqa*+9c;)gakBSc%$`1e@GypJGA`l_7W-S2Ymo8HI zv_RMrA`y#)&sDm&b~V(;0Adm%9~Y|tJbUqy)5njbgxk2OD{h=b0AP&;pv5TjUmO6# zBPW=+aqU-O7?SHRkslnOH4M`L#(Dr}pZ z4VD+@nVo%yK~TV{BfDxf8#+2ta}hm3VyUV9@X0;L&T(~Ol%-lVDb8ftIIw>&{e9bh zbHVy~GS>37wv;5ZyAP6WpkJIEXY$r{?0!EA=%f`hy+$O00000NkvXXu0mjfksts* literal 0 HcmV?d00001 diff --git a/src/searchengine/nova3/engines/extratorrent.py b/src/searchengine/nova3/engines/extratorrent.py new file mode 100755 index 000000000..522e224b8 --- /dev/null +++ b/src/searchengine/nova3/engines/extratorrent.py @@ -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).*') + 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 + diff --git a/src/searchengine/nova3/engines/isohunt.png b/src/searchengine/nova3/engines/isohunt.png new file mode 100644 index 0000000000000000000000000000000000000000..e71fb1ca23ff886c2bd8e656d31034943c8ec568 GIT binary patch literal 633 zcmV-<0*3vGP)K z^Us`Ho5TIH?PpHs)1`i}qMAL&FVE-2=izxC_>UD$tjf@GLIRX-H~ea3Lb}gf$6IG3 zwVIL2c7bp@gQl%>w97?BxkczeG@h3CpS+Q8$EM}qfWe`6>@bvZps5i69e6P^PXB{v ze3%OSG5mQUB7@NcjSbb<$_<6U=wt|!s?zFp;9pvihcDcg=LR0hrC3sL6iuXvrLr9H zI_W*uA#`9R{Z-B_gfN@RXmjmh+8IWO0OlfLa`{cXP8(LUN+uZx zz-%@G0ubc4B-yofdOF<5Eead>7goq>IS%dj5QrpLjHGFD*%`Wb1#d&O02m(g$%{80 zqpAw6UWa}xDad)|s3Q z;IvyYs0OBH!^DyqTn>A|#>?kVh?ftqiSAC9C;+M|EaKMHK2G;^;;P*(KKX(GR9a0% zz06X8zT@q>8q+iY_BpGI0)G1oD$vzjkH=k86d-;bWVJP!+mNWn63hmJ_!saU_r<`@ T=4)dh00000NkvXXu0mjfO$8z7 literal 0 HcmV?d00001 diff --git a/src/searchengine/nova3/engines/isohunt.py b/src/searchengine/nova3/engines/isohunt.py new file mode 100644 index 000000000..3428b82a3 --- /dev/null +++ b/src/searchengine/nova3/engines/isohunt.py @@ -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.*?.*?[^/]+).*?' + '>(?P.*?).*?' + '>(?P[\d,\.]+\s+MB).*?' + '>(?P\d+).*?' + '>(?P\d+)') + 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 diff --git a/src/searchengine/nova3/engines/kickasstorrents.png b/src/searchengine/nova3/engines/kickasstorrents.png new file mode 100644 index 0000000000000000000000000000000000000000..7ddc99353881dbc741e1dad0acce65517089f9ec GIT binary patch literal 787 zcmV+u1MK{XP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXf7 z7cwRU6Xi*|XNi}Jiql1rh9B0&dIOkrcgqVI8e>?AWu(Q|0*e8$F^1E|XuVER{uuza7oQ+~>u$VmCta;KR&iT1tSOacg$auR zW<#mEWR@hE_{1%!Y8YEu0RW@UkEmK{IeOmIGcoE|7CQU7@JtTknHWUVDMGNYpvp49 zTp%J`_L*fyJ7!iXRx78gFj@;gJEF+xuLpwwV6B zOl!6_VcI=RVsa+g`TNPp+Jy)Qc~V!k`~ugA_W^*U0mGx-N`EjE5sE-4g7>c*5aA#? zokY;@rKRy28u#1fnQnaUxV~zY0V?cS^*ak}r?lQ;O~YbM#WnB=RbBjtxqw*c>HSmE z(&6k}7XVOkIJ0S6uC+|kt)rOr3-$AN_tBP)p(YK}uN8gk_cR&}`eBn%f6y2$$&pBe zNF>5fr|*|zV0uSer>lGIM*g?#CF$p5&9dF&4WE7aZlrf3&0omuEd`|WoEQKA002ov RPDHLkV1f!D2_PT|APPpGRqFr% literal 0 HcmV?d00001 diff --git a/src/searchengine/nova3/engines/kickasstorrents.py b/src/searchengine/nova3/engines/kickasstorrents.py new file mode 100755 index 000000000..7593da58f --- /dev/null +++ b/src/searchengine/nova3/engines/kickasstorrents.py @@ -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 + diff --git a/src/searchengine/nova3/engines/mininova.png b/src/searchengine/nova3/engines/mininova.png new file mode 100644 index 0000000000000000000000000000000000000000..1513376b66dd7e8e99e33b045c9932e0bd827679 GIT binary patch literal 365 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G`DAk4@xYmNj^kiEpy*OmP`hdjHqtjR3vpFp8so-U3d7N?I+-s^QZL8NW|v*Xhy zxas(o(i_PTskNYV#cU??zny85}S Ib4q9e09Q_ud;kCd literal 0 HcmV?d00001 diff --git a/src/searchengine/nova3/engines/mininova.py b/src/searchengine/nova3/engines/mininova.py new file mode 100644 index 000000000..9eac5f4fe --- /dev/null +++ b/src/searchengine/nova3/engines/mininova.py @@ -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)

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 + diff --git a/src/searchengine/nova3/engines/piratebay.png b/src/searchengine/nova3/engines/piratebay.png new file mode 100644 index 0000000000000000000000000000000000000000..3ab97883d3571e609a5278ff37c56d3c040d78d6 GIT binary patch literal 609 zcmV-n0-pVeP)4;^@v00062NklX{L z>+380ih8{sDmM-+yCIQCgb>2>JR!t!8~_M{01@~0_Cmpr0EI%~{r&y!?(XH~Wj>!j zJUqO;y*)lYZjZ-q2jw_U5CqTj+U<5U8ufj@)oN*)7W({~2@#*3p29MbNNjt?Vljqc z0H9i}dY*@fyF0OMJDbh!@9zVED2lG@5<&pr`}=z{!(y>e6vg-bu#+syk|ZUQNdUOG zxR7O;=Xn4)K0anymJmXX$K$K3t4^nLaBz@Lr+J=VtyaG8hfz6>qiLF^=}M*2@As$E zDU-=$CX-3{cgy9HVHio0E-x=jrBbn2JUKa8uh)iQsH)oSb~%othQr}tFwiuu*=&A% zd^|rtTb6Zrc$m-UP1AH;_w4NK^z`)W>&rAvLI{P3rfHs^pC^;aTrPKXbQF)rMNtfb zU_PJgx-JMpxm@n`de_(2_xJaJh>qi=QYl4IY}>}4ux(pa)!W-!0GQ2YhzN-2`~GM& vvMdY#1H&+Cwc6+BCn8e6ob=Akh8O+^O42Py1rH4t00000NkvXXu0mjf8w?xe literal 0 HcmV?d00001 diff --git a/src/searchengine/nova3/engines/piratebay.py b/src/searchengine/nova3/engines/piratebay.py new file mode 100644 index 000000000..ae862ac79 --- /dev/null +++ b/src/searchengine/nova3/engines/piratebay.py @@ -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 diff --git a/src/searchengine/nova3/engines/torrentdownloads.png b/src/searchengine/nova3/engines/torrentdownloads.png new file mode 100644 index 0000000000000000000000000000000000000000..ce14cea487d289ace537cb4139b8f5080e39ee47 GIT binary patch literal 423 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85p>QL70(Y)*K0-AbW|YuPgga9zJ1R#dR;W-U3Zg_jGX#u{gbS zvcDIzp~TVosbcdEuVw$hbdL428MU*)fpNNzF=Wk_0z3E{OFv^)dlAm8J^`- z)K&OAE92wWdh2>oSx_X$v;5pa%l0N-7daQX+tt4nK2_}6yShe2NchP@OGmjiq0>2y z^qXulm=qSZ24;5$$ug!kEwxT~mht4)I`3W6625Z$-Mm`sRsO7y@P-9lO}FclXQq`I zvN7cJ>S@;pxE|Z}tLN*s%9?pSujBd{vj1w-Uu%k5bFBaS_H(RXnO{}0yj{NJwJ|Uh O89ZJ6T-G@yGywp^OsDMt literal 0 HcmV?d00001 diff --git a/src/searchengine/nova3/engines/torrentdownloads.py b/src/searchengine/nova3/engines/torrentdownloads.py new file mode 100644 index 000000000..6bb3e2b4b --- /dev/null +++ b/src/searchengine/nova3/engines/torrentdownloads.py @@ -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)
.*') + 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 + diff --git a/src/searchengine/nova3/engines/torrentreactor.png b/src/searchengine/nova3/engines/torrentreactor.png new file mode 100644 index 0000000000000000000000000000000000000000..480c0b32e309093c46a31f61d36771d84dd3987a GIT binary patch literal 529 zcmV+s0`C2ZP)yvXtq_B{tDFBnn1fa}qnW57$ zesqjIep;xSjtQe7HDL4dEi4ubcXu~4?{qre@Auu^J?ZP50mdHHP6c7NCO z`F!pg#}S*&HOu94S7v_0&E~q&+E$I36*Eu`#PNP(9vu`7z-ImjfcV#U!f1#e9g`$U ziC4yf7aNe@KY4WVVh3A4e+3$dR72$*s0KuJuf96#VNae>PZHb=-5yE^QPm}tYg?W5 z3JX`4)RP3ubKD&j5kzF~!bAk_l<9O#t 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 diff --git a/src/searchengine/nova3/engines/vertor.png b/src/searchengine/nova3/engines/vertor.png new file mode 100644 index 0000000000000000000000000000000000000000..f9975f508bb42ca0e328fe3175c20491c43e998c GIT binary patch literal 643 zcmV-}0(||6P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXf7 z6Brt3GXU z0I$HC@ETmZaN)|tt*sxix{-oJt3_yvEm#K2R2V*_z__3p90#4%n=|kE<(zk(5gu(l z(2~JB1YHpTN@{`iy(f%&!tOZN=57J-qy3%d-yUN<>Qvb%KjUunJ^=M@ji>pCV}YCD zdphv(@GaF=2@zmZexWyo+%e{-mQ0(iqQuJV4FEJWveh>LL!hJLk@e)(0Tw zUuIJB7=SA?NxjZgU+J|D_>;`Ay2Kf5(PoW%FPK&RK#V=5qE5r>D|R#LQks`(!QSPkAbEVhqT`dS~$$!7$p dwo9fyfZx~Nxu{`V$ZP-r002ovPDHLkV1gdb5O@Fp literal 0 HcmV?d00001 diff --git a/src/searchengine/nova3/engines/vertor.py b/src/searchengine/nova3/engines/vertor.py new file mode 100755 index 000000000..b886baddb --- /dev/null +++ b/src/searchengine/nova3/engines/vertor.py @@ -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)

.*') + 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 + diff --git a/src/searchengine/nova3/helpers.py b/src/searchengine/nova3/helpers.py new file mode 100644 index 000000000..18e7510e6 --- /dev/null +++ b/src/searchengine/nova3/helpers.py @@ -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[^:]+):(?P[^@]+)@)?(?P[^:]+):(?P\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 diff --git a/src/searchengine/nova3/nova2.py b/src/searchengine/nova3/nova2.py new file mode 100755 index 000000000..9f546b3c3 --- /dev/null +++ b/src/searchengine/nova3/nova2.py @@ -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 +# Contributors: +# Christophe Dumez (qbittorrent integration) +# Thanks to gab #gcu @ irc.freenode.net (multipage support on PirateBay) +# Thanks to Elias (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 += "%s\n"%search_engine.name + xml += "%s\n"%search_engine.url + xml += "" + if hasattr(search_engine, 'supported_categories'): + supported_categories = list(search_engine.supported_categories.keys()) + supported_categories.remove('all') + xml += " ".join(supported_categories) + xml += "\n" + xml += "\n"%short_name + return xml + +def displayCapabilities(): + """ + Display capabilities in XML format + + + long name + http://example.com + movies music games + + + """ + xml = "" + for short_name in supported_engines: + xml += engineToXml(short_name) + xml += "" + 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]*] \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]*] \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() diff --git a/src/searchengine/nova3/nova2dl.py b/src/searchengine/nova3/nova2dl.py new file mode 100755 index 000000000..b04974966 --- /dev/null +++ b/src/searchengine/nova3/nova2dl.py @@ -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) \ No newline at end of file diff --git a/src/searchengine/nova3/novaprinter.py b/src/searchengine/nova3/novaprinter.py new file mode 100644 index 000000000..8a29accfd --- /dev/null +++ b/src/searchengine/nova3/novaprinter.py @@ -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) diff --git a/src/searchengine/nova3/socks.py b/src/searchengine/nova3/socks.py new file mode 100644 index 000000000..d6b61fd97 --- /dev/null +++ b/src/searchengine/nova3/socks.py @@ -0,0 +1,391 @@ +"""SocksiPy - Python SOCKS module. +Version 1.01 + +Copyright 2006 Dan-Haim. All rights reserved. +Various fixes by Christophe DUMEZ - 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])) diff --git a/src/searchengine/search.qrc b/src/searchengine/search.qrc index 03f94024e..b7f2ba886 100644 --- a/src/searchengine/search.qrc +++ b/src/searchengine/search.qrc @@ -25,5 +25,31 @@ nova/engines/piratebay.png nova/engines/vertor.py nova/engines/btdigg.png + nova3/nova2.py + nova3/novaprinter.py + nova3/socks.py + nova3/nova2dl.py + nova3/helpers.py + nova3/sgmllib3.py + nova3/engines/vertor.png + nova3/engines/kickasstorrents.png + nova3/engines/mininova.png + nova3/engines/mininova.py + nova3/engines/torrentdownloads.png + nova3/engines/isohunt.png + nova3/engines/torrentreactor.py + nova3/engines/btjunkie.png + nova3/engines/extratorrent.py + nova3/engines/piratebay.py + nova3/engines/torrentdownloads.py + nova3/engines/torrentreactor.png + nova3/engines/isohunt.py + nova3/engines/btdigg.py + nova3/engines/btjunkie.py + nova3/engines/kickasstorrents.py + nova3/engines/extratorrent.png + nova3/engines/piratebay.png + nova3/engines/vertor.py + nova3/engines/btdigg.png - \ No newline at end of file + diff --git a/src/searchengine/searchengine.cpp b/src/searchengine/searchengine.cpp index 913131348..86ea52899 100644 --- a/src/searchengine/searchengine.cpp +++ b/src/searchengine/searchengine.cpp @@ -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);