Версия для Kodi 19
This commit is contained in:
		
							parent
							
								
									f39749807f
								
							
						
					
					
						commit
						6e04f3fc14
					
				| @ -1,7 +1,7 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8" standalone="yes"?> | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> | ||||||
| <addon id="script.module.pyrrent2http" name="pyrrent2http" version="1.0.0" provider-name="inpos"> | <addon id="script.module.pyrrent2http" name="pyrrent2http" version="1.1.0" provider-name="inpos"> | ||||||
|     <requires> |     <requires> | ||||||
|         <import addon="xbmc.python" version="2.14.0"/> |         <import addon="xbmc.python" version="3.0.0"/> | ||||||
|         <import	addon="script.module.libtorrent" /> |         <import	addon="script.module.libtorrent" /> | ||||||
|         <import addon="script.module.chardet" /> |         <import addon="script.module.chardet" /> | ||||||
|     </requires> |     </requires> | ||||||
| @ -17,5 +17,5 @@ | |||||||
|         <description lang="ru">Обеспечивает последовательную (sequential) загрузку торрентов для потокового онлайн просмотра через HTTP. Основан на библиотеке LibTorrent.</description> |         <description lang="ru">Обеспечивает последовательную (sequential) загрузку торрентов для потокового онлайн просмотра через HTTP. Основан на библиотеке LibTorrent.</description> | ||||||
|         <description lang="en">Provides sequential torrent downloading for online streaming video and other media over HTTP.</description> |         <description lang="en">Provides sequential torrent downloading for online streaming video and other media over HTTP.</description> | ||||||
|         <email>inpos@yandex.ru</email> |         <email>inpos@yandex.ru</email> | ||||||
|         <source>https://github.com/inpos/script.module.pyrrent2http</source></extension> |         <source>https://git.ukamnya.ru/ukamnya/script.module.pyrrent2http</source></extension> | ||||||
| </addon> | </addon> | ||||||
|  | |||||||
| @ -3,33 +3,6 @@ | |||||||
| from collections import namedtuple | from collections import namedtuple | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # noinspection PyClassHasNoInit |  | ||||||
| class State: |  | ||||||
|     QUEUED_FOR_CHECKING = 0 |  | ||||||
|     CHECKING_FILES = 1 |  | ||||||
|     DOWNLOADING_METADATA = 2 |  | ||||||
|     DOWNLOADING = 3 |  | ||||||
|     FINISHED = 4 |  | ||||||
|     SEEDING = 5 |  | ||||||
|     ALLOCATING = 6 |  | ||||||
|     CHECKING_RESUME_DATA = 7 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # noinspection PyClassHasNoInit |  | ||||||
| class MediaType: |  | ||||||
|     UNKNOWN = None |  | ||||||
|     AUDIO = 'audio' |  | ||||||
|     VIDEO = 'video' |  | ||||||
|     SUBTITLES = 'subtitles' |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| # noinspection PyClassHasNoInit |  | ||||||
| class Encryption: |  | ||||||
|     FORCED = 0 |  | ||||||
|     ENABLED = 1 |  | ||||||
|     DISABLED = 2 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| SessionStatus = namedtuple('SessionStatus', "name, state, state_str, error, progress, download_rate, upload_rate, " | SessionStatus = namedtuple('SessionStatus', "name, state, state_str, error, progress, download_rate, upload_rate, " | ||||||
|                                             "total_download, total_upload, num_peers, num_seeds, total_seeds, " |                                             "total_download, total_upload, num_peers, num_seeds, total_seeds, " | ||||||
|                                             "total_peers") |                                             "total_peers") | ||||||
| @ -39,5 +12,5 @@ FileStatus = namedtuple('FileStatus', "name, save_path, url, size, offset, downl | |||||||
| PeerInfo = namedtuple('PeerInfo', "ip, flags, source, up_speed, down_speed, total_upload, total_download, " | PeerInfo = namedtuple('PeerInfo', "ip, flags, source, up_speed, down_speed, total_upload, total_download, " | ||||||
|                                   "country, client") |                                   "country, client") | ||||||
| 
 | 
 | ||||||
| from engine import Engine | from .engine import Engine | ||||||
| from error import Error | from .error import Error | ||||||
|  | |||||||
| @ -1,31 +1,37 @@ | |||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
| import os | import os | ||||||
|  | import threading | ||||||
|  | import urllib.error | ||||||
|  | import urllib.parse | ||||||
|  | import urllib.request | ||||||
|  | 
 | ||||||
|  | import chardet | ||||||
| import sys | import sys | ||||||
| import time | import time | ||||||
| import pyrrent2http |  | ||||||
| import xbmc | import xbmc | ||||||
| from error import Error | 
 | ||||||
| from . import SessionStatus, FileStatus, PeerInfo, Encryption | from . import SessionStatus, FileStatus, PeerInfo | ||||||
| from util import can_bind, find_free_port, localize_path, uri2path, detect_media_type | from . import pyrrent2http | ||||||
| import threading | from .error import Error | ||||||
| import urllib | from .structs import Encryption | ||||||
| import chardet | from .util import can_bind, find_free_port, localize_path, uri2path, detect_media_type | ||||||
| 
 | 
 | ||||||
| LOGGING = True | LOGGING = True | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| class Engine: | class Engine: | ||||||
|     """ |     """ | ||||||
|     This is python binding class to pyrrent2http client. |     This is python binding class to pyrrent2http client. | ||||||
|     """ |     """ | ||||||
|  | 
 | ||||||
|     def _log(self, message): |     def _log(self, message): | ||||||
|         if self.logger: |         if self.logger: | ||||||
|             self.logger(message) |             self.logger(message) | ||||||
|         else: |         else: | ||||||
|             xbmc.log("[pyrrent2http] %s" % message) |             xbmc.log("[pyrrent2http] %s" % message) | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     def __init__(self, uri=None, platform=None, download_path=".", |     def __init__(self, uri=None, platform=None, download_path=".", | ||||||
|                  bind_host='127.0.0.1', bind_port=5001, connections_limit=None, download_kbps=None, upload_kbps=None, |                  bind_host='127.0.0.1', bind_port=5001, connections_limit=200, download_kbps=-1, upload_kbps=-1, | ||||||
|                  enable_dht=True, enable_lsd=True, enable_natpmp=True, enable_upnp=True, enable_scrape=False, |                  enable_dht=True, enable_lsd=True, enable_natpmp=True, enable_upnp=True, enable_scrape=False, | ||||||
|                  log_stats=False, encryption=Encryption.ENABLED, keep_complete=False, keep_incomplete=False, |                  log_stats=False, encryption=Encryption.ENABLED, keep_complete=False, keep_incomplete=False, | ||||||
|                  keep_files=False, log_files_progress=False, log_overall_progress=False, log_pieces_progress=False, |                  keep_files=False, log_files_progress=False, log_overall_progress=False, log_pieces_progress=False, | ||||||
| @ -208,15 +214,19 @@ class Engine: | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         self._log("Invoking pyrrent2http") |         self._log("Invoking pyrrent2http") | ||||||
|  | 
 | ||||||
|         class Logging(object): |         class Logging(object): | ||||||
|             def __init__(self, _log): |             def __init__(self, _log): | ||||||
|                 self._log = _log |                 self._log = _log | ||||||
|  | 
 | ||||||
|             def info(self, message): |             def info(self, message): | ||||||
|                 if LOGGING: |                 if LOGGING: | ||||||
|                     self._log('INFO: %s' % (message,)) |                     self._log('INFO: %s' % (message,)) | ||||||
|  | 
 | ||||||
|             def error(self, message): |             def error(self, message): | ||||||
|                 if LOGGING: |                 if LOGGING: | ||||||
|                     self._log('ERROR: %s' % (message,)) |                     self._log('ERROR: %s' % (message,)) | ||||||
|  | 
 | ||||||
|         pyrrent2http.logging = Logging(self._log) |         pyrrent2http.logging = Logging(self._log) | ||||||
| 
 | 
 | ||||||
|         self.pyrrent2http = pyrrent2http.Pyrrent2http(**kwargs) |         self.pyrrent2http = pyrrent2http.Pyrrent2http(**kwargs) | ||||||
| @ -227,7 +237,6 @@ class Engine: | |||||||
|         self.pyrrent2http_loop = threading.Thread(target=self.pyrrent2http.loop) |         self.pyrrent2http_loop = threading.Thread(target=self.pyrrent2http.loop) | ||||||
|         self.pyrrent2http_loop.start() |         self.pyrrent2http_loop.start() | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|         start = time.time() |         start = time.time() | ||||||
|         self.started = True |         self.started = True | ||||||
|         initialized = False |         initialized = False | ||||||
| @ -252,6 +261,7 @@ class Engine: | |||||||
| 
 | 
 | ||||||
|     def pause(self): |     def pause(self): | ||||||
|         self.pyrrent2http.pause = True |         self.pyrrent2http.pause = True | ||||||
|  | 
 | ||||||
|     def resume(self): |     def resume(self): | ||||||
|         self.pyrrent2http.pause = False |         self.pyrrent2http.pause = False | ||||||
| 
 | 
 | ||||||
| @ -279,8 +289,6 @@ class Engine: | |||||||
|         status = SessionStatus(**status) |         status = SessionStatus(**status) | ||||||
|         return status |         return status | ||||||
| 
 | 
 | ||||||
|      |  | ||||||
| 
 |  | ||||||
|     def list(self, media_types=None, timeout=10): |     def list(self, media_types=None, timeout=10): | ||||||
|         """ |         """ | ||||||
|         Returns list of files in the torrent (see FileStatus named tuple). |         Returns list of files in the torrent (see FileStatus named tuple). | ||||||
| @ -296,8 +304,9 @@ class Engine: | |||||||
|         if files: |         if files: | ||||||
|             res = [FileStatus(index=index, **f) for index, f in enumerate(files)] |             res = [FileStatus(index=index, **f) for index, f in enumerate(files)] | ||||||
|             if media_types is not None: |             if media_types is not None: | ||||||
|                 res = filter(lambda fs: fs.media_type in media_types, res) |                 res = [fs for fs in res if fs.media_type in media_types] | ||||||
|             return res |             return res | ||||||
|  | 
 | ||||||
|     def list_from_info(self, media_types=None): |     def list_from_info(self, media_types=None): | ||||||
|         try: |         try: | ||||||
|             info = pyrrent2http.lt.torrent_info(uri2path(self.uri)) |             info = pyrrent2http.lt.torrent_info(uri2path(self.uri)) | ||||||
| @ -306,21 +315,21 @@ class Engine: | |||||||
|         files = [] |         files = [] | ||||||
|         for i in range(info.num_files()): |         for i in range(info.num_files()): | ||||||
|             f = info.file_at(i) |             f = info.file_at(i) | ||||||
|             Url = 'http://' + "%s:%s" % (self.bind_host, self.bind_port) + '/files/' + urllib.quote(f.path) |             Url = 'http://' + "%s:%s" % (self.bind_host, self.bind_port) + '/files/' + urllib.parse.quote(f.path) | ||||||
|             files.append({ |             files.append({ | ||||||
|                 'name': localize_path(f.path), |                 'name': localize_path(f.path), | ||||||
|                 'size': f.size, |                 'size': f.size, | ||||||
|                 'offset': f.offset, |                 'offset': f.offset, | ||||||
|                      'media_type': media_types and detect_media_type(f.path.decode(chardet.detect(f.path)['encoding'])) or '', |                 'media_type': media_types is not None and detect_media_type(f.path) or '', | ||||||
|                 'download': 0, |                 'download': 0, | ||||||
|                 'progress': 0.0, |                 'progress': 0.0, | ||||||
|                 'save_path': '', |                 'save_path': '', | ||||||
|                 'url': Url |                 'url': Url | ||||||
|             }) |             }) | ||||||
|         if files: |         if len(files) > 0: | ||||||
|             res = [FileStatus(index=index, **f) for index, f in enumerate(files)] |             res = [FileStatus(index=index, **f) for index, f in enumerate(files)] | ||||||
|         if media_types is not None: |         if media_types is not None: | ||||||
|                 res = filter(lambda fs: fs.media_type in media_types, res) |             res = [fs for fs in res if fs.media_type in media_types] | ||||||
|         return res |         return res | ||||||
| 
 | 
 | ||||||
|     def file_status(self, file_index, timeout=10): |     def file_status(self, file_index, timeout=10): | ||||||
| @ -355,6 +364,7 @@ class Engine: | |||||||
| 
 | 
 | ||||||
|     def is_alive(self): |     def is_alive(self): | ||||||
|         return self.pyrrent2http_loop.is_alive() |         return self.pyrrent2http_loop.is_alive() | ||||||
|  | 
 | ||||||
|     def wait_on_close(self, wait_timeout=10): |     def wait_on_close(self, wait_timeout=10): | ||||||
|         """ |         """ | ||||||
|         By default, close() method sends shutdown command to pyrrent2http, stops logging and returns immediately, not |         By default, close() method sends shutdown command to pyrrent2http, stops logging and returns immediately, not | ||||||
|  | |||||||
| @ -1,613 +0,0 @@ | |||||||
| """Guess the MIME type of a file. |  | ||||||
| 
 |  | ||||||
| This module defines two useful functions: |  | ||||||
| 
 |  | ||||||
| guess_type(url, strict=1) -- guess the MIME type and encoding of a URL. |  | ||||||
| 
 |  | ||||||
| guess_extension(type, strict=1) -- guess the extension for a given MIME type. |  | ||||||
| 
 |  | ||||||
| It also contains the following, for tuning the behavior: |  | ||||||
| 
 |  | ||||||
| Data: |  | ||||||
| 
 |  | ||||||
| knownfiles -- list of files to parse |  | ||||||
| inited -- flag set when init() has been called |  | ||||||
| suffix_map -- dictionary mapping suffixes to suffixes |  | ||||||
| encodings_map -- dictionary mapping suffixes to encodings |  | ||||||
| types_map -- dictionary mapping suffixes to types |  | ||||||
| 
 |  | ||||||
| Functions: |  | ||||||
| 
 |  | ||||||
| init([files]) -- parse a list of files, default knownfiles (on Windows, the |  | ||||||
|   default values are taken from the registry) |  | ||||||
| read_mime_types(file) -- parse one file, return a dictionary or None |  | ||||||
| """ |  | ||||||
| 
 |  | ||||||
| import os |  | ||||||
| import sys |  | ||||||
| import posixpath |  | ||||||
| import urllib |  | ||||||
| try: |  | ||||||
|     import _winreg |  | ||||||
| except ImportError: |  | ||||||
|     _winreg = None |  | ||||||
| 
 |  | ||||||
| __all__ = [ |  | ||||||
|     "guess_type","guess_extension","guess_all_extensions", |  | ||||||
|     "add_type","read_mime_types","init" |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| knownfiles = [ |  | ||||||
|     "/etc/mime.types", |  | ||||||
|     "/etc/httpd/mime.types",                    # Mac OS X |  | ||||||
|     "/etc/httpd/conf/mime.types",               # Apache |  | ||||||
|     "/etc/apache/mime.types",                   # Apache 1 |  | ||||||
|     "/etc/apache2/mime.types",                  # Apache 2 |  | ||||||
|     "/usr/local/etc/httpd/conf/mime.types", |  | ||||||
|     "/usr/local/lib/netscape/mime.types", |  | ||||||
|     "/usr/local/etc/httpd/conf/mime.types",     # Apache 1.2 |  | ||||||
|     "/usr/local/etc/mime.types",                # Apache 1.3 |  | ||||||
|     ] |  | ||||||
| 
 |  | ||||||
| inited = False |  | ||||||
| _db = None |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class MimeTypes: |  | ||||||
|     """MIME-types datastore. |  | ||||||
| 
 |  | ||||||
|     This datastore can handle information from mime.types-style files |  | ||||||
|     and supports basic determination of MIME type from a filename or |  | ||||||
|     URL, and can guess a reasonable extension given a MIME type. |  | ||||||
|     """ |  | ||||||
| 
 |  | ||||||
|     def __init__(self, filenames=(), strict=True): |  | ||||||
|         if not inited: |  | ||||||
|             init() |  | ||||||
|         self.encodings_map = encodings_map.copy() |  | ||||||
|         self.suffix_map = suffix_map.copy() |  | ||||||
|         self.types_map = ({}, {}) # dict for (non-strict, strict) |  | ||||||
|         self.types_map_inv = ({}, {}) |  | ||||||
|         for (ext, type) in types_map.items(): |  | ||||||
|             self.add_type(type, ext, True) |  | ||||||
|         for (ext, type) in common_types.items(): |  | ||||||
|             self.add_type(type, ext, False) |  | ||||||
|         for name in filenames: |  | ||||||
|             self.read(name, strict) |  | ||||||
| 
 |  | ||||||
|     def add_type(self, type, ext, strict=True): |  | ||||||
|         """Add a mapping between a type and an extension. |  | ||||||
| 
 |  | ||||||
|         When the extension is already known, the new |  | ||||||
|         type will replace the old one. When the type |  | ||||||
|         is already known the extension will be added |  | ||||||
|         to the list of known extensions. |  | ||||||
| 
 |  | ||||||
|         If strict is true, information will be added to |  | ||||||
|         list of standard types, else to the list of non-standard |  | ||||||
|         types. |  | ||||||
|         """ |  | ||||||
|         self.types_map[strict][ext] = type |  | ||||||
|         exts = self.types_map_inv[strict].setdefault(type, []) |  | ||||||
|         if ext not in exts: |  | ||||||
|             exts.append(ext) |  | ||||||
| 
 |  | ||||||
|     def guess_type(self, url, strict=True): |  | ||||||
|         """Guess the type of a file based on its URL. |  | ||||||
| 
 |  | ||||||
|         Return value is a tuple (type, encoding) where type is None if |  | ||||||
|         the type can't be guessed (no or unknown suffix) or a string |  | ||||||
|         of the form type/subtype, usable for a MIME Content-type |  | ||||||
|         header; and encoding is None for no encoding or the name of |  | ||||||
|         the program used to encode (e.g. compress or gzip).  The |  | ||||||
|         mappings are table driven.  Encoding suffixes are case |  | ||||||
|         sensitive; type suffixes are first tried case sensitive, then |  | ||||||
|         case insensitive. |  | ||||||
| 
 |  | ||||||
|         The suffixes .tgz, .taz and .tz (case sensitive!) are all |  | ||||||
|         mapped to '.tar.gz'.  (This is table-driven too, using the |  | ||||||
|         dictionary suffix_map.) |  | ||||||
| 
 |  | ||||||
|         Optional `strict' argument when False adds a bunch of commonly found, |  | ||||||
|         but non-standard types. |  | ||||||
|         """ |  | ||||||
|         scheme, url = urllib.splittype(url) |  | ||||||
|         if scheme == 'data': |  | ||||||
|             # syntax of data URLs: |  | ||||||
|             # dataurl   := "data:" [ mediatype ] [ ";base64" ] "," data |  | ||||||
|             # mediatype := [ type "/" subtype ] *( ";" parameter ) |  | ||||||
|             # data      := *urlchar |  | ||||||
|             # parameter := attribute "=" value |  | ||||||
|             # type/subtype defaults to "text/plain" |  | ||||||
|             comma = url.find(',') |  | ||||||
|             if comma < 0: |  | ||||||
|                 # bad data URL |  | ||||||
|                 return None, None |  | ||||||
|             semi = url.find(';', 0, comma) |  | ||||||
|             if semi >= 0: |  | ||||||
|                 type = url[:semi] |  | ||||||
|             else: |  | ||||||
|                 type = url[:comma] |  | ||||||
|             if '=' in type or '/' not in type: |  | ||||||
|                 type = 'text/plain' |  | ||||||
|             return type, None           # never compressed, so encoding is None |  | ||||||
|         base, ext = posixpath.splitext(url) |  | ||||||
|         while ext in self.suffix_map: |  | ||||||
|             base, ext = posixpath.splitext(base + self.suffix_map[ext]) |  | ||||||
|         if ext in self.encodings_map: |  | ||||||
|             encoding = self.encodings_map[ext] |  | ||||||
|             base, ext = posixpath.splitext(base) |  | ||||||
|         else: |  | ||||||
|             encoding = None |  | ||||||
|         types_map = self.types_map[True] |  | ||||||
|         if ext in types_map: |  | ||||||
|             return types_map[ext], encoding |  | ||||||
|         elif ext.lower() in types_map: |  | ||||||
|             return types_map[ext.lower()], encoding |  | ||||||
|         elif strict: |  | ||||||
|             return None, encoding |  | ||||||
|         types_map = self.types_map[False] |  | ||||||
|         if ext in types_map: |  | ||||||
|             return types_map[ext], encoding |  | ||||||
|         elif ext.lower() in types_map: |  | ||||||
|             return types_map[ext.lower()], encoding |  | ||||||
|         else: |  | ||||||
|             return None, encoding |  | ||||||
| 
 |  | ||||||
|     def guess_all_extensions(self, type, strict=True): |  | ||||||
|         """Guess the extensions for a file based on its MIME type. |  | ||||||
| 
 |  | ||||||
|         Return value is a list of strings giving the possible filename |  | ||||||
|         extensions, including the leading dot ('.').  The extension is not |  | ||||||
|         guaranteed to have been associated with any particular data stream, |  | ||||||
|         but would be mapped to the MIME type `type' by guess_type(). |  | ||||||
| 
 |  | ||||||
|         Optional `strict' argument when false adds a bunch of commonly found, |  | ||||||
|         but non-standard types. |  | ||||||
|         """ |  | ||||||
|         type = type.lower() |  | ||||||
|         extensions = self.types_map_inv[True].get(type, []) |  | ||||||
|         if not strict: |  | ||||||
|             for ext in self.types_map_inv[False].get(type, []): |  | ||||||
|                 if ext not in extensions: |  | ||||||
|                     extensions.append(ext) |  | ||||||
|         return extensions |  | ||||||
| 
 |  | ||||||
|     def guess_extension(self, type, strict=True): |  | ||||||
|         """Guess the extension for a file based on its MIME type. |  | ||||||
| 
 |  | ||||||
|         Return value is a string giving a filename extension, |  | ||||||
|         including the leading dot ('.').  The extension is not |  | ||||||
|         guaranteed to have been associated with any particular data |  | ||||||
|         stream, but would be mapped to the MIME type `type' by |  | ||||||
|         guess_type().  If no extension can be guessed for `type', None |  | ||||||
|         is returned. |  | ||||||
| 
 |  | ||||||
|         Optional `strict' argument when false adds a bunch of commonly found, |  | ||||||
|         but non-standard types. |  | ||||||
|         """ |  | ||||||
|         extensions = self.guess_all_extensions(type, strict) |  | ||||||
|         if not extensions: |  | ||||||
|             return None |  | ||||||
|         return extensions[0] |  | ||||||
| 
 |  | ||||||
|     def read(self, filename, strict=True): |  | ||||||
|         """ |  | ||||||
|         Read a single mime.types-format file, specified by pathname. |  | ||||||
| 
 |  | ||||||
|         If strict is true, information will be added to |  | ||||||
|         list of standard types, else to the list of non-standard |  | ||||||
|         types. |  | ||||||
|         """ |  | ||||||
|         with open(filename) as fp: |  | ||||||
|             self.readfp(fp, strict) |  | ||||||
| 
 |  | ||||||
|     def readfp(self, fp, strict=True): |  | ||||||
|         """ |  | ||||||
|         Read a single mime.types-format file. |  | ||||||
| 
 |  | ||||||
|         If strict is true, information will be added to |  | ||||||
|         list of standard types, else to the list of non-standard |  | ||||||
|         types. |  | ||||||
|         """ |  | ||||||
|         while 1: |  | ||||||
|             line = fp.readline() |  | ||||||
|             if not line: |  | ||||||
|                 break |  | ||||||
|             words = line.split() |  | ||||||
|             for i in range(len(words)): |  | ||||||
|                 if words[i][0] == '#': |  | ||||||
|                     del words[i:] |  | ||||||
|                     break |  | ||||||
|             if not words: |  | ||||||
|                 continue |  | ||||||
|             type, suffixes = words[0], words[1:] |  | ||||||
|             for suff in suffixes: |  | ||||||
|                 self.add_type(type, '.' + suff, strict) |  | ||||||
| 
 |  | ||||||
|     def read_windows_registry(self, strict=True): |  | ||||||
|         """ |  | ||||||
|         Load the MIME types database from Windows registry. |  | ||||||
| 
 |  | ||||||
|         If strict is true, information will be added to |  | ||||||
|         list of standard types, else to the list of non-standard |  | ||||||
|         types. |  | ||||||
|         """ |  | ||||||
| 
 |  | ||||||
|         # Windows only |  | ||||||
|         if not _winreg: |  | ||||||
|             return |  | ||||||
| 
 |  | ||||||
|         def enum_types(mimedb): |  | ||||||
|             i = 0 |  | ||||||
|             while True: |  | ||||||
|                 try: |  | ||||||
|                     ctype = _winreg.EnumKey(mimedb, i) |  | ||||||
|                 except EnvironmentError: |  | ||||||
|                     break |  | ||||||
|                 try: |  | ||||||
|                     ctype = ctype#.encode(default_encoding) # omit in 3.x! |  | ||||||
|                 except UnicodeEncodeError: |  | ||||||
|                     pass |  | ||||||
|                 else: |  | ||||||
|                     yield ctype |  | ||||||
|                 i += 1 |  | ||||||
| 
 |  | ||||||
|         default_encoding = sys.getdefaultencoding() |  | ||||||
|         with _winreg.OpenKey(_winreg.HKEY_CLASSES_ROOT, |  | ||||||
|                              r'MIME\Database\Content Type') as mimedb: |  | ||||||
|             for ctype in enum_types(mimedb): |  | ||||||
|                 try: |  | ||||||
|                     with _winreg.OpenKey(mimedb, ctype) as key: |  | ||||||
|                         suffix, datatype = _winreg.QueryValueEx(key, |  | ||||||
|                                                                 'Extension') |  | ||||||
|                 except EnvironmentError: |  | ||||||
|                     continue |  | ||||||
|                 if datatype != _winreg.REG_SZ: |  | ||||||
|                     continue |  | ||||||
|                 try: |  | ||||||
|                     suffix = suffix.encode(default_encoding) # omit in 3.x! |  | ||||||
|                 except UnicodeEncodeError: |  | ||||||
|                     continue |  | ||||||
|                 self.add_type(ctype, suffix, strict) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def guess_type(url, strict=True): |  | ||||||
|     """Guess the type of a file based on its URL. |  | ||||||
| 
 |  | ||||||
|     Return value is a tuple (type, encoding) where type is None if the |  | ||||||
|     type can't be guessed (no or unknown suffix) or a string of the |  | ||||||
|     form type/subtype, usable for a MIME Content-type header; and |  | ||||||
|     encoding is None for no encoding or the name of the program used |  | ||||||
|     to encode (e.g. compress or gzip).  The mappings are table |  | ||||||
|     driven.  Encoding suffixes are case sensitive; type suffixes are |  | ||||||
|     first tried case sensitive, then case insensitive. |  | ||||||
| 
 |  | ||||||
|     The suffixes .tgz, .taz and .tz (case sensitive!) are all mapped |  | ||||||
|     to ".tar.gz".  (This is table-driven too, using the dictionary |  | ||||||
|     suffix_map). |  | ||||||
| 
 |  | ||||||
|     Optional `strict' argument when false adds a bunch of commonly found, but |  | ||||||
|     non-standard types. |  | ||||||
|     """ |  | ||||||
|     if _db is None: |  | ||||||
|         init() |  | ||||||
|     return _db.guess_type(url, strict) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def guess_all_extensions(type, strict=True): |  | ||||||
|     """Guess the extensions for a file based on its MIME type. |  | ||||||
| 
 |  | ||||||
|     Return value is a list of strings giving the possible filename |  | ||||||
|     extensions, including the leading dot ('.').  The extension is not |  | ||||||
|     guaranteed to have been associated with any particular data |  | ||||||
|     stream, but would be mapped to the MIME type `type' by |  | ||||||
|     guess_type().  If no extension can be guessed for `type', None |  | ||||||
|     is returned. |  | ||||||
| 
 |  | ||||||
|     Optional `strict' argument when false adds a bunch of commonly found, |  | ||||||
|     but non-standard types. |  | ||||||
|     """ |  | ||||||
|     if _db is None: |  | ||||||
|         init() |  | ||||||
|     return _db.guess_all_extensions(type, strict) |  | ||||||
| 
 |  | ||||||
| def guess_extension(type, strict=True): |  | ||||||
|     """Guess the extension for a file based on its MIME type. |  | ||||||
| 
 |  | ||||||
|     Return value is a string giving a filename extension, including the |  | ||||||
|     leading dot ('.').  The extension is not guaranteed to have been |  | ||||||
|     associated with any particular data stream, but would be mapped to the |  | ||||||
|     MIME type `type' by guess_type().  If no extension can be guessed for |  | ||||||
|     `type', None is returned. |  | ||||||
| 
 |  | ||||||
|     Optional `strict' argument when false adds a bunch of commonly found, |  | ||||||
|     but non-standard types. |  | ||||||
|     """ |  | ||||||
|     if _db is None: |  | ||||||
|         init() |  | ||||||
|     return _db.guess_extension(type, strict) |  | ||||||
| 
 |  | ||||||
| def add_type(type, ext, strict=True): |  | ||||||
|     """Add a mapping between a type and an extension. |  | ||||||
| 
 |  | ||||||
|     When the extension is already known, the new |  | ||||||
|     type will replace the old one. When the type |  | ||||||
|     is already known the extension will be added |  | ||||||
|     to the list of known extensions. |  | ||||||
| 
 |  | ||||||
|     If strict is true, information will be added to |  | ||||||
|     list of standard types, else to the list of non-standard |  | ||||||
|     types. |  | ||||||
|     """ |  | ||||||
|     if _db is None: |  | ||||||
|         init() |  | ||||||
|     return _db.add_type(type, ext, strict) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def init(files=None): |  | ||||||
|     global suffix_map, types_map, encodings_map, common_types |  | ||||||
|     global inited, _db |  | ||||||
|     inited = True    # so that MimeTypes.__init__() doesn't call us again |  | ||||||
|     db = MimeTypes() |  | ||||||
|     if files is None: |  | ||||||
|         if _winreg: |  | ||||||
|             db.read_windows_registry() |  | ||||||
|         files = knownfiles |  | ||||||
|     for file in files: |  | ||||||
|         if os.path.isfile(file): |  | ||||||
|             db.read(file) |  | ||||||
|     encodings_map = db.encodings_map |  | ||||||
|     suffix_map = db.suffix_map |  | ||||||
|     types_map = db.types_map[True] |  | ||||||
|     common_types = db.types_map[False] |  | ||||||
|     # Make the DB a global variable now that it is fully initialized |  | ||||||
|     _db = db |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def read_mime_types(file): |  | ||||||
|     try: |  | ||||||
|         f = open(file) |  | ||||||
|     except IOError: |  | ||||||
|         return None |  | ||||||
|     db = MimeTypes() |  | ||||||
|     db.readfp(f, True) |  | ||||||
|     return db.types_map[True] |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def _default_mime_types(): |  | ||||||
|     global suffix_map |  | ||||||
|     global encodings_map |  | ||||||
|     global types_map |  | ||||||
|     global common_types |  | ||||||
| 
 |  | ||||||
|     suffix_map = { |  | ||||||
|         '.tgz': '.tar.gz', |  | ||||||
|         '.taz': '.tar.gz', |  | ||||||
|         '.tz': '.tar.gz', |  | ||||||
|         '.tbz2': '.tar.bz2', |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     encodings_map = { |  | ||||||
|         '.gz': 'gzip', |  | ||||||
|         '.Z': 'compress', |  | ||||||
|         '.bz2': 'bzip2', |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     # Before adding new types, make sure they are either registered with IANA, |  | ||||||
|     # at http://www.isi.edu/in-notes/iana/assignments/media-types |  | ||||||
|     # or extensions, i.e. using the x- prefix |  | ||||||
| 
 |  | ||||||
|     # If you add to these, please keep them sorted! |  | ||||||
|     types_map = { |  | ||||||
|         '.3gp'    : 'video/3gp', |  | ||||||
|         '.a'      : 'application/octet-stream', |  | ||||||
|         '.ai'     : 'application/postscript', |  | ||||||
|         '.aif'    : 'audio/x-aiff', |  | ||||||
|         '.aifc'   : 'audio/x-aiff', |  | ||||||
|         '.aiff'   : 'audio/x-aiff', |  | ||||||
|         '.asf'    : 'video/x-ms-asf', |  | ||||||
|         '.asx'    : 'video/x-ms-asf', |  | ||||||
|         '.au'     : 'audio/basic', |  | ||||||
|         '.avi'    : 'video/x-msvideo', |  | ||||||
|         '.axv'    : 'video/annodex', |  | ||||||
|         '.bat'    : 'text/plain', |  | ||||||
|         '.bcpio'  : 'application/x-bcpio', |  | ||||||
|         '.bin'    : 'application/octet-stream', |  | ||||||
|         '.bmp'    : 'image/x-ms-bmp', |  | ||||||
|         '.c'      : 'text/plain', |  | ||||||
|         # Duplicates :( |  | ||||||
|         '.cdf'    : 'application/x-cdf', |  | ||||||
|         '.cdf'    : 'application/x-netcdf', |  | ||||||
|         '.cpio'   : 'application/x-cpio', |  | ||||||
|         '.csh'    : 'application/x-csh', |  | ||||||
|         '.css'    : 'text/css', |  | ||||||
|         '.dif'    : 'video/dv', |  | ||||||
|         '.dl'     : 'video/dl', |  | ||||||
|         '.dll'    : 'application/octet-stream', |  | ||||||
|         '.dv'     : 'video/dv', |  | ||||||
|         '.doc'    : 'application/msword', |  | ||||||
|         '.dot'    : 'application/msword', |  | ||||||
|         '.dvi'    : 'application/x-dvi', |  | ||||||
|         '.eml'    : 'message/rfc822', |  | ||||||
|         '.eps'    : 'application/postscript', |  | ||||||
|         '.etx'    : 'text/x-setext', |  | ||||||
|         '.exe'    : 'application/octet-stream', |  | ||||||
|         '.fli'    : 'video/fli', |  | ||||||
|         '.flv'    : 'video/x-flv', |  | ||||||
|         '.gif'    : 'image/gif', |  | ||||||
|         '.gl'     : 'video/gl', |  | ||||||
|         '.gtar'   : 'application/x-gtar', |  | ||||||
|         '.h'      : 'text/plain', |  | ||||||
|         '.hdf'    : 'application/x-hdf', |  | ||||||
|         '.htm'    : 'text/html', |  | ||||||
|         '.html'   : 'text/html', |  | ||||||
|         '.ief'    : 'image/ief', |  | ||||||
|         '.jpe'    : 'image/jpeg', |  | ||||||
|         '.jpeg'   : 'image/jpeg', |  | ||||||
|         '.jpg'    : 'image/jpeg', |  | ||||||
|         '.js'     : 'application/x-javascript', |  | ||||||
|         '.ksh'    : 'text/plain', |  | ||||||
|         '.latex'  : 'application/x-latex', |  | ||||||
|         '.lsf'    : 'video/x-la-lsf', |  | ||||||
|         '.lsx'    : 'video/x-la-lsf', |  | ||||||
|         '.m1v'    : 'video/mpeg', |  | ||||||
|         '.man'    : 'application/x-troff-man', |  | ||||||
|         '.me'     : 'application/x-troff-me', |  | ||||||
|         '.mht'    : 'message/rfc822', |  | ||||||
|         '.mhtml'  : 'message/rfc822', |  | ||||||
|         '.mif'    : 'application/x-mif', |  | ||||||
|         '.mng'    : 'video/x-mng', |  | ||||||
|         '.movie'  : 'video/x-sgi-movie', |  | ||||||
|         '.mp2'    : 'audio/mpeg', |  | ||||||
|         '.mp3'    : 'audio/mpeg', |  | ||||||
|         '.mp4'    : 'video/mp4', |  | ||||||
|         '.mpa'    : 'video/mpeg', |  | ||||||
|         '.mpe'    : 'video/mpeg', |  | ||||||
|         '.mpeg'   : 'video/mpeg', |  | ||||||
|         '.mpg'    : 'video/mpeg', |  | ||||||
|         '.mpv'    : 'video/matroska', |  | ||||||
|         '.mkv'    : 'video/matroska', |  | ||||||
|         '.mov'    : 'video/quicktime', |  | ||||||
|         '.ms'     : 'application/x-troff-ms', |  | ||||||
|         '.nc'     : 'application/x-netcdf', |  | ||||||
|         '.nws'    : 'message/rfc822', |  | ||||||
|         '.o'      : 'application/octet-stream', |  | ||||||
|         '.obj'    : 'application/octet-stream', |  | ||||||
|         '.oda'    : 'application/oda', |  | ||||||
|         '.ogv'    : 'video/ogg', |  | ||||||
|         '.p12'    : 'application/x-pkcs12', |  | ||||||
|         '.p7c'    : 'application/pkcs7-mime', |  | ||||||
|         '.pbm'    : 'image/x-portable-bitmap', |  | ||||||
|         '.pdf'    : 'application/pdf', |  | ||||||
|         '.pfx'    : 'application/x-pkcs12', |  | ||||||
|         '.pgm'    : 'image/x-portable-graymap', |  | ||||||
|         '.pl'     : 'text/plain', |  | ||||||
|         '.png'    : 'image/png', |  | ||||||
|         '.pnm'    : 'image/x-portable-anymap', |  | ||||||
|         '.pot'    : 'application/vnd.ms-powerpoint', |  | ||||||
|         '.ppa'    : 'application/vnd.ms-powerpoint', |  | ||||||
|         '.ppm'    : 'image/x-portable-pixmap', |  | ||||||
|         '.pps'    : 'application/vnd.ms-powerpoint', |  | ||||||
|         '.ppt'    : 'application/vnd.ms-powerpoint', |  | ||||||
|         '.ps'     : 'application/postscript', |  | ||||||
|         '.pwz'    : 'application/vnd.ms-powerpoint', |  | ||||||
|         '.py'     : 'text/x-python', |  | ||||||
|         '.pyc'    : 'application/x-python-code', |  | ||||||
|         '.pyo'    : 'application/x-python-code', |  | ||||||
|         '.qt'     : 'video/quicktime', |  | ||||||
|         '.ra'     : 'audio/x-pn-realaudio', |  | ||||||
|         '.ram'    : 'application/x-pn-realaudio', |  | ||||||
|         '.ras'    : 'image/x-cmu-raster', |  | ||||||
|         '.rdf'    : 'application/xml', |  | ||||||
|         '.rgb'    : 'image/x-rgb', |  | ||||||
|         '.roff'   : 'application/x-troff', |  | ||||||
|         '.rtx'    : 'text/richtext', |  | ||||||
|         '.sgm'    : 'text/x-sgml', |  | ||||||
|         '.sgml'   : 'text/x-sgml', |  | ||||||
|         '.sh'     : 'application/x-sh', |  | ||||||
|         '.shar'   : 'application/x-shar', |  | ||||||
|         '.snd'    : 'audio/basic', |  | ||||||
|         '.so'     : 'application/octet-stream', |  | ||||||
|         '.src'    : 'application/x-wais-source', |  | ||||||
|         '.sv4cpio': 'application/x-sv4cpio', |  | ||||||
|         '.sv4crc' : 'application/x-sv4crc', |  | ||||||
|         '.swf'    : 'application/x-shockwave-flash', |  | ||||||
|         '.t'      : 'application/x-troff', |  | ||||||
|         '.tar'    : 'application/x-tar', |  | ||||||
|         '.tcl'    : 'application/x-tcl', |  | ||||||
|         '.tex'    : 'application/x-tex', |  | ||||||
|         '.texi'   : 'application/x-texinfo', |  | ||||||
|         '.texinfo': 'application/x-texinfo', |  | ||||||
|         '.tif'    : 'image/tiff', |  | ||||||
|         '.tiff'   : 'image/tiff', |  | ||||||
|         '.tr'     : 'application/x-troff', |  | ||||||
|         '.ts'     : 'video/MP2T', |  | ||||||
|         '.tsv'    : 'text/tab-separated-values', |  | ||||||
|         '.txt'    : 'text/plain', |  | ||||||
|         '.ustar'  : 'application/x-ustar', |  | ||||||
|         '.vcf'    : 'text/x-vcard', |  | ||||||
|         '.wav'    : 'audio/x-wav', |  | ||||||
|         '.webm'   : 'video/webm', |  | ||||||
|         '.wiz'    : 'application/msword', |  | ||||||
|         '.wm'     : 'video/x-ms-wm', |  | ||||||
|         '.wmv'    : 'video/x-ms-wmv', |  | ||||||
|         '.wmx'    : 'video/x-ms-wmx', |  | ||||||
|         '.wvx'    : 'video/x-ms-wvx', |  | ||||||
|         '.wsdl'   : 'application/xml', |  | ||||||
|         '.xbm'    : 'image/x-xbitmap', |  | ||||||
|         '.xlb'    : 'application/vnd.ms-excel', |  | ||||||
|         # Duplicates :( |  | ||||||
|         '.xls'    : 'application/excel', |  | ||||||
|         '.xls'    : 'application/vnd.ms-excel', |  | ||||||
|         '.xml'    : 'text/xml', |  | ||||||
|         '.xpdl'   : 'application/xml', |  | ||||||
|         '.xpm'    : 'image/x-xpixmap', |  | ||||||
|         '.xsl'    : 'application/xml', |  | ||||||
|         '.xwd'    : 'image/x-xwindowdump', |  | ||||||
|         '.zip'    : 'application/zip', |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|     # These are non-standard types, commonly found in the wild.  They will |  | ||||||
|     # only match if strict=0 flag is given to the API methods. |  | ||||||
| 
 |  | ||||||
|     # Please sort these too |  | ||||||
|     common_types = { |  | ||||||
|         '.jpg' : 'image/jpg', |  | ||||||
|         '.mid' : 'audio/midi', |  | ||||||
|         '.midi': 'audio/midi', |  | ||||||
|         '.pct' : 'image/pict', |  | ||||||
|         '.pic' : 'image/pict', |  | ||||||
|         '.pict': 'image/pict', |  | ||||||
|         '.rtf' : 'application/rtf', |  | ||||||
|         '.xul' : 'text/xul' |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| _default_mime_types() |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| if __name__ == '__main__': |  | ||||||
|     import getopt |  | ||||||
| 
 |  | ||||||
|     USAGE = """\ |  | ||||||
| Usage: mimetypes.py [options] type |  | ||||||
| 
 |  | ||||||
| Options: |  | ||||||
|     --help / -h       -- print this message and exit |  | ||||||
|     --lenient / -l    -- additionally search of some common, but non-standard |  | ||||||
|                          types. |  | ||||||
|     --extension / -e  -- guess extension instead of type |  | ||||||
| 
 |  | ||||||
| More than one type argument may be given. |  | ||||||
| """ |  | ||||||
| 
 |  | ||||||
|     def usage(code, msg=''): |  | ||||||
|         print USAGE |  | ||||||
|         if msg: print msg |  | ||||||
|         sys.exit(code) |  | ||||||
| 
 |  | ||||||
|     try: |  | ||||||
|         opts, args = getopt.getopt(sys.argv[1:], 'hle', |  | ||||||
|                                    ['help', 'lenient', 'extension']) |  | ||||||
|     except getopt.error, msg: |  | ||||||
|         usage(1, msg) |  | ||||||
| 
 |  | ||||||
|     strict = 1 |  | ||||||
|     extension = 0 |  | ||||||
|     for opt, arg in opts: |  | ||||||
|         if opt in ('-h', '--help'): |  | ||||||
|             usage(0) |  | ||||||
|         elif opt in ('-l', '--lenient'): |  | ||||||
|             strict = 0 |  | ||||||
|         elif opt in ('-e', '--extension'): |  | ||||||
|             extension = 1 |  | ||||||
|     for gtype in args: |  | ||||||
|         if extension: |  | ||||||
|             guess = guess_extension(gtype, strict) |  | ||||||
|             if not guess: print "I don't know anything about type", gtype |  | ||||||
|             else: print guess |  | ||||||
|         else: |  | ||||||
|             guess, encoding = guess_type(gtype, strict) |  | ||||||
|             if not guess: print "I don't know anything about type", gtype |  | ||||||
|             else: print 'type:', guess, 'encoding:', encoding |  | ||||||
| @ -4,10 +4,11 @@ import chardet | |||||||
| 
 | 
 | ||||||
| try: | try: | ||||||
|     from python_libtorrent import get_libtorrent  # @UnresolvedImport |     from python_libtorrent import get_libtorrent  # @UnresolvedImport | ||||||
|  | 
 | ||||||
|     lt = get_libtorrent() |     lt = get_libtorrent() | ||||||
|     print('Imported libtorrent v%s from python_libtorrent' %(lt.version, )) |     print(('Imported libtorrent v%s from python_libtorrent' % (lt.version,))) | ||||||
| except Exception, e: | except Exception as e: | ||||||
|     print('Error importing python_libtorrent.Exception: %s' %(str(e),)) |     print(('Error importing python_libtorrent.Exception: %s' % (str(e),))) | ||||||
|     try: |     try: | ||||||
|         import libtorrent as lt  # @UnresolvedImport |         import libtorrent as lt  # @UnresolvedImport | ||||||
|     except Exception as e: |     except Exception as e: | ||||||
| @ -17,17 +18,16 @@ except Exception, e: | |||||||
| 
 | 
 | ||||||
| from random import SystemRandom | from random import SystemRandom | ||||||
| import time | import time | ||||||
| import urllib | import urllib.request, urllib.parse, urllib.error | ||||||
| import BaseHTTPServer | import http.server | ||||||
| import SocketServer | import socketserver | ||||||
| import threading | import threading | ||||||
| import io | import io | ||||||
| from util import localize_path, Struct, detect_media_type, uri2path, encode_msg | from .util import localize_path, Struct, detect_media_type, uri2path, encode_msg | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| if os.getenv('ANDROID_ROOT'): | if os.getenv('ANDROID_ROOT'): | ||||||
|     from ctypes import * |     from ctypes import * | ||||||
|  | 
 | ||||||
|     libc = CDLL('/system/lib/libc.so') |     libc = CDLL('/system/lib/libc.so') | ||||||
|     libc.lseek64.restype = c_ulonglong |     libc.lseek64.restype = c_ulonglong | ||||||
|     libc.lseek64.argtypes = [c_uint, c_ulonglong, c_uint] |     libc.lseek64.argtypes = [c_uint, c_ulonglong, c_uint] | ||||||
| @ -41,7 +41,7 @@ if os.getenv('ANDROID_ROOT'): | |||||||
| if not hasattr(os, 'getppid'): | if not hasattr(os, 'getppid'): | ||||||
|     import ctypes |     import ctypes | ||||||
| 
 | 
 | ||||||
|     TH32CS_SNAPPROCESS = 0x02L |     TH32CS_SNAPPROCESS = 0x02 | ||||||
|     CreateToolhelp32Snapshot = ctypes.windll.kernel32.CreateToolhelp32Snapshot  # @UndefinedVariable |     CreateToolhelp32Snapshot = ctypes.windll.kernel32.CreateToolhelp32Snapshot  # @UndefinedVariable | ||||||
|     GetCurrentProcessId = ctypes.windll.kernel32.GetCurrentProcessId  # @UndefinedVariable |     GetCurrentProcessId = ctypes.windll.kernel32.GetCurrentProcessId  # @UndefinedVariable | ||||||
| 
 | 
 | ||||||
| @ -50,6 +50,7 @@ if not hasattr(os, 'getppid'): | |||||||
|     _kernel32dll = ctypes.windll.Kernel32 |     _kernel32dll = ctypes.windll.Kernel32 | ||||||
|     CloseHandle = _kernel32dll.CloseHandle |     CloseHandle = _kernel32dll.CloseHandle | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|     class PROCESSENTRY32(ctypes.Structure): |     class PROCESSENTRY32(ctypes.Structure): | ||||||
|         _fields_ = [ |         _fields_ = [ | ||||||
|             ("dwSize", ctypes.c_ulong), |             ("dwSize", ctypes.c_ulong), | ||||||
| @ -65,9 +66,11 @@ if not hasattr(os, 'getppid'): | |||||||
|             ("szExeFile", ctypes.c_wchar * MAX_PATH) |             ("szExeFile", ctypes.c_wchar * MAX_PATH) | ||||||
|         ] |         ] | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|     Process32First = _kernel32dll.Process32FirstW |     Process32First = _kernel32dll.Process32FirstW | ||||||
|     Process32Next = _kernel32dll.Process32NextW |     Process32Next = _kernel32dll.Process32NextW | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|     def getppid(): |     def getppid(): | ||||||
|         ''' |         ''' | ||||||
|         :return: The pid of the parent of this process. |         :return: The pid of the parent of this process. | ||||||
| @ -93,6 +96,7 @@ if not hasattr(os, 'getppid'): | |||||||
| 
 | 
 | ||||||
|         return result |         return result | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|     os.getppid = getppid |     os.getppid = getppid | ||||||
| 
 | 
 | ||||||
| ################################################################################# | ################################################################################# | ||||||
| @ -105,6 +109,8 @@ USER_AGENT = 'libtorrent/1.0.9.0' | |||||||
| VIDEO_EXTS = {'.avi': 'video/x-msvideo', '.mp4': 'video/mp4', '.mkv': 'video/x-matroska', | VIDEO_EXTS = {'.avi': 'video/x-msvideo', '.mp4': 'video/mp4', '.mkv': 'video/x-matroska', | ||||||
|               '.m4v': 'video/mp4', '.mov': 'video/quicktime', '.mpg': 'video/mpeg', '.ogv': 'video/ogg', |               '.m4v': 'video/mp4', '.mov': 'video/quicktime', '.mpg': 'video/mpeg', '.ogv': 'video/ogg', | ||||||
|               '.ogg': 'video/ogg', '.webm': 'video/webm', '.ts': 'video/mp2t', '.3gp': 'video/3gpp'} |               '.ogg': 'video/ogg', '.webm': 'video/webm', '.ts': 'video/mp2t', '.3gp': 'video/3gpp'} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| ###################################################################################### | ###################################################################################### | ||||||
| 
 | 
 | ||||||
| class Ticker(object): | class Ticker(object): | ||||||
| @ -114,6 +120,7 @@ class Ticker(object): | |||||||
|         self.interval = interval |         self.interval = interval | ||||||
|         self.is_running = False |         self.is_running = False | ||||||
|         self.start() |         self.start() | ||||||
|  | 
 | ||||||
|     @property |     @property | ||||||
|     def true(self): |     def true(self): | ||||||
|         if self.tick: |         if self.tick: | ||||||
| @ -137,6 +144,7 @@ class Ticker(object): | |||||||
|         self._timer.cancel() |         self._timer.cancel() | ||||||
|         self.is_running = False |         self.is_running = False | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| ####################################################################################### | ####################################################################################### | ||||||
| 
 | 
 | ||||||
| class TorrentFile(object): | class TorrentFile(object): | ||||||
| @ -149,11 +157,12 @@ class TorrentFile(object): | |||||||
|     downloaded = 0 |     downloaded = 0 | ||||||
|     progress = 0.0 |     progress = 0.0 | ||||||
|     pdl_thread = None |     pdl_thread = None | ||||||
|  | 
 | ||||||
|     def __init__(self, tfs, fileEntry, savePath, index): |     def __init__(self, tfs, fileEntry, savePath, index): | ||||||
|         self.tfs = tfs |         self.tfs = tfs | ||||||
|         self.fileEntry = fileEntry |         self.fileEntry = fileEntry | ||||||
|         self.name = self.fileEntry.path |         self.name = self.fileEntry.path | ||||||
|         self.unicode_name = self.name.decode(chardet.detect(self.name)['encoding']) |         self.unicode_name = isinstance(self.name, str) and self.name or self.name.decode(chardet.detect(self.name)['encoding']) | ||||||
|         self.media_type = detect_media_type(self.unicode_name) |         self.media_type = detect_media_type(self.unicode_name) | ||||||
|         self.save_path = savePath |         self.save_path = savePath | ||||||
|         self.index = index |         self.index = index | ||||||
| @ -165,8 +174,10 @@ class TorrentFile(object): | |||||||
| 
 | 
 | ||||||
|     def Downloaded(self): |     def Downloaded(self): | ||||||
|         return self.downloaded |         return self.downloaded | ||||||
|  | 
 | ||||||
|     def Progress(self): |     def Progress(self): | ||||||
|         return self.progress |         return self.progress | ||||||
|  | 
 | ||||||
|     def __fileptr_(self): |     def __fileptr_(self): | ||||||
|         if self.closed: |         if self.closed: | ||||||
|             return None |             return None | ||||||
| @ -180,35 +191,44 @@ class TorrentFile(object): | |||||||
|             else: |             else: | ||||||
|                 self.filePtr = io.open(self.save_path, 'rb') |                 self.filePtr = io.open(self.save_path, 'rb') | ||||||
|         return self.filePtr |         return self.filePtr | ||||||
|  | 
 | ||||||
|     def log(self, message): |     def log(self, message): | ||||||
|         fnum = self.tfs.openedFiles.index(self) |         fnum = self.tfs.openedFiles.index(self) | ||||||
|         logging.info("[Thread No.%d] %s\n" % (fnum, message)) |         logging.info("[Thread No.%d] %s\n" % (fnum, message)) | ||||||
|  | 
 | ||||||
|     def Pieces(self): |     def Pieces(self): | ||||||
|         startPiece, _ = self.pieceFromOffset(1) |         startPiece, _ = self.pieceFromOffset(1) | ||||||
|         endPiece, _ = self.pieceFromOffset(self.size - 1) |         endPiece, _ = self.pieceFromOffset(self.size - 1) | ||||||
|         return startPiece, endPiece |         return startPiece, endPiece | ||||||
|  | 
 | ||||||
|     def SetPriority(self, priority): |     def SetPriority(self, priority): | ||||||
|         self.tfs.setPriority(self.index, priority) |         self.tfs.setPriority(self.index, priority) | ||||||
|  | 
 | ||||||
|     def readOffset(self): |     def readOffset(self): | ||||||
|         if os.getenv('ANDROID_ROOT'): |         if os.getenv('ANDROID_ROOT'): | ||||||
|             return libc.lseek64(self.filePtr, 0, os.SEEK_CUR) |             return libc.lseek64(self.filePtr, 0, os.SEEK_CUR) | ||||||
|         else: |         else: | ||||||
|             return self.filePtr.seek(0, os.SEEK_CUR) |             return self.filePtr.seek(0, os.SEEK_CUR) | ||||||
|  | 
 | ||||||
|     def havePiece(self, piece): |     def havePiece(self, piece): | ||||||
|         return self.tfs.handle.have_piece(piece) |         return self.tfs.handle.have_piece(piece) | ||||||
|  | 
 | ||||||
|     def pieceFromOffset(self, offset): |     def pieceFromOffset(self, offset): | ||||||
|         piece = int((self.offset + offset) / self.piece_length) |         piece = int((self.offset + offset) / self.piece_length) | ||||||
|         pieceOffset = int((self.offset + offset) % self.piece_length) |         pieceOffset = int((self.offset + offset) % self.piece_length) | ||||||
|         return piece, pieceOffset |         return piece, pieceOffset | ||||||
|  | 
 | ||||||
|     def waitForPiece(self, piece): |     def waitForPiece(self, piece): | ||||||
|         def set_deadlines(p): |         def set_deadlines(p): | ||||||
|             next_piece = p + 1 |             next_piece = p + 1 | ||||||
|             BUF_SIZE = 2  # Лучшее враг хорошего |             BUF_SIZE = 2  # Лучшее враг хорошего | ||||||
|             for i in range(BUF_SIZE): |             for i in range(BUF_SIZE): | ||||||
|                 if (next_piece + i < self.endPiece and |                 if (next_piece + i < self.endPiece and | ||||||
|                     not self.pieces_deadlined[(next_piece + i) - self.startPiece] and not self.havePiece(next_piece + i)): |                         not self.pieces_deadlined[(next_piece + i) - self.startPiece] and not self.havePiece( | ||||||
|  |                             next_piece + i)): | ||||||
|                     self.tfs.handle.set_piece_deadline(next_piece + i, 70 + (20 * i)) |                     self.tfs.handle.set_piece_deadline(next_piece + i, 70 + (20 * i)) | ||||||
|                     self.pieces_deadlined[(next_piece + i) - self.startPiece] = True |                     self.pieces_deadlined[(next_piece + i) - self.startPiece] = True | ||||||
|  | 
 | ||||||
|         if not self.havePiece(piece): |         if not self.havePiece(piece): | ||||||
|             self.log('Waiting for piece %d' % (piece,)) |             self.log('Waiting for piece %d' % (piece,)) | ||||||
|             self.tfs.handle.set_piece_deadline(piece, 50) |             self.tfs.handle.set_piece_deadline(piece, 50) | ||||||
| @ -220,6 +240,7 @@ class TorrentFile(object): | |||||||
|             self.pdl_thread = threading.Thread(target=set_deadlines, args=(piece,)) |             self.pdl_thread = threading.Thread(target=set_deadlines, args=(piece,)) | ||||||
|             self.pdl_thread.start() |             self.pdl_thread.start() | ||||||
|         return True |         return True | ||||||
|  | 
 | ||||||
|     def Close(self): |     def Close(self): | ||||||
|         if self.closed: return |         if self.closed: return | ||||||
|         self.log('Closing %s...' % (self.name,)) |         self.log('Closing %s...' % (self.name,)) | ||||||
| @ -231,6 +252,7 @@ class TorrentFile(object): | |||||||
|             else: |             else: | ||||||
|                 self.filePtr.close() |                 self.filePtr.close() | ||||||
|             self.filePtr = None |             self.filePtr = None | ||||||
|  | 
 | ||||||
|     def ShowPieces(self): |     def ShowPieces(self): | ||||||
|         pieces = self.tfs.handle.status().pieces |         pieces = self.tfs.handle.status().pieces | ||||||
|         str_ = '' |         str_ = '' | ||||||
| @ -240,6 +262,7 @@ class TorrentFile(object): | |||||||
|             else: |             else: | ||||||
|                 str_ += "#" |                 str_ += "#" | ||||||
|         self.log(str_) |         self.log(str_) | ||||||
|  | 
 | ||||||
|     def Read(self, buf): |     def Read(self, buf): | ||||||
|         filePtr = self.__fileptr_() |         filePtr = self.__fileptr_() | ||||||
|         if filePtr is None: |         if filePtr is None: | ||||||
| @ -258,6 +281,7 @@ class TorrentFile(object): | |||||||
|         else: |         else: | ||||||
|             read = filePtr.readinto(buf) |             read = filePtr.readinto(buf) | ||||||
|         return read |         return read | ||||||
|  | 
 | ||||||
|     def Seek(self, offset, whence): |     def Seek(self, offset, whence): | ||||||
|         filePtr = self.__fileptr_() |         filePtr = self.__fileptr_() | ||||||
|         if filePtr is None: return |         if filePtr is None: return | ||||||
| @ -270,9 +294,11 @@ class TorrentFile(object): | |||||||
|             newOffset = filePtr.seek(offset, whence) |             newOffset = filePtr.seek(offset, whence) | ||||||
|         self.log('Seeking to %d/%d' % (newOffset, self.size)) |         self.log('Seeking to %d/%d' % (newOffset, self.size)) | ||||||
|         return newOffset |         return newOffset | ||||||
|  | 
 | ||||||
|     def IsComplete(self): |     def IsComplete(self): | ||||||
|         return self.downloaded == self.size |         return self.downloaded == self.size | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| ####################################################################################### | ####################################################################################### | ||||||
| 
 | 
 | ||||||
| class TorrentFS(object): | class TorrentFS(object): | ||||||
| @ -296,8 +322,9 @@ class TorrentFS(object): | |||||||
|         num_files = self.info.num_files() |         num_files = self.info.num_files() | ||||||
|         for i in range(num_files): |         for i in range(num_files): | ||||||
|             self.setPriority(i, 0) |             self.setPriority(i, 0) | ||||||
|  | 
 | ||||||
|     def file(self, index): |     def file(self, index): | ||||||
|         for name in self.files.keys(): |         for name in list(self.files.keys()): | ||||||
|             if self.files[name].index == index: |             if self.files[name].index == index: | ||||||
|                 return self.files[name] |                 return self.files[name] | ||||||
|         file_ = self.__file_at_(index) |         file_ = self.__file_at_(index) | ||||||
| @ -311,22 +338,27 @@ class TorrentFS(object): | |||||||
|             logging.info('Closing %d opened file(s)' % (len(self.openedFiles),)) |             logging.info('Closing %d opened file(s)' % (len(self.openedFiles),)) | ||||||
|             for f in self.openedFiles: |             for f in self.openedFiles: | ||||||
|                 f.Close() |                 f.Close() | ||||||
|  | 
 | ||||||
|     def addOpenedFile(self, file_): |     def addOpenedFile(self, file_): | ||||||
|         self.openedFiles.append(file_) |         self.openedFiles.append(file_) | ||||||
|  | 
 | ||||||
|     def setPriority(self, index, priority): |     def setPriority(self, index, priority): | ||||||
|         if self.priorities[index] != priority: |         if self.priorities[index] != priority: | ||||||
|             logging.info('Setting %s priority to %d' % (self.info.file_at(index).path, priority)) |             logging.info('Setting %s priority to %d' % (self.info.file_at(index).path, priority)) | ||||||
|             self.priorities[index] = priority |             self.priorities[index] = priority | ||||||
|             self.handle.file_priority(index, priority) |             self.handle.file_priority(index, priority) | ||||||
|  | 
 | ||||||
|     def findOpenedFile(self, file): |     def findOpenedFile(self, file): | ||||||
|         for i, f in enumerate(self.openedFiles): |         for i, f in enumerate(self.openedFiles): | ||||||
|             if f == file: |             if f == file: | ||||||
|                 return i |                 return i | ||||||
|         return -1 |         return -1 | ||||||
|  | 
 | ||||||
|     def removeOpenedFile(self, file): |     def removeOpenedFile(self, file): | ||||||
|         pos = self.findOpenedFile(file) |         pos = self.findOpenedFile(file) | ||||||
|         if pos >= 0: |         if pos >= 0: | ||||||
|             del self.openedFiles[pos] |             del self.openedFiles[pos] | ||||||
|  | 
 | ||||||
|     def waitForMetadata(self): |     def waitForMetadata(self): | ||||||
|         if not self.handle.status().has_metadata: |         if not self.handle.status().has_metadata: | ||||||
|             time.sleep(0.1) |             time.sleep(0.1) | ||||||
| @ -334,19 +366,24 @@ class TorrentFS(object): | |||||||
|             self.info = self.handle.torrent_file() |             self.info = self.handle.torrent_file() | ||||||
|         except: |         except: | ||||||
|             self.info = self.handle.get_torrent_info() |             self.info = self.handle.get_torrent_info() | ||||||
|  | 
 | ||||||
|     def HasTorrentInfo(self): |     def HasTorrentInfo(self): | ||||||
|         return self.info is not None |         return self.info is not None | ||||||
|  | 
 | ||||||
|     def LoadFileProgress(self): |     def LoadFileProgress(self): | ||||||
|         self.progresses = self.handle.file_progress() |         self.progresses = self.handle.file_progress() | ||||||
|         for k in self.files.keys(): |         for k in list(self.files.keys()): | ||||||
|             self.files[k].downloaded = self.getFileDownloadedBytes(self.files[k].index) |             self.files[k].downloaded = self.getFileDownloadedBytes(self.files[k].index) | ||||||
|             if self.files[k].size > 0: self.files[k].progress = float(self.files[k].downloaded) / float(self.files[k].size) |             if self.files[k].size > 0: self.files[k].progress = float(self.files[k].downloaded) / float( | ||||||
|  |                 self.files[k].size) | ||||||
|  | 
 | ||||||
|     def getFileDownloadedBytes(self, i): |     def getFileDownloadedBytes(self, i): | ||||||
|         try: |         try: | ||||||
|             bytes_ = self.progresses[i] |             bytes_ = self.progresses[i] | ||||||
|         except IndexError: |         except IndexError: | ||||||
|             bytes_ = 0 |             bytes_ = 0 | ||||||
|         return bytes_ |         return bytes_ | ||||||
|  | 
 | ||||||
|     def __files_(self): |     def __files_(self): | ||||||
|         info = self.info |         info = self.info | ||||||
|         files_ = [] |         files_ = [] | ||||||
| @ -357,6 +394,7 @@ class TorrentFS(object): | |||||||
|                 file_.progress = float(file_.downloaded) / float(file_.size) |                 file_.progress = float(file_.downloaded) / float(file_.size) | ||||||
|             files_.append(file_) |             files_.append(file_) | ||||||
|         return files_ |         return files_ | ||||||
|  | 
 | ||||||
|     def __file_at_(self, index): |     def __file_at_(self, index): | ||||||
|         info = self.info |         info = self.info | ||||||
|         fileEntry = info.file_at(index) |         fileEntry = info.file_at(index) | ||||||
| @ -368,15 +406,18 @@ class TorrentFS(object): | |||||||
|             path, |             path, | ||||||
|             index |             index | ||||||
|         ) |         ) | ||||||
|  | 
 | ||||||
|     def FileByName(self, name): |     def FileByName(self, name): | ||||||
|         for i, f in enumerate(self.info.files()): |         for i, f in enumerate(self.info.files()): | ||||||
|             if f.path == name: |             if f.path == name: | ||||||
|                 return self.__file_at_(i) |                 return self.__file_at_(i) | ||||||
|         raise IOError |         raise IOError | ||||||
|  | 
 | ||||||
|     def Open(self, name): |     def Open(self, name): | ||||||
|         if self.shuttingDown or not self.HasTorrentInfo(): |         if self.shuttingDown or not self.HasTorrentInfo(): | ||||||
|             raise IOError |             raise IOError | ||||||
|         return self.OpenFile(name) |         return self.OpenFile(name) | ||||||
|  | 
 | ||||||
|     def checkPriorities(self): |     def checkPriorities(self): | ||||||
|         for index, priority in enumerate(self.priorities): |         for index, priority in enumerate(self.priorities): | ||||||
|             if priority == 0: |             if priority == 0: | ||||||
| @ -388,6 +429,7 @@ class TorrentFS(object): | |||||||
|                     break |                     break | ||||||
|             if not found: |             if not found: | ||||||
|                 self.setPriority(index, 0) |                 self.setPriority(index, 0) | ||||||
|  | 
 | ||||||
|     def OpenFile(self, name): |     def OpenFile(self, name): | ||||||
|         try: |         try: | ||||||
|             tf = self.FileByName(name) |             tf = self.FileByName(name) | ||||||
| @ -405,16 +447,18 @@ class TorrentFS(object): | |||||||
|         self.checkPriorities() |         self.checkPriorities() | ||||||
|         return tf |         return tf | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| ############################################################# | ############################################################# | ||||||
| 
 | 
 | ||||||
| class ThreadingHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer): | class ThreadingHTTPServer(socketserver.ThreadingMixIn, http.server.HTTPServer): | ||||||
|     def handle_error(self, *args, **kwargs): |     def handle_error(self, *args, **kwargs): | ||||||
|         '''Обходим злосчастный "Broken Pipe" и прочие трейсы''' |         '''Обходим злосчастный "Broken Pipe" и прочие трейсы''' | ||||||
|         if not AVOID_HTTP_SERVER_EXCEPTION_OUTPUT: |         if not AVOID_HTTP_SERVER_EXCEPTION_OUTPUT: | ||||||
|             BaseHTTPServer.HTTPServer.handle_error(self, *args, **kwargs) |             http.server.HTTPServer.handle_error(self, *args, **kwargs) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| def HttpHandlerFactory(): | def HttpHandlerFactory(): | ||||||
|     class HttpHandler(BaseHTTPServer.BaseHTTPRequestHandler): |     class HttpHandler(http.server.BaseHTTPRequestHandler): | ||||||
|         def do_GET(self): |         def do_GET(self): | ||||||
|             # print ('---Headers---\n%s\n' % (self.headers,)) |             # print ('---Headers---\n%s\n' % (self.headers,)) | ||||||
|             # print ('---Request---\n%s\n' % (self.path,)) |             # print ('---Request---\n%s\n' % (self.path,)) | ||||||
| @ -423,6 +467,7 @@ def HttpHandlerFactory(): | |||||||
|             else: |             else: | ||||||
|                 self.send_error(404, 'Not found') |                 self.send_error(404, 'Not found') | ||||||
|                 self.end_headers() |                 self.end_headers() | ||||||
|  | 
 | ||||||
|         def filesHandler(self): |         def filesHandler(self): | ||||||
|             f, start_range, end_range = self.send_head() |             f, start_range, end_range = self.send_head() | ||||||
|             if not f.closed: |             if not f.closed: | ||||||
| @ -454,15 +499,16 @@ def HttpHandlerFactory(): | |||||||
|                     total += chunk |                     total += chunk | ||||||
|                     start_range += chunk |                     start_range += chunk | ||||||
|                 f.Close() |                 f.Close() | ||||||
|  | 
 | ||||||
|         def send_head(self): |         def send_head(self): | ||||||
|             fname = urllib.unquote(self.path.lstrip('/files/')) |             fname = urllib.parse.unquote(self.path.lstrip('/files/')) | ||||||
|             try: |             try: | ||||||
|                 f = self.server.root_obj.TorrentFS.Open(fname) |                 f = self.server.root_obj.TorrentFS.Open(fname) | ||||||
|             except IOError: |             except IOError: | ||||||
|                 self.send_error(404, "File not found") |                 self.send_error(404, "File not found") | ||||||
|                 return (None, 0, 0) |                 return (None, 0, 0) | ||||||
|             _, ext = os.path.splitext(fname) |             _, ext = os.path.splitext(fname) | ||||||
|             ctype = (ext != '' and ext in VIDEO_EXTS.keys())and VIDEO_EXTS[ext] or 'application/octet-stream' |             ctype = (ext != '' and ext in list(VIDEO_EXTS.keys())) and VIDEO_EXTS[ext] or 'application/octet-stream' | ||||||
|             if "Range" in self.headers: |             if "Range" in self.headers: | ||||||
|                 self.send_response(206, 'Partial Content') |                 self.send_response(206, 'Partial Content') | ||||||
|             else: |             else: | ||||||
| @ -490,14 +536,18 @@ def HttpHandlerFactory(): | |||||||
|             self.send_header("Last-Modified", self.date_time_string(f.fileEntry.mtime)) |             self.send_header("Last-Modified", self.date_time_string(f.fileEntry.mtime)) | ||||||
|             self.end_headers() |             self.end_headers() | ||||||
|             # print "Sending Bytes ",start_range, " to ", end_range, "...\n" |             # print "Sending Bytes ",start_range, " to ", end_range, "...\n" | ||||||
|             return (f, start_range, end_range) |             return f, start_range, end_range | ||||||
|  | 
 | ||||||
|         # Вырубаем access-log |         # Вырубаем access-log | ||||||
|         def log_message(self, format, *args): |         def log_message(self, fmt, *args): | ||||||
|             return |             return | ||||||
|  | 
 | ||||||
|     return HttpHandler |     return HttpHandler | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| class Pyrrent2http(object): | class Pyrrent2http(object): | ||||||
|     pause = False |     pause = False | ||||||
|  | 
 | ||||||
|     def __init__(self, uri='', bindAddress='localhost:5001', downloadPath='.', |     def __init__(self, uri='', bindAddress='localhost:5001', downloadPath='.', | ||||||
|                  idleTimeout=-1, keepComplete=False, |                  idleTimeout=-1, keepComplete=False, | ||||||
|                  keepIncomplete=False, keepFiles=False, showAllStats=False, |                  keepIncomplete=False, keepFiles=False, showAllStats=False, | ||||||
| @ -623,7 +673,7 @@ class Pyrrent2http(object): | |||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             logging.error(e.args) |             logging.error(e.args) | ||||||
|         name = self.TorrentFS.info.name() |         name = self.TorrentFS.info.name() | ||||||
|         self.torrent_name = name.decode(chardet.detect(name)['encoding']) |         self.torrent_name = name | ||||||
| 
 | 
 | ||||||
|     def startHTTP(self): |     def startHTTP(self): | ||||||
|         logging.info('Starting HTTP Server...') |         logging.info('Starting HTTP Server...') | ||||||
| @ -768,8 +818,8 @@ class Pyrrent2http(object): | |||||||
|             'state_str': str(tstatus.state), |             'state_str': str(tstatus.state), | ||||||
|             'error': tstatus.error, |             'error': tstatus.error, | ||||||
|             'progress': tstatus.progress, |             'progress': tstatus.progress, | ||||||
|                      'download_rate'   :   tstatus.download_rate / 1024, |             'download_rate': tstatus.download_rate // 1024, | ||||||
|                      'upload_rate'     :   tstatus.upload_rate / 1024, |             'upload_rate': tstatus.upload_rate // 1024, | ||||||
|             'total_download': tstatus.total_download, |             'total_download': tstatus.total_download, | ||||||
|             'total_upload': tstatus.total_upload, |             'total_upload': tstatus.total_upload, | ||||||
|             'num_peers': tstatus.num_peers, |             'num_peers': tstatus.num_peers, | ||||||
| @ -778,13 +828,14 @@ class Pyrrent2http(object): | |||||||
|             'total_peers': tstatus.num_incomplete |             'total_peers': tstatus.num_incomplete | ||||||
|         } |         } | ||||||
|         return status |         return status | ||||||
|  | 
 | ||||||
|     def Ls(self, index): |     def Ls(self, index): | ||||||
|         fi = {} |         fi = {} | ||||||
|         if self.TorrentFS.HasTorrentInfo(): |         if self.TorrentFS.HasTorrentInfo(): | ||||||
|             x = [n for n in self.TorrentFS.files.keys() if self.TorrentFS.files[n].index == index] |             x = [n for n in list(self.TorrentFS.files.keys()) if self.TorrentFS.files[n].index == index] | ||||||
|             name = x[0] |             name = x[0] | ||||||
|             files = self.TorrentFS.files |             files = self.TorrentFS.files | ||||||
|             Url = 'http://' + self.config.bindAddress + '/files/' + urllib.quote(name) |             Url = 'http://' + self.config.bindAddress + '/files/' + urllib.parse.quote(name) | ||||||
|             fi = { |             fi = { | ||||||
|                 'index': files[name].index, |                 'index': files[name].index, | ||||||
|                 'name': files[name].unicode_name, |                 'name': files[name].unicode_name, | ||||||
| @ -797,6 +848,7 @@ class Pyrrent2http(object): | |||||||
|                 'url': Url |                 'url': Url | ||||||
|             } |             } | ||||||
|         return fi |         return fi | ||||||
|  | 
 | ||||||
|     def Peers(self): |     def Peers(self): | ||||||
|         peers = {'peers': []} |         peers = {'peers': []} | ||||||
|         for peer in self.torrentHandle.get_peer_info(): |         for peer in self.torrentHandle.get_peer_info(): | ||||||
| @ -806,8 +858,8 @@ class Pyrrent2http(object): | |||||||
|                 'Ip': peer.ip, |                 'Ip': peer.ip, | ||||||
|                 'Flags': peer.flags, |                 'Flags': peer.flags, | ||||||
|                 'Source': peer.source, |                 'Source': peer.source, | ||||||
|                    'UpSpeed':       peer.up_speed/1024, |                 'UpSpeed': peer.up_speed // 1024, | ||||||
|                    'DownSpeed':     peer.down_speed/1024, |                 'DownSpeed': peer.down_speed // 1024, | ||||||
|                 'TotalDownload': peer.total_download, |                 'TotalDownload': peer.total_download, | ||||||
|                 'TotalUpload': peer.total_upload, |                 'TotalUpload': peer.total_upload, | ||||||
|                 'Country': peer.country, |                 'Country': peer.country, | ||||||
| @ -815,12 +867,14 @@ class Pyrrent2http(object): | |||||||
|             } |             } | ||||||
|             peers['peers'].append(pi) |             peers['peers'].append(pi) | ||||||
|         return peers |         return peers | ||||||
|  | 
 | ||||||
|     def consumeAlerts(self): |     def consumeAlerts(self): | ||||||
|         alerts = self.session.pop_alerts() |         alerts = self.session.pop_alerts() | ||||||
|         for alert in alerts: |         for alert in alerts: | ||||||
|             if type(alert) == lt.save_resume_data_alert: |             if type(alert) == lt.save_resume_data_alert: | ||||||
|                 self.processSaveResumeDataAlert(alert) |                 self.processSaveResumeDataAlert(alert) | ||||||
|                 break |                 break | ||||||
|  | 
 | ||||||
|     def waitForAlert(self, alert_type, timeout): |     def waitForAlert(self, alert_type, timeout): | ||||||
|         start = time.time() |         start = time.time() | ||||||
|         while True: |         while True: | ||||||
| @ -831,6 +885,7 @@ class Pyrrent2http(object): | |||||||
|                 alert = self.session.pop_alert() |                 alert = self.session.pop_alert() | ||||||
|                 if type(alert) == alert_type: |                 if type(alert) == alert_type: | ||||||
|                     return alert |                     return alert | ||||||
|  | 
 | ||||||
|     def loop(self): |     def loop(self): | ||||||
|         self.saveResumeDataTicker = Ticker(5) |         self.saveResumeDataTicker = Ticker(5) | ||||||
|         time_start = time.time() |         time_start = time.time() | ||||||
| @ -859,16 +914,18 @@ class Pyrrent2http(object): | |||||||
|         except IOError as e: |         except IOError as e: | ||||||
|             strerror = e.args |             strerror = e.args | ||||||
|             logging.error(strerror) |             logging.error(strerror) | ||||||
|     def saveResumeData(self, async = False): | 
 | ||||||
|  |     def saveResumeData(self, async_=False): | ||||||
|         if not self.torrentHandle.status().need_save_resume or self.config.resumeFile == '': |         if not self.torrentHandle.status().need_save_resume or self.config.resumeFile == '': | ||||||
|             return False |             return False | ||||||
|         self.torrentHandle.save_resume_data(lt.save_resume_flags_t.flush_disk_cache) |         self.torrentHandle.save_resume_data(lt.save_resume_flags_t.flush_disk_cache) | ||||||
|         if not async: |         if not async_: | ||||||
|             alert = self.waitForAlert(lt.save_resume_data_alert, 5) |             alert = self.waitForAlert(lt.save_resume_data_alert, 5) | ||||||
|             if alert == None: |             if alert is None: | ||||||
|                 return False |                 return False | ||||||
|             self.processSaveResumeDataAlert(alert) |             self.processSaveResumeDataAlert(alert) | ||||||
|         return True |         return True | ||||||
|  | 
 | ||||||
|     def saveSessionState(self): |     def saveSessionState(self): | ||||||
|         if self.config.stateFile == '': |         if self.config.stateFile == '': | ||||||
|             return |             return | ||||||
| @ -882,6 +939,7 @@ class Pyrrent2http(object): | |||||||
|         except IOError as e: |         except IOError as e: | ||||||
|             strerror = e.args |             strerror = e.args | ||||||
|             logging.error(strerror) |             logging.error(strerror) | ||||||
|  | 
 | ||||||
|     def removeFiles(self, files): |     def removeFiles(self, files): | ||||||
|         for file in files: |         for file in files: | ||||||
|             try: |             try: | ||||||
| @ -897,6 +955,7 @@ class Pyrrent2http(object): | |||||||
|                     os.remove(path) |                     os.remove(path) | ||||||
|                     path_ = os.path.dirname(path) |                     path_ = os.path.dirname(path) | ||||||
|                     path = path_[-1] == os.path.sep and path_[:-1] or path_ |                     path = path_[-1] == os.path.sep and path_[:-1] or path_ | ||||||
|  | 
 | ||||||
|     def filesToRemove(self): |     def filesToRemove(self): | ||||||
|         files = [] |         files = [] | ||||||
|         if self.TorrentFS.HasTorrentInfo(): |         if self.TorrentFS.HasTorrentInfo(): | ||||||
| @ -907,6 +966,7 @@ class Pyrrent2http(object): | |||||||
|                     if os.path.exists(path): |                     if os.path.exists(path): | ||||||
|                         files.append(path) |                         files.append(path) | ||||||
|         return files |         return files | ||||||
|  | 
 | ||||||
|     def removeTorrent(self): |     def removeTorrent(self): | ||||||
|         files = [] |         files = [] | ||||||
|         flag = 0 |         flag = 0 | ||||||
| @ -922,6 +982,7 @@ class Pyrrent2http(object): | |||||||
|             logging.info('Waiting for files to be removed') |             logging.info('Waiting for files to be removed') | ||||||
|             self.waitForAlert(lt.torrent_deleted_alert, 15) |             self.waitForAlert(lt.torrent_deleted_alert, 15) | ||||||
|             self.removeFiles(files) |             self.removeFiles(files) | ||||||
|  | 
 | ||||||
|     def shutdown(self): |     def shutdown(self): | ||||||
|         logging.info('Stopping pyrrent2http...') |         logging.info('Stopping pyrrent2http...') | ||||||
|         self.forceShutdown = True |         self.forceShutdown = True | ||||||
|  | |||||||
							
								
								
									
										22
									
								
								lib/pyrrent2http/structs.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								lib/pyrrent2http/structs.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | class State: | ||||||
|  |     QUEUED_FOR_CHECKING = 0 | ||||||
|  |     CHECKING_FILES = 1 | ||||||
|  |     DOWNLOADING_METADATA = 2 | ||||||
|  |     DOWNLOADING = 3 | ||||||
|  |     FINISHED = 4 | ||||||
|  |     SEEDING = 5 | ||||||
|  |     ALLOCATING = 6 | ||||||
|  |     CHECKING_RESUME_DATA = 7 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class MediaType: | ||||||
|  |     UNKNOWN = None | ||||||
|  |     AUDIO = 'audio' | ||||||
|  |     VIDEO = 'video' | ||||||
|  |     SUBTITLES = 'subtitles' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Encryption: | ||||||
|  |     FORCED = 0 | ||||||
|  |     ENABLED = 1 | ||||||
|  |     DISABLED = 2 | ||||||
| @ -1,31 +1,41 @@ | |||||||
| import sys |  | ||||||
| import socket |  | ||||||
| import chardet |  | ||||||
| import os |  | ||||||
| from . import MediaType |  | ||||||
| import mimetypes | import mimetypes | ||||||
| import urlparse, urllib | import os | ||||||
|  | import socket | ||||||
|  | import urllib.error | ||||||
|  | import urllib.parse | ||||||
|  | import urllib.parse | ||||||
|  | import urllib.request | ||||||
|  | 
 | ||||||
|  | import chardet | ||||||
|  | import sys | ||||||
|  | import xbmc | ||||||
|  | 
 | ||||||
|  | from .structs import MediaType | ||||||
| 
 | 
 | ||||||
| SUBTITLES_FORMATS = ['.aqt', '.gsub', '.jss', '.sub', '.ttxt', '.pjs', '.psb', '.rt', '.smi', '.stl', | SUBTITLES_FORMATS = ['.aqt', '.gsub', '.jss', '.sub', '.ttxt', '.pjs', '.psb', '.rt', '.smi', '.stl', | ||||||
|                      '.ssf', '.srt', '.ssa', '.ass', '.usf', '.idx'] |                      '.ssf', '.srt', '.ssa', '.ass', '.usf', '.idx'] | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| class Struct(dict): | class Struct(dict): | ||||||
|     def __getattr__(self, attr): |     def __getattr__(self, attr): | ||||||
|         return self[attr] |         return self[attr] | ||||||
|  | 
 | ||||||
|     def __setattr__(self, attr, value): |     def __setattr__(self, attr, value): | ||||||
|         self[attr] = value |         self[attr] = value | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def uri2path(uri): | def uri2path(uri): | ||||||
|     if uri[1] == ':' and sys.platform.startswith('win'): |     if uri[1] == ':' and sys.platform.startswith('win'): | ||||||
|         uri = 'file:///' + uri |         uri = 'file:///' + uri | ||||||
|     fileUri = urlparse.urlparse(uri) |     fileUri = urllib.parse.urlparse(uri) | ||||||
|     if fileUri.scheme == 'file': |     if fileUri.scheme == 'file': | ||||||
|         uriPath = fileUri.path |         uriPath = fileUri.path | ||||||
|         if uriPath != '' and sys.platform.startswith('win') and (os.path.sep == uriPath[0] or uriPath[0] == '/'): |         if uriPath != '' and sys.platform.startswith('win') and (os.path.sep == uriPath[0] or uriPath[0] == '/'): | ||||||
|             uriPath = uriPath[1:] |             uriPath = uriPath[1:] | ||||||
|     absPath = os.path.abspath(urllib.unquote(uriPath)) |     absPath = os.path.abspath(urllib.parse.unquote(uriPath)) | ||||||
|     return localize_path(absPath) |     return localize_path(absPath) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def detect_media_type(name): | def detect_media_type(name): | ||||||
|     ext = os.path.splitext(name)[1] |     ext = os.path.splitext(name)[1] | ||||||
|     if ext in SUBTITLES_FORMATS: |     if ext in SUBTITLES_FORMATS: | ||||||
| @ -41,24 +51,31 @@ def detect_media_type(name): | |||||||
|             return MediaType.VIDEO |             return MediaType.VIDEO | ||||||
|         else: |         else: | ||||||
|             return MediaType.UNKNOWN |             return MediaType.UNKNOWN | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| def unicode_msg(tmpl, args): | def unicode_msg(tmpl, args): | ||||||
|     msg = isinstance(tmpl, unicode) and tmpl or tmpl.decode(chardet.detect(tmpl)['encoding']) |     msg = isinstance(tmpl, str) and tmpl or tmpl.decode(chardet.detect(tmpl)['encoding']) | ||||||
|     arg_ = [] |     arg_ = [] | ||||||
|     for a in args: |     for a in args: | ||||||
|         arg_.append(isinstance(a, unicode) and a or a.decode(chardet.detect(a)['encoding'])) |         arg_.append(isinstance(a, str) and a or a.decode(chardet.detect(a)['encoding'])) | ||||||
|     return msg % tuple(arg_) |     return msg % tuple(arg_) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def encode_msg(msg): | def encode_msg(msg): | ||||||
|     msg = isinstance(msg, unicode) and msg.encode(sys.getfilesystemencoding() != 'ascii' and sys.getfilesystemencoding() or 'utf-8') or msg |     msg = isinstance(msg, str) and msg.encode( | ||||||
|  |         sys.getfilesystemencoding() != 'ascii' and sys.getfilesystemencoding() or 'utf-8') or msg | ||||||
|     return msg |     return msg | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def localize_path(path): | def localize_path(path): | ||||||
|     if not isinstance(path, unicode): path = path.decode(chardet.detect(path)['encoding']) |     if isinstance(path, bytes): | ||||||
|     if not sys.platform.startswith('win'): |         path = path.decode(chardet.detect(path)['encoding']) | ||||||
|         path = path.encode((sys.getfilesystemencoding() not in ('ascii', 'ANSI_X3.4-1968')) and sys.getfilesystemencoding() or 'utf-8') |     # if not sys.platform.startswith('win'): | ||||||
|  |     #    path = path.encode( | ||||||
|  |     #        (sys.getfilesystemencoding() not in ('ascii', 'ANSI_X3.4-1968')) and sys.getfilesystemencoding() or 'utf-8') | ||||||
|     return path |     return path | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def can_bind(host, port): | def can_bind(host, port): | ||||||
|     """ |     """ | ||||||
|     Checks we can bind to specified host and port |     Checks we can bind to specified host and port | ||||||
| @ -94,6 +111,6 @@ def find_free_port(host): | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def ensure_fs_encoding(string): | def ensure_fs_encoding(string): | ||||||
|     if isinstance(string, str): |     if isinstance(string, bytes): | ||||||
|         string = string.decode('utf-8') |         string = string.decode('utf-8') | ||||||
|     return string.encode(sys.getfilesystemencoding() or 'utf-8') |     return string.encode(sys.getfilesystemencoding() or 'utf-8') | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user