folder reorganization

This commit is contained in:
cttynul
2019-04-23 14:32:53 +02:00
parent 659751b2f4
commit 8e7ee78a87
1195 changed files with 267003 additions and 2 deletions
+3
View File
@@ -0,0 +1,3 @@
from client import Client
from server import Server
__all__ = ['Client', 'Server']
+200
View File
@@ -0,0 +1,200 @@
import base64
import json
import random
import struct
import time
import urllib
from core import httptools
from threading import Thread
from file import File
from handler import Handler
from platformcode import logger
from server import Server
class Client(object):
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',
'.ogg': 'video/ogg', '.webm': 'video/webm', '.ts': 'video/mp2t', '.3gp': 'video/3gpp'}
def __init__(self, url, port=None, ip=None, auto_shutdown=True, wait_time=20, timeout=5, is_playing_fnc=None):
self.port = port if port else random.randint(8000,8099)
self.ip = ip if ip else "127.0.0.1"
self.connected = False
self.start_time = None
self.last_connect = None
self.is_playing_fnc = is_playing_fnc
self.auto_shutdown = auto_shutdown
self.wait_time = wait_time
self.timeout = timeout
self.running = False
self.file = None
self.files = []
self._server = Server((self.ip, self.port), Handler, client=self)
self.add_url(url)
self.start()
def start(self):
self.start_time = time.time()
self.running = True
self._server.run()
t= Thread(target=self._auto_shutdown)
t.setDaemon(True)
t.start()
logger.info("MEGA Server Started")
def _auto_shutdown(self):
while self.running:
time.sleep(1)
if self.file and self.file.cursor:
self.last_connect = time.time()
if self.is_playing_fnc and self.is_playing_fnc():
self.last_connect = time.time()
if self.auto_shutdown:
#shudown por haber cerrado el reproductor
if self.connected and self.last_connect and self.is_playing_fnc and not self.is_playing_fnc():
if time.time() - self.last_connect - 1 > self.timeout:
self.stop()
#shutdown por no realizar ninguna conexion
if (not self.file or not self.file.cursor) and self.start_time and self.wait_time and not self.connected:
if time.time() - self.start_time - 1 > self.wait_time:
self.stop()
#shutdown tras la ultima conexion
if (not self.file or not self.file.cursor) and self.timeout and self.connected and self.last_connect and not self.is_playing_fnc:
if time.time() - self.last_connect - 1 > self.timeout:
self.stop()
def stop(self):
self.running = False
self._server.stop()
logger.info("MEGA Server Stopped")
def get_play_list(self):
if len(self.files) > 1:
return "http://" + self.ip + ":" + str(self.port) + "/playlist.pls"
else:
return "http://" + self.ip + ":" + str(self.port) + "/" + urllib.quote(self.files[0].name.encode("utf8"))
def get_files(self):
files = []
if self.files:
for file in self.files:
n = file.name.encode("utf8")
u = "http://" + self.ip + ":" + str(self.port) + "/" + urllib.quote(n)
s = file.size
file_id = file.file_id
files.append({"name":n,"url":u,"size":s, "id": file_id})
return files
def add_url(self, url):
url = url.split("/#")[1]
id_video = None
if "|" in url:
url, id_video = url.split("|")
if url.startswith("F!"):
if len(url.split("!")) ==3:
folder_id = url.split("!")[1]
folder_key = url.split("!")[2]
master_key = self.base64_to_a32(folder_key)
files = self.api_req({"a":"f","c":1,"r":1},"&n="+folder_id)
for file in files["f"]:
if file["t"] == 0:
if id_video and id_video != file["h"]:
continue
key = file['k'][file['k'].index(':') + 1:]
key = self.decrypt_key(self.base64_to_a32(key), master_key)
k = (key[0] ^ key[4], key[1] ^ key[5], key[2] ^ key[6], key[3] ^ key[7])
attributes = self.base64urldecode(file['a'])
attributes = self.dec_attr(attributes, k)
self.files.append(File(info=attributes, file_id=file["h"], key=key, folder_id=folder_id, file= file, client = self ))
else:
raise Exception("Enlace no valido")
elif url.startswith("!") or url.startswith("N!"):
if len(url.split("!")) ==3:
file_id = url.split("!")[1]
file_key = url.split("!")[2]
file = self.api_req({'a': 'g', 'g': 1, 'p': file_id})
key = self.base64_to_a32(file_key)
k = (key[0] ^ key[4], key[1] ^ key[5], key[2] ^ key[6], key[3] ^ key[7])
attributes = self.base64urldecode(file['at'])
attributes = self.dec_attr(attributes, k)
self.files.append(File(info=attributes, file_id=file_id, key=key, file= file, client = self))
else:
raise Exception("Enlace no valido")
else:
raise Exception("Enlace no valido")
def api_req(self, req, get=""):
seqno = random.randint(0, 0xFFFFFFFF)
url = 'https://g.api.mega.co.nz/cs?id=%d%s' % (seqno, get)
return json.loads(self.post(url, json.dumps([req])))[0]
def base64urldecode(self,data):
data += '=='[(2 - len(data) * 3) % 4:]
for search, replace in (('-', '+'), ('_', '/'), (',', '')):
data = data.replace(search, replace)
return base64.b64decode(data)
def base64urlencode(self,data):
data = base64.b64encode(data)
for search, replace in (('+', '-'), ('/', '_'), ('=', '')):
data = data.replace(search, replace)
return data
def a32_to_str(self,a):
return struct.pack('>%dI' % len(a), *a)
def str_to_a32(self,b):
if len(b) % 4: # Add padding, we need a string with a length multiple of 4
b += '\0' * (4 - len(b) % 4)
return struct.unpack('>%dI' % (len(b) / 4), b)
def base64_to_a32(self,s):
return self.str_to_a32(self.base64urldecode(s))
def a32_to_base64(self,a):
return self.base64urlencode(self.a32_to_str(a))
def aes_cbc_decrypt(self, data, key):
try:
from Crypto.Cipher import AES
decryptor = AES.new(key, AES.MODE_CBC, '\0' * 16)
#decryptor = aes.AESModeOfOperationCBC(key, iv='\0' * 16)
except:
import jscrypto
decryptor = jscrypto.new(key, jscrypto.MODE_CBC, '\0' * 16)
return decryptor.decrypt(data)
def aes_cbc_decrypt_a32(self,data, key):
return self.str_to_a32(self.aes_cbc_decrypt(self.a32_to_str(data), self.a32_to_str(key)))
def decrypt_key(self,a, key):
return sum((self.aes_cbc_decrypt_a32(a[i:i+4], key) for i in xrange(0, len(a), 4)), ())
def post(self, url, data):
return httptools.downloadpage(url, data).data
import ssl
from functools import wraps
def sslwrap(func):
@wraps(func)
def bar(*args, **kw):
kw['ssl_version'] = ssl.PROTOCOL_TLSv1
return func(*args, **kw)
return bar
ssl.wrap_socket = sslwrap(ssl.wrap_socket)
return urllib.urlopen(url, data).read()
def dec_attr(self, attr, key):
attr = self.aes_cbc_decrypt(attr, self.a32_to_str(key)).rstrip('\0')
if not attr.endswith("}"):
attr = attr.rsplit("}", 1)[0] + "}"
return json.loads(attr[4:]) if attr[:6] == 'MEGA{"' else False
+72
View File
@@ -0,0 +1,72 @@
import urllib2
class Cursor(object):
def __init__(self, file):
self._file=file
self.pos=0
self.conn =None
self.initial_value = file.initial_value
self.k = file.k
def mega_request(self,offset, retry=False):
if not self._file.url or retry:
if self._file.folder_id :
file = self._file._client.api_req({"a":"g","g":1,"n":self._file.file_id},"&n="+self._file.folder_id)
self._file.url= file["g"]
else:
file = self._file._client.api_req({'a': 'g', 'g': 1, 'p': self._file.file_id})
self._file.url= file["g"]
req = urllib2.Request(self._file.url)
req.headers['Range'] = 'bytes=%s-' % (offset)
try:
self.conn = urllib2.urlopen(req)
self.prepare_decoder(offset)
except:
#La url del archivo expira transcurrido un tiempo, si da error 403, reintenta volviendo a solicitar la url mediante la API
self.mega_request(offset, True)
def read(self,n=None):
if not self.conn:
return
res=self.conn.read(n)
if res:
res = self.decode(res)
self.pos+=len(res)
return res
def seek(self,n):
if n>self._file.size:
n=self._file.size
elif n<0:
raise ValueError('Seeking negative')
self.mega_request(n)
self.pos=n
def tell(self):
return self.pos
def __enter__(self):
return self
def __exit__(self,exc_type, exc_val, exc_tb):
self._file.cursors.remove(self)
if len(self._file.cursors) == 0: self._file.cursor = False
def decode(self, data):
return self.decryptor.decrypt(data)
def prepare_decoder(self,offset):
initial_value = self.initial_value + int(offset/16)
try:
from Crypto.Cipher import AES
from Crypto.Util import Counter
self.decryptor = AES.new(self._file._client.a32_to_str(self.k), AES.MODE_CTR, counter = Counter.new(128, initial_value = initial_value))
except:
from pyaes import aes
self.decryptor = aes.AESModeOfOperationCTR(f=self,key=self._client.a32_to_str(self.k),counter=aes.Counter(initial_value=initial_value))
rest = offset - int(offset/16)*16
if rest:
self.decode(str(0)*rest)
+35
View File
@@ -0,0 +1,35 @@
from cursor import Cursor
class File(object):
def __init__(self, info, file_id, key, file ,client, folder_id=None):
self._client = client
self.folder_id = folder_id
self.file_id = file_id
self.cursor = False
self.cursors = []
self.key = key
self.file = file
self.info= info
self.name = info["n"]
self.size = file["s"]
self.request=None
self.k = self.key[0] ^ self.key[4] , self.key[1] ^ self.key[5] , self.key[2] ^ self.key[6], self.key[3] ^ self.key[7]
self.iv = self.key[4:6] + (0, 0)
self.initial_value = (((self.iv[0] << 32) + self.iv[1]) << 64)
if not self.folder_id:
self.url= self.file["g"]
else:
self.url = None
def create_cursor(self,offset):
c = Cursor(self)
c.seek(offset)
self.cursor = True
self.cursors.append(c)
return c
+122
View File
@@ -0,0 +1,122 @@
import BaseHTTPServer
import urlparse
import time
import urllib
import types
import os
import re
class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
protocol_version = 'HTTP/1.1'
def log_message(self, format, *args):
pass
def parse_range(self, range):
if range:
m=re.compile(r'bytes=(\d+)-(\d+)?').match(range)
if m:
return m.group(1), m.group(2)
return None, None
def do_GET(self):
self.server._client.connected = True
if self.do_HEAD():
with self.server._client.file.create_cursor(self.offset) as f:
sended = 0
while sended < self.size:
buf= f.read(1024*16)
if buf:
if sended + len(buf) > self.size: buf=buf[:self.size-sended]
self.wfile.write(buf)
sended +=len(buf)
else:
break
def send_pls(self, files):
playlist = "[playlist]\n\n"
for x,f in enumerate(files):
playlist += "File"+str(x+1)+"=http://" + self.server._client.ip + ":" + str(self.server._client.port) + "/" + urllib.quote(f.name)+"\n"
playlist += "Title"+str(x+1)+"=" +f.name+"\n"
playlist +="NumberOfEntries=" + str(len(files))
playlist +="Version=2"
self.send_response(200, 'OK')
self.send_header("Content-Length", str(len(playlist)))
self.finish_header()
self.wfile.write(playlist)
def do_HEAD(self):
url=urlparse.urlparse(self.path).path
while not self.server._client.files:
time.sleep(1)
if url=="/playlist.pls":
self.send_pls(self.server._client.files)
return False
if not self.server._client.file or urllib.unquote(url)[1:] != self.server._client.file.name:
for f in self.server._client.files:
if f.name == urllib.unquote(url)[1:].decode("utf-8"):
self.server._client.file = f
break
if self.server._client.file and urllib.unquote(url)[1:].decode("utf-8") == self.server._client.file.name:
range = False
self.offset=0
size, mime = self._file_info()
start, end = self.parse_range(self.headers.get('Range', ""))
self.size = size
if start <> None:
if end == None: end = size - 1
self.offset=int(start)
self.size=int(end) - int(start) + 1
range=(int(start), int(end), int(size))
else:
range = None
self.send_resp_header(mime, size, range)
return True
else:
self.send_error(404, 'Not Found')
def _file_info(self):
size=self.server._client.file.size
ext=os.path.splitext(self.server._client.file.name)[1]
mime=self.server._client.VIDEO_EXTS.get(ext)
if not mime:
mime='application/octet-stream'
return size,mime
def send_resp_header(self, cont_type, size, range=False):
if range:
self.send_response(206, 'Partial Content')
else:
self.send_response(200, 'OK')
self.send_header('Content-Type', cont_type)
self.send_header('Accept-Ranges', 'bytes')
if range:
if isinstance(range, (types.TupleType, types.ListType)) and len(range)==3:
self.send_header('Content-Range', 'bytes %d-%d/%d' % range)
self.send_header('Content-Length', range[1]-range[0]+1)
else:
raise ValueError('Invalid range value')
else:
self.send_header('Content-Length', size)
self.send_header('Connection', 'close')
self.end_headers()
+33
View File
@@ -0,0 +1,33 @@
import traceback
import BaseHTTPServer
from SocketServer import ThreadingMixIn
from threading import Thread
class Server(ThreadingMixIn, BaseHTTPServer.HTTPServer):
daemon_threads = True
timeout = 1
def __init__(self, address, handler, client):
BaseHTTPServer.HTTPServer.__init__(self,address,handler)
self._client = client
self.running=True
self.request = None
def stop(self):
self.running=False
def serve(self):
while self.running:
try:
self.handle_request()
except:
print traceback.format_exc()
def run(self):
t=Thread(target=self.serve, name='HTTP Server')
t.daemon=self.daemon_threads
t.start()
def handle_error(self, request, client_address):
if not "socket.py" in traceback.format_exc():
print traceback.format_exc()