├── .gitignore ├── .travis.yml ├── AlertEncoder.py ├── FAQ.md ├── Makefile ├── NOTES.md ├── README.md ├── TorrentPlugin.py ├── __init__.py ├── appveyor.yml ├── libtorrent └── __init__.py ├── plugin_info.json ├── screenshots ├── form.png ├── install.png └── play.png └── test.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | libtorrent/*.so 3 | __pycache__ 4 | *.tar.gz 5 | boost_* 6 | *.torrent 7 | libtorrent-repo/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | matrix: 2 | include: 3 | - os: linux 4 | language: cpp 5 | compiler: gcc 6 | dist: focal 7 | sudo: true 8 | addons: 9 | apt: 10 | update: true 11 | packages: 12 | - g++-9 13 | - gcc-9 14 | install: 15 | - make config-linux 16 | - make boost 17 | - make libtorrent-repo 18 | - export BOOST_BUILD_PATH=${PWD}/boost_1_74_0/tools/build 19 | - export BOOST_ROOT=${PWD}/boost_1_74_0 20 | - cd libtorrent-repo/bindings/python && ../../../boost_1_74_0/b2 release --debug-configuration crypto=openssl cxxstd=14 python=3.8 libtorrent-link=static boost-link=static 21 | - cd ../../.. 22 | - cp libtorrent-repo/bindings/python/bin/gcc-9/release/crypto-openssl/cxxstd-14-iso/libtorrent-python-pic-on/python-3.8/libtorrent.so libtorrent/ 23 | 24 | - os: osx 25 | osx_image: xcode12.5 26 | language: generic 27 | sudo: true 28 | addons: 29 | homebrew: 30 | packages: 31 | - openssl@1.1 32 | - boost-python3 33 | - boost-build 34 | install: 35 | - ln -s -f /usr/local/bin/python3.9 /usr/local/bin/python 36 | - make config-os 37 | - make libtorrent-repo 38 | - cd libtorrent-repo/bindings/python && b2 release --debug-configuration crypto=openssl cxxstd=14 python=3.9 libtorrent-link=static boost-link=static 39 | - ls bin/darwin-12.0.5/release/crypto-openssl/cxxstd-14-iso/python-3.9/ 40 | - cd ../../.. 41 | - cp libtorrent-repo/bindings/python/bin/darwin-12.0.5/release/crypto-openssl/cxxstd-14-iso/python-3.9/libtorrent.so libtorrent/ 42 | 43 | script: 44 | - python3 test.py 45 | 46 | before_deploy: 47 | - zip build-$TRAVIS_OS_NAME.zip libtorrent/__init__.py libtorrent/libtorrent.so __init__.py TorrentPlugin.py AlertEncoder.py test.py plugin_info.json 48 | deploy: 49 | provider: releases 50 | skip_cleanup: true 51 | api_key: $GH_TOKEN 52 | file: 53 | - build-$TRAVIS_OS_NAME.zip 54 | on: 55 | tags: true 56 | -------------------------------------------------------------------------------- /AlertEncoder.py: -------------------------------------------------------------------------------- 1 | import base64 2 | 3 | class AlertEncoder(object): 4 | 5 | def __init__(self, alert): 6 | self.alert = alert 7 | self.encodeAlert() 8 | 9 | if self.response['what'] == 'read_piece': 10 | self.encodeReadPieceAlert() 11 | elif self.response['what'] == 'add_torrent': 12 | print('Add Torrent Alert !') 13 | elif self.response['what'] == 'piece_finished': 14 | self.encodePieceFinishedAlert() 15 | 16 | 17 | def encodeAlert(self): 18 | self.response = { 19 | 'what': self.alert.what(), 20 | 'message': self.alert.message(), 21 | 'category': self.alert.category() 22 | } 23 | 24 | def encodeReadPieceAlert(self): 25 | if hasattr(self.alert, 'error') and self.alert.error.value() != 0: 26 | self.response['error'] = { 27 | 'value': self.alert.error.value(), 28 | 'message': self.alert.error.message() 29 | } 30 | else : 31 | self.response['pieceIndex'] = self.alert.piece 32 | self.response['size'] = self.alert.size 33 | self.response['buffer'] = self.alert.buffer 34 | 35 | def encodePieceFinishedAlert(self): 36 | self.response['handle'] = self.alert.handle 37 | self.response['pieceIndex'] = self.alert.piece_index 38 | 39 | def get(self): 40 | return self.response 41 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | 2 | ## FAQ 3 | 4 | ### How to install it 5 | You will need to install it via this site [/1ChMNjXpW5vU5iXb9DSXzqAUfY46Pc2RTL](http://127.0.0.1:43110//1ChMNjXpW5vU5iXb9DSXzqAUfY46Pc2RTL). Once it is installed you will be asked to restart ZeroNet and then refresh the site. 6 | 7 | ### How to use it 8 | You will be presented with a form to add a torrent via either a magnet link or a .torrent file. To properly test if the plugin is working please click on the `sintel` link which will fill the form with the Sintel movie magnet and then submit. 9 | 10 | You will need to wait a bit for it to connect to peers and start downloading the film. It will then show the video player and you can start to watch Sintel as it is being downloaded. 11 | 12 | ### How to stop it 13 | Once you have stopped ZeroNet or disabled the plugin (using [/Plugins](http://127.0.0.1:43110//Plugins)) the torrent will not be shared anymore. 14 | 15 | ### Where is the file saved 16 | The file will be saved in your `data` folder under the folder of the site that it has been requested from (so here `1ChMNjXpW5vU5iXb9DSXzqAUfY46Pc2RTL`) under a `downloads` folder (e.g `data/1ChMNjXpW5vU5iXb9DSXzqAUfY46Pc2RTL/downloads`). 17 | 18 | This should be accessible in ZeroNet via this link [/list/1ChMNjXpW5vU5iXb9DSXzqAUfY46Pc2RTL](http://127.0.0.1:43110/list/1ChMNjXpW5vU5iXb9DSXzqAUfY46Pc2RTL). You can look for the `downloads` folder if you have added torrent file via the `1ChMNjXpW5vU5iXb9DSXzqAUfY46Pc2RTL` site and want to access it. 19 | 20 | ### How can I hide my identity 21 | Use a no-logging VPN. 22 | 23 | ### Where can it be used 24 | It can only be used in site that supports and have implemented the specific api of the plugin. Documentation will be available soon. Devs can also take a look at this file for the available actions implemented by the plugin (https://github.com/rllola/zeronet-torrent-plugin-example/blob/master/app/stores/Site.js) 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | libtorrent-repo: 2 | git clone https://github.com/arvidn/libtorrent.git libtorrent-repo 3 | cd libtorrent-repo && git submodule update --init 4 | cd libtorrent-repo && git checkout v2.0.4 5 | 6 | boost: 7 | wget -q https://boostorg.jfrog.io/artifactory/main/release/1.74.0/source/boost_1_74_0.tar.gz 8 | tar -zxf boost_1_74_0.tar.gz 9 | rm boost_1_74_0.tar.gz 10 | cd boost_1_74_0 && ./bootstrap.sh 11 | 12 | 13 | build-libtorrent: boost 14 | cd libtorrent-repo/bindings/python && ../../../boost_1_74_0/b2 release --debug-configuration crypto=openssl cxxstd=17 python=3.8 libtorrent-link=static boost-link=static 15 | cp libtorrent-repo/bindings/python/bin/gcc-9/release/crypto-openssl/cxxstd-17-iso/libtorrent-python-pic-on/python-3.8/libtorrent.so lib/ 16 | 17 | config-linux: 18 | echo "using gcc ;" >> ~/user-config.jam 19 | echo "using python : 3.8 ;" >> ~/user-config.jam 20 | 21 | config-os: 22 | echo "using darwin ;" >> ~/user-config.jam 23 | echo "using python : 3.9 ;" >> ~/user-config.jam 24 | 25 | test: 26 | python3 test.py 27 | 28 | clean: 29 | rm libtorrent/*.so -------------------------------------------------------------------------------- /NOTES.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | 3 | ### Create a plugin 4 | 5 | To add a plugin to ZeroNet you can create a new folder with the name of your plugin in the `plugins` folder. 6 | 7 | example : 8 | ``` 9 | cd plugins 10 | mkdir Example 11 | touch __init__.py ExamplePlugin.py 12 | ``` 13 | 14 | in `__init__.py` : 15 | ``` 16 | import ExamplePlugin 17 | ``` 18 | 19 | in `ExamplePlugin.py` : 20 | ``` 21 | from Plugin import PluginManager 22 | 23 | @PluginManager.registerTo("UiWebsocket") 24 | class UiWebsocketPlugin(object): 25 | 26 | # Create a new action that can be called using zeroframe api 27 | def actionHelloWorld(self, to): 28 | self.response(to, {'message':'Hello World'}) 29 | ``` 30 | 31 | Example of code calling helloWorld action using zeroframe api 32 | ``` 33 | import ZeroFrame from 'zeroframe' 34 | 35 | var zeroframe = new ZeroFrame() 36 | 37 | zeroframe.cmd('helloWorld', {}, (response) => { 38 | console.log(response) // print 'Hello World' message 39 | }) 40 | ``` 41 | 42 | ## Build libtorrent python bindings 43 | 44 | 45 | ### For Linux 46 | 47 | We download boost 1.74 (min required). You will need `gcc`, `git` and `wget` installed. 48 | 49 | ``` 50 | $ make libtorrent 51 | $ export BOOST_BUILD_PATH=${PWD}/boost_1_74_0/tools/build 52 | $ export BOOST_ROOT=${PWD}/boost_1_74_0 53 | $ make build-libtorrent 54 | ``` 55 | 56 | Test: 57 | ``` 58 | $ make test 59 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Torrent Plugin 2 | 3 | [![Build Status](https://travis-ci.org/rllola/zeronet-torrent-plugin.svg?branch=master)](https://travis-ci.org/rllola/zeronet-torrent-plugin) 4 | 5 | **Important! This is still work in progress. Don't use it if you don't want to.** 6 | 7 | Example site and installer : http://127.0.0.1:43110/1ChMNjXpW5vU5iXb9DSXzqAUfY46Pc2RTL/ 8 | 9 | This project is an attempt to integrate a Torrent client in ZeroNet as a plugin. It makes available a Torrent specific api for site that allows site to add torrent files, hash or magnet. The files will be saved in the site folder which makes it available to the site (e.g `data/1ChMNjXpW5vU5iXb9DSXzqAUfY46Pc2RTL/downloads`). 10 | 11 | It also allows streaming video files out of the box. When a chunk of video will be requested by the player the torrent plugin will verify if it is available and it not will prioritize this piece. This makes it possible to watch videos when they start being being downloaded. 12 | 13 | More information is available in the [FAQ](/FAQ.md). 14 | 15 | ## Screenshots 16 | 17 | ![Install](/screenshots/install.png) 18 | 19 | ![Form](/screenshots/form.png) 20 | 21 | ![Play](/screenshots/play.png) 22 | 23 | ## TODO 24 | 25 | - [ ] Save torrents list at shut down and restart using this list 26 | - [ ] UI for managing the Torrent client and vizualize files being downloaded 27 | - [ ] Config UI (trackers, limits, ...) 28 | 29 | ## Dev (for linux) 30 | 31 | We need to build libtorrent python bindings with at least boost 1.74. 32 | 33 | ``` 34 | $ make config-linux 35 | $ make boost 36 | $ make libtorrent-repo 37 | $ export BOOST_BUILD_PATH=${PWD}/boost_1_74_0/tools/build 38 | $ export BOOST_ROOT=${PWD}/boost_1_74_0 39 | $ cd libtorrent-repo/bindings/python && ../../../boost_1_74_0/b2 release --debug-configuration crypto=openssl cxxstd=14 python=3.8 libtorrent-link=static boost-link=static 40 | $ cd ../../.. 41 | $ cp libtorrent-repo/bindings/python/bin/gcc-9/release/crypto-openssl/cxxstd-14-iso/libtorrent-python-pic-on/python-3.8/libtorrent.so libtorrent/ 42 | 43 | $ python test.py 44 | ``` 45 | 46 | It might take a couple of minutes. 47 | 48 | ## Troubleshooting 49 | 50 | ... -------------------------------------------------------------------------------- /TorrentPlugin.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import time 3 | import base64 4 | import gevent 5 | import sys 6 | import libtorrent 7 | from util.Flag import flag 8 | 9 | from .AlertEncoder import AlertEncoder 10 | 11 | from Plugin import PluginManager 12 | 13 | libtorrent = libtorrent.libtorrent 14 | 15 | VERSION = '0.4.8' 16 | 17 | def popAlerts(session): 18 | while 1: 19 | alertsList = session.pop_alerts() 20 | # Need encoding 21 | for alert in alertsList: 22 | result = AlertEncoder(alert) 23 | for instanceOfUiWebsocketPlugin in UiWebsocketPlugin._instances: 24 | if alert.what() == 'read_piece' and not 'error' in result.get(): 25 | for instanceOfUiRequestPlugin in UiRequestPlugin._instances: 26 | if result.get()["pieceIndex"] in instanceOfUiRequestPlugin.piece_index_requested: 27 | instanceOfUiRequestPlugin.requested_pieces.append(result.get()) 28 | instanceOfUiWebsocketPlugin.cmd(alert.what(), result.get()) 29 | gevent.sleep(0.250) 30 | 31 | # Initiate libtorrent session 32 | session = libtorrent.session({'listen_interfaces':'0.0.0.0:6881', 'alert_mask': libtorrent.alert.category_t.progress_notification+libtorrent.alert.category_t.status_notification}) 33 | gevent.spawn(popAlerts, session) 34 | 35 | @PluginManager.registerTo("UiRequest") 36 | class UiRequestPlugin(object): 37 | _instances = [] 38 | 39 | def __init__(self, *args, **kwargs): 40 | super(UiRequestPlugin, self).__init__(*args, **kwargs) 41 | UiRequestPlugin._instances.append(self) 42 | self.requested_pieces = [] 43 | self.file = None 44 | self.piece_index_requested = [] 45 | 46 | def getTorrentFile(self, file_path): 47 | torrent_handles = session.get_torrents() 48 | for h in torrent_handles: 49 | for file_index in range(0,files.num_files()): 50 | file = ti.file_at(file_index) 51 | 52 | if file.path in file_path: 53 | print(file.path) 54 | return ti.info_hash() 55 | return False 56 | 57 | def actionFile(self, file_path, *args, **kwargs): 58 | if "info_hash" in self.get: 59 | print("info hash :", self.get["info_hash"]) 60 | print("File index :", self.get["file_index"]) 61 | 62 | info_hash = libtorrent.sha1_hash(bytes.fromhex(self.get["info_hash"])) 63 | h = session.find_torrent(info_hash) 64 | 65 | if h.is_valid(): 66 | ti = h.torrent_file() 67 | files = ti.files() 68 | for file_index in range(0,files.num_files()): 69 | file = ti.file_at(file_index) 70 | 71 | if file.path in file_path: 72 | # priotarize file (7 max priority) 73 | h.file_priority(file_index, 7) 74 | 75 | kwargs["block_size"] = ti.piece_length() 76 | print("piece_size : {}".format(kwargs["block_size"])) 77 | self.file = file 78 | kwargs["file_size"] = file.size 79 | kwargs["file_obj"] = TorrentFile(h, file, self) 80 | 81 | return super(UiRequestPlugin, self).actionFile(file_path, *args, **kwargs) 82 | 83 | 84 | @PluginManager.registerTo("UiWebsocket") 85 | class UiWebsocketPlugin(object): 86 | 87 | _instances = [] 88 | 89 | def __init__(self, *args, **kwargs): 90 | super(UiWebsocketPlugin, self).__init__(*args, **kwargs) 91 | UiWebsocketPlugin._instances.append(self) 92 | 93 | def hasSitePermission(self, address, cmd=None): 94 | if "Torrent" in self.site.settings["permissions"]: 95 | return True 96 | else: 97 | return False 98 | 99 | def actionGetVersion(self, to): 100 | self.response(to, {'version': VERSION}) 101 | 102 | @flag.no_multiuser 103 | def actionAddTorrent(self, to, torrentIdentifier): 104 | if not self.hasSitePermission(self.site.address): 105 | return self.response(to, {"error": "Forbidden"}) 106 | 107 | save_path = './data/' + self.site.address + '/downloads/' 108 | try: 109 | e = libtorrent.bdecode(base64.b64decode(torrentIdentifier)) 110 | info = libtorrent.torrent_info(e) 111 | params = { 'save_path': save_path, \ 112 | 'storage_mode': libtorrent.storage_mode_t.storage_mode_sparse, \ 113 | 'ti': info } 114 | except Exception as exception: 115 | try: 116 | # Test if a magnet 117 | params = libtorrent.parse_magnet_uri(torrentIdentifier) 118 | params.save_path = save_path 119 | # HACK: 120 | # Doesn't recognise sha1_hash python object when added to session if not converted to string 121 | # 'No registered converter was able to produce a C++ rvalue of type bytes from this Python object of type sha1_hash' 122 | #params['info_hash'] = params['info_hash'].to_string() 123 | except Exception as exception: 124 | params = { 'save_path': save_path, \ 125 | 'storage_mode': libtorrent.storage_mode_t.storage_mode_sparse, \ 126 | 'info_hash': torrentIdentifier.decode('hex') } 127 | try: 128 | session.async_add_torrent(params) 129 | except Exception as exception: 130 | self.response(to, {'error': str(exception)}) 131 | else: 132 | if type(params) is libtorrent.add_torrent_params: 133 | info_hash = params.info_hashes.get_best() 134 | else: 135 | if not info is None: 136 | info_hash = info.info_hashes().get_best() 137 | else: 138 | info_hash = params['info_hash'] 139 | self.response(to, {'info_hash': str(info_hash)}) 140 | 141 | def actionTorrentStatus(self, to, info_hash): 142 | if not self.hasSitePermission(self.site.address): 143 | return self.response(to, {"error": "Forbidden"}) 144 | 145 | info_hash = libtorrent.sha1_hash(bytes.fromhex(info_hash)) 146 | h = session.find_torrent(info_hash) 147 | if h.is_valid(): 148 | s = h.status() 149 | self.response(to, {'progress': s.progress, \ 150 | 'download_rate': s.download_rate, \ 151 | 'upload_rate': s.upload_rate, \ 152 | 'num_peers': s.num_peers, \ 153 | 'state': str(s.state) }) 154 | else: 155 | self.response(to, {'error': 'Torrent not found'}) 156 | 157 | def actionGetTorrentInfo(self, to, info_hash): 158 | if not self.hasSitePermission(self.site.address): 159 | return self.response(to, {"error": "Forbidden"}) 160 | 161 | info_hash = libtorrent.sha1_hash(bytes.fromhex(info_hash)) 162 | h = session.find_torrent(info_hash) 163 | if h.is_valid(): 164 | ti = h.get_torrent_info() 165 | files = ti.files() 166 | 167 | arrayFiles = [] 168 | for file in files: 169 | arrayFiles.append({'path': file.path, \ 170 | 'offset': file.offset, \ 171 | 'size': file.size \ 172 | }) 173 | 174 | self.response(to, {'name': ti.name(), \ 175 | 'num_files' : ti.num_files(), \ 176 | 'files': arrayFiles, \ 177 | 'piece_length': ti.piece_length() \ 178 | }) 179 | else: 180 | self.response(to, {'error': 'Torrent not found'}) 181 | 182 | def actionReadPiece(self, to, info_hash, piece_index): 183 | if not self.hasSitePermission(self.site.address): 184 | return self.response(to, {"error": "Forbidden"}) 185 | 186 | info_hash = libtorrent.sha1_hash(bytes.fromhex(info_hash)) 187 | h = session.find_torrent(info_hash) 188 | if h.is_valid(): 189 | h.read_piece(piece_index) 190 | self.response(to, 'ok') 191 | else: 192 | self.response(to, {'error': 'Torrent not found'}) 193 | 194 | def actionHavePiece(self, to, info_hash, piece_index): 195 | if not self.hasSitePermission(self.site.address): 196 | return self.response(to, {"error": "Forbidden"}) 197 | 198 | info_hash = libtorrent.sha1_hash(bytes.fromhex(info_hash)) 199 | h = session.find_torrent(info_hash) 200 | if h.is_valid(): 201 | response = h.have_piece(piece_index) 202 | self.response(to, response) 203 | else: 204 | self.response(to, {'error': 'Torrent not found'}) 205 | 206 | 207 | def actionPrioritizePiece(self, to, info_hash, piece_index, new_priority): 208 | if not self.hasSitePermission(self.site.address): 209 | return self.response(to, {"error": "Forbidden"}) 210 | 211 | info_hash = libtorrent.sha1_hash(bytes.fromhex(info_hash)) 212 | h = session.find_torrent(info_hash) 213 | if h.is_valid(): 214 | if 0 <= new_priority <= 7 : 215 | h.piece_priority(piece_index, new_priority) 216 | self.response(to, 'ok') 217 | else : 218 | self.response(to, {'error': 'new_priority should be an integer bewteen 0 and 7'}) 219 | else: 220 | self.response(to, {'error': 'Torrent not found'}) 221 | 222 | 223 | class TorrentFile(object): 224 | def __init__(self, torrent_handle, file, uirequest): 225 | self.torrent_handle = torrent_handle 226 | self.read_bytes = 0 227 | self.file = file 228 | self._offset = file.offset 229 | self.uirequest = uirequest 230 | self.cache = [] 231 | 232 | 233 | def read(self, buff=64 * 1024): 234 | chunk_file = 0x00 235 | ti = self.torrent_handle.torrent_file() 236 | piece_index = (self.file.offset + self.read_bytes) // ti.piece_length() 237 | #print("Piece Index requested : {} ( ({} + {}) // {})".format(piece_index, self.file.offset, self.read_bytes, ti.piece_length())) 238 | 239 | self.uirequest.piece_index_requested.append(piece_index) 240 | 241 | # TODO: `set_piece_deadline` has the same behavior as read when piece already available 242 | if self.torrent_handle.have_piece(piece_index): 243 | self.torrent_handle.read_piece(piece_index) 244 | else: 245 | print("Piece not available!") 246 | # deadline in milliseconds so we have a 900 seconds deadline after what maybe we can have a timeout ? 247 | self.torrent_handle.set_piece_deadline(piece_index, 900 * 1000, libtorrent.deadline_flags_t.alert_when_available) 248 | 249 | while chunk_file == 0x00: 250 | for piece in self.uirequest.requested_pieces: 251 | if piece["pieceIndex"] == piece_index: 252 | if self._offset: 253 | chunk_file = piece["buffer"][self._offset:] 254 | self._offset = 0 255 | else: 256 | chunk_file = piece["buffer"] 257 | self.read_bytes += len(chunk_file) 258 | self.uirequest.requested_pieces.remove(piece) 259 | break 260 | gevent.sleep(0.1) 261 | pass 262 | 263 | return chunk_file 264 | 265 | def seek(self, pos, whence=0): 266 | #print("SEEKING {}".format(pos)) 267 | self.read_bytes = pos 268 | ti = self.torrent_handle.torrent_file() 269 | self._offset = (self.file.offset + self.read_bytes) % ti.piece_length() 270 | return 271 | 272 | def close(self): 273 | pass 274 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import zipfile 4 | 5 | if sys.platform == 'win32': 6 | libtorrent_file = 'libtorrent.pyd' 7 | else: 8 | libtorrent_file = 'libtorrent.so' 9 | 10 | libtorrent_zip_path = os.path.join(os.path.dirname(__file__),'libtorrent-{}.zip'.format(sys.platform)) 11 | 12 | # if libtorrent lib not present look for it 13 | if not os.path.exists(os.path.join(os.path.dirname(__file__),'libtorrent', libtorrent_file)): 14 | with zipfile.ZipFile(libtorrent_zip_path,'r') as zip_ref: 15 | zip_ref.extractall(os.path.join(os.path.dirname(__file__),'libtorrent')) 16 | 17 | # Need to add libtorrent lib 18 | sys.path.append(os.path.dirname(__file__)) 19 | 20 | from . import TorrentPlugin -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2019 2 | 3 | platform: 4 | - x64 5 | 6 | environment: 7 | matrix: 8 | - PYTHON: "C:\\Python38-x64" 9 | PYV: "3.8" 10 | - PYTHON: "C:\\Python37-x64" 11 | PYV: "3.7" 12 | artifacts: 13 | - path: build-windows-*.zip 14 | 15 | build: off 16 | 17 | init: 18 | - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" 19 | - python -m pip install --upgrade pip 20 | - python -m pip install numpy 21 | 22 | install: 23 | - '"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"' 24 | - nmake libtorrent-repo 25 | - dir 26 | - curl -LO https://boostorg.jfrog.io/artifactory/main/release/1.74.0/source/boost_1_74_0.zip 27 | - unzip -q boost_1_74_0.zip 28 | - cmd: set BOOST_ROOT=C:/projects/zeronet-torrent-plugin/boost_1_74_0 29 | - cmd: set BOOST_BUILD_PATH=C:/projects/zeronet-torrent-plugin/boost_1_74_0/tools/build 30 | - cd C:/projects/zeronet-torrent-plugin/boost_1_74_0/ 31 | - bootstrap.bat 32 | - cmd: echo using msvc ; >>%HOMEDRIVE%%HOMEPATH%/user-config.jam 33 | - cd C:\projects\zeronet-torrent-plugin\libtorrent-repo\bindings\python 34 | - C:/projects/zeronet-torrent-plugin/boost_1_74_0/b2 release --debug-configuration address-model=64 cxxstd=14 python=%PYV% libtorrent-link=static boost-link=static 35 | - cp C:\projects\zeronet-torrent-plugin\libtorrent-repo\bindings\python\bin\msvc-14.2\release\address-model-64\cxxstd-14-iso\python-%PYV%\threading-multi\libtorrent.pyd C:\projects\zeronet-torrent-plugin\libtorrent 36 | - cp C:\projects\zeronet-torrent-plugin\libtorrent-repo\bindings\python\bin\msvc-14.2\release\address-model-64\cxxstd-14-iso\python-%PYV%\threading-multi\libtorrent.lib C:\projects\zeronet-torrent-plugin\libtorrent 37 | - cd C:\projects\zeronet-torrent-plugin 38 | 39 | test_script: 40 | - python test.py 41 | 42 | after_test: 43 | - 7z a build-windows-%PYV%.zip libtorrent/__init__.py libtorrent/libtorrent.lib libtorrent/libtorrent.pyd __init__.py TorrentPlugin.py AlertEncoder.py test.py plugin_info.json 44 | 45 | deploy: 46 | provider: GitHub 47 | auth_token: 48 | secure: dSaK+GaXURrAcZoxypp7VRGj7JLzSdBvQLkOZh5tAAn4wyyCMzaH4DqXgrLtAy1h 49 | artifact: build-windows-%PYV%.zip 50 | prerelease: true 51 | force_update: true 52 | on: 53 | appveyor_repo_tag: true # deploy on tag push only 54 | -------------------------------------------------------------------------------- /libtorrent/__init__.py: -------------------------------------------------------------------------------- 1 | from . import libtorrent -------------------------------------------------------------------------------- /plugin_info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Torrent", 3 | "description": "Torrent client in ZeroNet.", 4 | "default": "enabled", 5 | "rev": 12 6 | } 7 | -------------------------------------------------------------------------------- /screenshots/form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rllola/zeronet-torrent-plugin/7e41506797256e9d644b6f556308ea8b4be92054/screenshots/form.png -------------------------------------------------------------------------------- /screenshots/install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rllola/zeronet-torrent-plugin/7e41506797256e9d644b6f556308ea8b4be92054/screenshots/install.png -------------------------------------------------------------------------------- /screenshots/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rllola/zeronet-torrent-plugin/7e41506797256e9d644b6f556308ea8b4be92054/screenshots/play.png -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import libtorrent 3 | 4 | libtorrent = libtorrent.libtorrent 5 | 6 | class TestBuild(unittest.TestCase): 7 | 8 | def test_build(self): 9 | self.assertEqual(libtorrent.version, '2.0.4.0') 10 | 11 | def test_session(self): 12 | session = libtorrent.session({'listen_interfaces':'0.0.0.0:6881', 'alert_mask': libtorrent.alert.category_t.progress_notification}) 13 | 14 | if __name__ == '__main__': 15 | unittest.main() 16 | --------------------------------------------------------------------------------