├── .gitignore ├── README.md ├── setup.py └── webshare ├── __init__.py └── webshare.py /.gitignore: -------------------------------------------------------------------------------- 1 | venv/ 2 | *.swp 3 | *.pyc 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### About 2 | Simple webshare.cz API interface for downloading files. For basic usage use `login` and `download_file` methods. 3 | 4 | Defined interface: 5 | ```python 6 | class WebshareAPI: 7 | def login(self, user_name, password): 8 | """Logs {user_name} in Webshare API""" 9 | 10 | def download_file(self, url, dest_path): 11 | """Downloads file in {url} to {dest_path}""" 12 | 13 | def hash_password(self, user_name, password, salt): 14 | """Creates password hash used by Webshare API""" 15 | 16 | def get_salt(self, user_name): 17 | """Retrieves salt for password hash from webshare.cz""" 18 | 19 | def get_file_id(self, url): 20 | """Return file_id for given webshare URL""" 21 | 22 | def get_download_link(self, file_id): 23 | """Query actual download link from {file_id}""" 24 | ``` 25 | 26 | ### To get pip plugin: 27 | 28 | ```sh 29 | $ virtualenv venv 30 | $ source venv/bin/activate 31 | $ pip install . 32 | ``` 33 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='webshare', 5 | version='1.0', 6 | packages=['webshare'], 7 | install_requires = [ 8 | "requests", 9 | "passlib", 10 | ] 11 | ) 12 | -------------------------------------------------------------------------------- /webshare/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cyrusmg/webshare-api/28889bcc0f177ccb159416a7e7f706c17634bcd1/webshare/__init__.py -------------------------------------------------------------------------------- /webshare/webshare.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Webshare downloader plugin 4 | """ 5 | 6 | import requests 7 | import hashlib 8 | import re 9 | import urllib.request 10 | import os.path 11 | import shutil 12 | import argparse 13 | from passlib.hash import md5_crypt 14 | from xml.etree import ElementTree 15 | 16 | class WebshareAPI: 17 | def __init__(self): 18 | self._base_url = "https://webshare.cz/api/" 19 | self._token = "" 20 | 21 | def login(self, user_name, password): 22 | """Logs {user_name} in Webshare API""" 23 | salt = self.get_salt(user_name) 24 | headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'} 25 | url = self._base_url + 'login/' 26 | password, digest = self.hash_password(user_name, password, salt) 27 | data = { 28 | 'username_or_email' : user_name, 29 | 'password' : password, 30 | 'digest' : digest, 31 | 'keep_logged_in' : 1 32 | } 33 | response = requests.post(url, data=data, headers=headers) 34 | assert(response.status_code == 200) 35 | root = ElementTree.fromstring(response.content) 36 | assert root.find('status').text == 'OK', 'Return code was not OK, debug info: status: {}, code: {}, message: {}'.format( 37 | root.find('status').text, 38 | root.find('code').text, 39 | root.find('message').text) 40 | 41 | self._token = root.find('token').text 42 | 43 | def download_file(self, url, dest_path): 44 | """Downloads file in {url} to {dest_path}""" 45 | file_id = self.get_file_id(url) 46 | download_url = self.get_download_link(file_id) 47 | 48 | with urllib.request.urlopen(download_url) as response: 49 | assert response.status == 200 50 | assert response.getheader('content-disposition'), "Expected 'content-disposition' header in response" 51 | file_name = response.getheader('content-disposition')[21:] 52 | file_path = os.path.join(dest_path, file_name) 53 | with open(file_path, 'wb') as out_file: 54 | shutil.copyfileobj(response, out_file) 55 | 56 | def hash_password(self, user_name, password, salt): 57 | """Creates password hash used by Webshare API""" 58 | password = hashlib.sha1(md5_crypt.encrypt(password, salt=salt).encode('utf-8')).hexdigest() 59 | digest = hashlib.md5((user_name + ':Webshare:' + password).encode('utf-8')).hexdigest() 60 | return password, digest 61 | 62 | def get_salt(self, user_name): 63 | """Retrieves salt for password hash from webshare.cz""" 64 | headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'} 65 | url = self._base_url + 'salt/' 66 | data = {'username_or_email' : user_name} 67 | response = requests.post(url, data=data, headers=headers) 68 | assert(response.status_code == 200) 69 | root = ElementTree.fromstring(response.content) 70 | assert root.find('status').text == 'OK', 'Return code was not OK, debug info: status: {}, code: {}, message: {}'.format( 71 | root.find('status').text, 72 | root.find('code').text, 73 | root.find('message').text) 74 | 75 | return root.find('salt').text 76 | 77 | def get_file_id(self, url): 78 | """Return {file_id} for given webshare URL""" 79 | # Example URL: https://webshare.cz/#/file/7e31cQ7l44/txt-txt?nojs=true 80 | regex = r'https?://(?:www\.)?webshare\.cz/(?:#/)?file/(?P\w+)' 81 | match = re.match(regex, url) 82 | assert match, "Invalid URL: {}".format(url) 83 | 84 | return match.groupdict()['ID'] 85 | 86 | def get_download_link(self, file_id): 87 | """Query actual download link from {file_id}""" 88 | headers = {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'} 89 | url = self._base_url + 'file_link/' 90 | data = {'ident' : file_id, 'wst' : self._token} 91 | response = requests.post(url, data=data, headers=headers) 92 | assert response.status_code == 200 93 | root = ElementTree.fromstring(response.content) 94 | assert root.find('status').text == 'OK', 'Return code was not OK, debug info: status: {}, code: {}, message: {}'.format( 95 | root.find('status').text, 96 | root.find('code').text, 97 | root.find('message').text) 98 | 99 | return root.find('link').text 100 | 101 | if __name__ == "__main__": 102 | parser = argparse.ArgumentParser() 103 | parser.add_argument('-u', '--user-name', type=str, help='your webshare username (REQUIRED)', required=True) 104 | parser.add_argument('-p', '--password', type=str, help='your webshare password (REQUIRED)', required=True) 105 | parser.add_argument('link', metavar='link', type=str, help='file link to be downloaded') 106 | parser.add_argument('-d', '--dest', type=str, default='.', help='destination folder (default is current folder)') 107 | params = parser.parse_args() 108 | 109 | webshare = WebshareAPI() 110 | webshare.login(params.user_name, params.password) 111 | webshare.download_file(params.link, params.dest) 112 | 113 | --------------------------------------------------------------------------------