commit 277feac742ff141fb46585d0a44ecd6e7505b75a Author: Chris Punches Date: Sun Oct 7 23:08:35 2018 -0400 first commit, ARGGGGGH! diff --git a/README.md b/README.md new file mode 100644 index 0000000..c0bc31a --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# Scallywag +Any swashbuckler's favorite tool. + +# Purpose +A piratebay browser that deliberately circumvents processor-sharing exploitation being sent to browsers. + +# Not Purpose +Not a tool to steal things with. Magnet links and torrents are a perfectly valid technology and must be used responsibly by the user. diff --git a/Scallywag b/Scallywag new file mode 100755 index 0000000..ad8f8a8 --- /dev/null +++ b/Scallywag @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 + +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk, Gdk + +from rsrc.Scraper import proxylister, searcher + +from configparser import ConfigParser + +import subprocess + +class Config: + def __init__(self, config_file): + settings = ConfigParser(allow_no_value=True) + settings.read(config_file) + + # the url used to list out proxies + self.proxylist_url = settings.get("list_source", "piratebay_proxy_list") + + +class Scallywag: + def on_btnRefresh_clicked( self, object, data=None ): + self.status("refresh pressed") + + def on_btnSearch_clicked( self, object, data=None ): + self.status("search pressed") + + + + + # clear the results_store + # seeders, leechers, size, name + self.results_store.clear() + + for row in self.results_store: + self.results_store.remove(row) + + # prove to me that the results_store is populated + for row in self.results_store: + print(row[:]) + + for column in self.results_tree_view.get_columns(): + self.results_tree_view.remove_column(column) + + # associate new results_store with the treeview + self.results_tree_view.set_model(self.results_store) + + # add search result entries to the results store + # TODO add retrieval from search textentry + search_field = self.builder.get_object("txtSearch") + search_terms = search_field.get_text() + + for result in self.get_results( search_terms ): + print( result.__str__() ) + self.results_store.append( [ result.seeders, result.leechers, result.size, result.title, result.author, result.url ]) + + # prove to me that the results_store is populated + for row in self.results_store: + print(row[:]) + + for i, col_title in enumerate( [ "Seeders", "Leechers", "Size", "Title", "Author", "Url" ] ): + # renderer creation + renderer = Gtk.CellRendererText() + + # text is column number + column = Gtk.TreeViewColumn(col_title, renderer, text=i) + + # add column to tvw + self.results_tree_view.append_column(column) + + + + def get_current_proxy(self): + return self.mnuPulldown.get_active_text() + + def on_mnuPulldown_changed(self, object, data=None ): + new_proxy = self.get_current_proxy() + self.config.proxy = new_proxy + + self.status( str.format( "Changed PirateBay proxy site to {0}", self.config.proxy ) ) + + + def get_results(self, search_terms): + + self.searcher = searcher.Scraper( self.config ) + + results = self.searcher.get_results( search_terms ) + + for result in results: + yield result + + + def on_btnDownload_clicked( self, object, data=None, **args ): + selection = self.results_tree_view.get_selection() + (tm, ti) = selection.get_selected() + url = tm.get_value(ti, 5) + # TODO scrape url for magnet link then pass to xdg-open + + magnet = self.searcher.get_magnet( url ) + + subprocess.call(["xdg-open", magnet]) + + self.status("Opening Magnet with xdg-open...") + + def status( self, msg ): + # GTK team, your API is bullshit. + self.statusbar.push( self.statusbar.get_context_id("stsBar"), msg ) + + def on_winMain_destroy(self, object, data=None): + print("quit by exiting") + Gtk.main_quit() + + def __init__(self, configFile): + self.config = Config( configFile ) + + self.gladefile = "./rsrc/gui/winMain.glade" + self.builder = Gtk.Builder() + self.builder.add_from_file(self.gladefile) + self.builder.connect_signals(self) + self.window = self.builder.get_object("winMain") + self.statusbar = self.builder.get_object("stsBar") + + self.status("Choose your PirateBay Proxy") + + self.mnuPulldown = self.builder.get_object("mnuPulldown") + + self.proxy_store = Gtk.ListStore(str) + + self.mnuPulldown.set_model(self.proxy_store) + + self.proxylister = proxylister.Scraper( self.config ) + for proxy in self.proxylister.get_proxies(): + self.proxy_store.append([proxy]) + + self.mnuPulldown.set_active(0) + + + self.results_store = Gtk.ListStore(int, int, str, str, str, str) + + self.window.set_focus(self.builder.get_object("txtSearch")) + + # treeview + self.results_tree_view = self.builder.get_object("tvwResults") + + # wtf. why doesn't this work? + self.results_tree_view.connect("row-activated", self.on_btnDownload_clicked ) + + self.window.show_all() + + + +if __name__ == "__main__": + main = Scallywag("config.ini") + + Gtk.main() diff --git a/config.ini b/config.ini new file mode 100644 index 0000000..a3b3b8e --- /dev/null +++ b/config.ini @@ -0,0 +1,2 @@ +[list_source] +piratebay_proxy_list = thepiratebay-proxylist.se diff --git a/rsrc/Scraper/__init__.py b/rsrc/Scraper/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rsrc/Scraper/__pycache__/__init__.cpython-36.pyc b/rsrc/Scraper/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000..08dedf6 Binary files /dev/null and b/rsrc/Scraper/__pycache__/__init__.cpython-36.pyc differ diff --git a/rsrc/Scraper/__pycache__/proxylister.cpython-36.pyc b/rsrc/Scraper/__pycache__/proxylister.cpython-36.pyc new file mode 100644 index 0000000..58896eb Binary files /dev/null and b/rsrc/Scraper/__pycache__/proxylister.cpython-36.pyc differ diff --git a/rsrc/Scraper/__pycache__/searcher.cpython-36.pyc b/rsrc/Scraper/__pycache__/searcher.cpython-36.pyc new file mode 100644 index 0000000..e69a64b Binary files /dev/null and b/rsrc/Scraper/__pycache__/searcher.cpython-36.pyc differ diff --git a/rsrc/Scraper/proxylister.py b/rsrc/Scraper/proxylister.py new file mode 100644 index 0000000..9c4ad97 --- /dev/null +++ b/rsrc/Scraper/proxylister.py @@ -0,0 +1,33 @@ +import requests +from lxml import html + + +class Scraper: + def __init__( self, config ): + # the request client, belongs to session even if no "user session" is needed + self.client = requests.Session() + self.config = config + + def get_proxies(self): + fetch_results = self.client.get( "https://" + self.config.proxylist_url ) + proxy_list = self.Parser.scrape( "proxy_list", fetch_results.content ) + return proxy_list + + class Parser: + @staticmethod + def scrape( datapoint, text ): + cases = { + "proxy_list": Scraper.Parser.proxy_list + } + return cases[ datapoint ]( text ) + + @staticmethod + def proxy_list( text ): + proxyTable = html.fromstring( text ) + proxyTable_xpath = proxyTable.xpath( '//table[@class="proxies"]/tbody/tr/@data-domain' ) + return proxyTable_xpath + + class SessionError( Exception ): + def __init__( self, value ): + self.value = value + diff --git a/rsrc/Scraper/searcher.py b/rsrc/Scraper/searcher.py new file mode 100644 index 0000000..8a1b5d3 --- /dev/null +++ b/rsrc/Scraper/searcher.py @@ -0,0 +1,108 @@ +import requests +from lxml import html +import urllib +import re +import json + +class Result: + def __init__(self, title, seeders, leechers, size, author, url): + self.title = str(title) + self.seeders = int(seeders) + self.leechers = int(leechers) + self.size = str(size) + self.author = str(author) + self.url = str(url) + + def __str__(self): + myjson = {} + myjson['title'] = self.title + myjson['seeders'] = self.seeders + myjson['leechers'] = self.leechers + myjson['size'] = self.size + myjson['author'] = self.author + myjson['url'] = self.url + + return json.dumps(myjson) + +class Scraper: + def __init__( self, config ): + # the request client, belongs to session even if no "user session" is needed + self.client = requests.Session() + self.config = config + + def craft_url(self, protocol, proxy, search_terms): + # https://pirate.blue/s/?q=Raising+Arizona&category=0&page=0&orderby=99 + f = { 'q': search_terms, 'category': 0, 'page': 0, 'orderby': 99 } + return str.format( "{0}://{1}/s/?{2}", protocol, proxy, urllib.parse.urlencode(f) ) + + + def get_results(self, search_terms): + print("Fetching from") + print(self.config.proxy) + + url = self.craft_url( "https", self.config.proxy, search_terms ) + + fetch_results = self.client.get( url ) + results_list = self.Parser.scrape( "results_list", fetch_results.content ) + + return results_list + + + def get_magnet(self, url): + url = "https://" + self.config.proxy + url + fetch_results = self.client.get(url) + + magnet = self.Parser.scrape( "magnet_link", fetch_results.content ) + + return magnet + + class Parser: + @staticmethod + def scrape( datapoint, text ): + cases = { + "results_list": Scraper.Parser.results_list, + "magnet_link": Scraper.Parser.magnet_link + } + return cases[ datapoint ]( text ) + + @staticmethod + def results_list( text ): + resultsTable = html.fromstring( text ) + resultsTable_xpath = resultsTable.xpath( '//table[@id="searchResult"]/tr' ) + + results_buffer = list() + + for tr in resultsTable_xpath: + title = tr.xpath('td[2]/div[1]/a[1]/text()')[0] + seeders = tr.xpath('td[3]/text()')[0] + leechers = tr.xpath('td[4]/text()')[0] + author = tr.xpath('td[2]/font/a/text()')[0] + size_unprocessed = tr.xpath('td[2]/font/text()')[0] + url = tr.xpath('td/div[@class="detName"]/a[@class="detLink"]/@href')[0] + + + m = re.search('Size (.+?),', size_unprocessed) + + if m: + size = m.group(1) + + + results_buffer.append( + Result(title, seeders, leechers, size, author, url) + ) + + return results_buffer + + + @staticmethod + def magnet_link( text ): + link_page = html.fromstring( text ) + magnet_link = link_page.xpath('//div[@class="download"]/a/@href')[0] + return magnet_link + + + + class SessionError( Exception ): + def __init__( self, value ): + self.value = value + diff --git a/rsrc/gui/#winMain.glade# b/rsrc/gui/#winMain.glade# new file mode 100644 index 0000000..9dd5ab0 --- /dev/null +++ b/rsrc/gui/#winMain.glade# @@ -0,0 +1,211 @@ + + + + + + winMain + False + 500 + 600 + + + + boxMain + True + False + vertical + + + True + False + + + False + True + 0 + + + + + True + False + + + mnuPulldown + True + False + 6 + 6 + 6 + + + + True + True + 0 + + + + + gtk-refresh + btnRefresh + True + True + True + 6 + 6 + True + True + + + + False + False + 3 + + + + + False + True + 0 + + + + + stsBar + True + False + 10 + 10 + 10 + 10 + 6 + 6 + True + 2 + + + False + True + end + 0 + + + + + True + False + + + False + True + 6 + 3 + + + + + Open in Client + btnDownload + True + True + True + + + + False + True + end + 4 + + + + + True + True + in + + + True + False + + + tvwResults + True + True + + + + + + + + + + True + True + end + 5 + + + + + boxSearch + True + False + + + txtSearch + True + True + True + True + True + 6 + 6 + True + + + + True + True + 0 + + + + + gtk-find + btnSearch + True + True + True + True + 6 + True + True + + + + False + True + 2 + + + + + False + True + 6 + + + + + + + True + False + + + + diff --git a/rsrc/gui/proxyselector.glade~ b/rsrc/gui/proxyselector.glade~ new file mode 100644 index 0000000..14fdcd3 --- /dev/null +++ b/rsrc/gui/proxyselector.glade~ @@ -0,0 +1,80 @@ + + + + + + False + 440 + 250 + + + True + False + vertical + + + True + False + Select a PirateBay Proxy + + + True + False + 0 + + + + + True + False + + + True + False + + + True + True + 0 + + + + + Select + True + True + True + + + False + True + 1 + + + + + Refresh + True + True + True + + + False + True + 3 + + + + + False + True + 1 + + + + + + + + + diff --git a/rsrc/gui/winMain.glade b/rsrc/gui/winMain.glade new file mode 100644 index 0000000..94155d1 --- /dev/null +++ b/rsrc/gui/winMain.glade @@ -0,0 +1,212 @@ + + + + + + winMain + False + 500 + 600 + + + + boxMain + True + False + vertical + + + True + False + + + False + True + 0 + + + + + True + False + + + mnuPulldown + True + False + 6 + 6 + 6 + + + + True + True + 0 + + + + + gtk-refresh + btnRefresh + True + True + True + 6 + 6 + True + True + + + + False + False + 3 + + + + + False + True + 0 + + + + + stsBar + True + False + 10 + 10 + 10 + 10 + 6 + 6 + True + 2 + + + False + True + end + 0 + + + + + True + False + + + False + True + 6 + 3 + + + + + Open in Client + btnDownload + True + True + True + + + + False + True + end + 4 + + + + + True + True + in + + + True + False + + + tvwResults + True + True + + + + + + + + + + + True + True + end + 5 + + + + + boxSearch + True + False + + + txtSearch + True + True + True + True + True + 6 + 6 + True + + + + True + True + 0 + + + + + gtk-find + btnSearch + True + True + True + True + 6 + True + True + + + + False + True + 2 + + + + + False + True + 6 + + + + + + + True + False + + + + diff --git a/rsrc/gui/winMain.glade~ b/rsrc/gui/winMain.glade~ new file mode 100644 index 0000000..5daefcf --- /dev/null +++ b/rsrc/gui/winMain.glade~ @@ -0,0 +1,210 @@ + + + + + + winMain + False + 500 + 600 + + + + boxMain + True + False + vertical + + + True + False + + + False + True + 0 + + + + + True + False + + + mnuPulldown + True + False + 6 + 6 + 6 + + + + True + True + 0 + + + + + gtk-refresh + btnRefresh + True + True + True + 6 + 6 + True + True + + + + False + False + 3 + + + + + False + True + 0 + + + + + stsBar + True + False + 10 + 10 + 10 + 10 + 6 + 6 + True + 2 + + + False + True + end + 0 + + + + + True + False + + + False + True + 6 + 3 + + + + + Open in Client + btnDownload + True + True + True + + + + False + True + end + 4 + + + + + True + True + in + + + True + False + + + tvwResults + True + True + + + + + + + + + + True + True + end + 5 + + + + + boxSearch + True + False + + + txtSearch + True + True + True + True + 6 + 6 + True + + + + True + True + 0 + + + + + gtk-find + btnSearch + True + True + True + True + 6 + True + True + + + + False + True + 2 + + + + + False + True + 6 + + + + + + + True + False + + + +