Skip to content
Snippets Groups Projects
upload.py 7.66 KiB
Newer Older
  • Learn to ignore specific revisions
  • # ##### BEGIN GPL LICENSE BLOCK #####
    #
    #  This program is free software; you can redistribute it and/or
    #  modify it under the terms of the GNU General Public License
    #  as published by the Free Software Foundation; either version 2
    #  of the License, or (at your option) any later version.
    #
    #  This program is distributed in the hope that it will be useful,
    #  but WITHOUT ANY WARRANTY; without even the implied warranty of
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #  GNU General Public License for more details.
    #
    #  You should have received a copy of the GNU General Public License
    #  along with this program; if not, write to the Free Software Foundation,
    #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    #
    # ##### END GPL LICENSE BLOCK #####
    
    import xmlrpc.client
    import http.client
    import hashlib
    from os.path import isabs, isfile, join, exists
    
    import bpy
    
    from .utils import _read_credentials
    from .rpc import rffi, _do_refresh, rffi_xmlrpc_upload, rffi_xmlrpc, RFFI_VERBOSE
    
    def _random_string(length):
        import string
        import random
        return ''.join(random.choice(string.ascii_letters) for ii in range(length + 1))
    
    def _encode_multipart_data(data, files):
        boundary = _random_string(30)
        
        def get_content_type(filename):
            return 'application/octet-stream' # default this
        
        def encode_field(field_name):
            return ('--' + boundary,
                    'Content-Disposition: form-data; name="%s"' % field_name,
                    '', str(data[field_name]))
        
        def encode_file(field_name):
            filename = files [field_name]
            fcontent = None
            print('encoding', field_name)
            try:
                fcontent = str(open(filename, 'rb').read(), encoding='iso-8859-1')
            except Exception as e:
                print('Trouble in paradise', e)
            return ('--' + boundary,
                    'Content-Disposition: form-data; name="%s"; filename="%s"' % (field_name, filename),
                    'Content-Type: %s' % get_content_type(filename),
                    '', fcontent)
        
        lines = []
        for name in data:
            lines.extend(encode_field(name))
        for name in files:
            lines.extend(encode_file(name))
        lines.extend(('--%s--' % boundary, ''))
        print("joining lines into body")
        body = '\r\n'.join(lines)
        
        headers = {'content-type': 'multipart/form-data; boundary=' + boundary,
                   'content-length': str(len(body))}
    
        print("headers and body ready")
        
        return body, headers
    
    def _send_post(data, files):
        print("Forming connection for post")
        connection = http.client.HTTPConnection(rffi_xmlrpc_upload)
        print("Requesting")
        connection.request('POST', '/burp/storage', *_encode_multipart_data(data, files)) # was /file
        print("Getting response")
        response = connection.getresponse()
        print("Reading response")
        res = response.read()
        return res
    
    def _md5_for_file(filepath):
        md5hash = hashlib.md5()
        blocksize = 0x10000
        f = open(filepath, "rb")
        while True:
            data = f.read(blocksize)
            if not data:
                break
            md5hash.update(data)
        return md5hash.hexdigest()
    
    def _upload_file(key, userid, sessionid, path):
        print("Asserting absolute path")
        assert isabs(path)
        print("Asserting path is a file")
        assert isfile(path)
        data = {
            'userId': str(userid),
            'sessionKey': key,
            'sessionId': sessionid,
            'md5sum': _md5_for_file(path)
        }
        files = {
            'blenderfile': path
        }
        r = _send_post(data, files)
        
        return r
    
    def _run_upload(key, userid, sessionid, path):
        print("Starting upload");
        r = _upload_file(key, userid, sessionid, path)
        print("Upload finished")
        o = xmlrpc.client.loads(r)
        print("Loaded xmlrpc response")
        return o[0][0]
    
    def _ore_upload(op, context):
        sce = context.scene
        ore = sce.ore_render
        
        if not bpy.ready:
            op.report({'ERROR'}, 'Your user or scene information is not complete')
            bpy.infoError = True
            bpy.errorStartTime = time.time()
            bpy.context.scene.render.engine = 'RENDERFARMFI_RENDER'
            return {'CANCELLED'}
        try:
            _read_credentials()
            res = rffi.login(op, True)
            key = res['key']
            userid = res['userId']
            print("Creating server proxy")
            proxy = xmlrpc.client.ServerProxy(rffi_xmlrpc, verbose=RFFI_VERBOSE)
            proxy._ServerProxy__transport.user_agent = 'Renderfarm.fi Uploader/%s' % (bpy.CURRENT_VERSION)
            print("Creating a new session")
            res = proxy.session.createSession(userid, key)  # This may use an existing, non-rendered session. Prevents spamming in case the upload fails for some reason
            sessionid = res['sessionId']
            key = res['key']
            print("Session id is " + str(sessionid))
            res = _run_upload(key, userid, sessionid, bpy.data.filepath)
            print("Getting fileid from xmlrpc response data")
            fileid = int(res['fileId'])
            print("Sending session details for session " + str(sessionid) + " with fileid " + str(fileid))
            res = proxy.session.setTitle(userid, res['key'], sessionid, ore.title)
            res = proxy.session.setLongDescription(userid, res['key'], sessionid, ore.longdesc)
            res = proxy.session.setShortDescription(userid, res['key'], sessionid, ore.shortdesc)
            if len(ore.url)>0:
                res = proxy.session.setExternalURLs(userid, res['key'], sessionid, ore.url)
            res = proxy.session.setStartFrame(userid, res['key'], sessionid, ore.start)
            res = proxy.session.setEndFrame(userid, res['key'], sessionid, ore.end)
            res = proxy.session.setSplit(userid, res['key'], sessionid, ore.parts)
            res = proxy.session.setMemoryLimit(userid, res['key'], sessionid, ore.memusage)
            res = proxy.session.setXSize(userid, res['key'], sessionid, ore.resox)
            res = proxy.session.setYSize(userid, res['key'], sessionid, ore.resoy)
            res = proxy.session.setFrameRate(userid, res['key'], sessionid, ore.fps)
            res = proxy.session.setFrameFormat(userid, res['key'], sessionid, ore.file_format)
            res = proxy.session.setRenderer(userid, res['key'], sessionid, ore.engine)
            res = proxy.session.setSamples(userid, res['key'], sessionid, ore.samples)
            res = proxy.session.setSubSamples(userid, res['key'], sessionid, ore.subsamples)
            if (ore.engine == 'cycles'):
                res = proxy.session.setReplication(userid, res['key'], sessionid, 1)
                if ore.subsamples > 1:
                    res = proxy.session.setStitcher(userid, res['key'], sessionid, 'AVERAGE')
            else:
                res = proxy.session.setReplication(userid, res['key'], sessionid, 3)
            res = proxy.session.setOutputLicense(userid, res['key'], sessionid, int(ore.outlicense))
            res = proxy.session.setInputLicense(userid, res['key'], sessionid, int(ore.inlicense))
            print("Setting primary input file")
            res = proxy.session.setPrimaryInputFile(userid, res['key'], sessionid, fileid)
            print("Submitting session")
            res = proxy.session.submit(userid, res['key'], sessionid)
            print("Session submitted")
            op.report({'INFO'}, 'Submission sent to Renderfarm.fi')
        except xmlrpc.client.Error as v:
            bpy.context.scene.render.engine = 'RENDERFARMFI_RENDER'
            print('ERROR:', v)
            op.report({'ERROR'}, 'An XMLRPC error occurred while sending submission to Renderfarm.fi')
        except Exception as e:
            bpy.context.scene.render.engine = 'RENDERFARMFI_RENDER'
            print('Unhandled error:', e)
            op.report({'ERROR'}, 'A generic error occurred while sending submission to Renderfarm.fi')
        
        bpy.context.scene.render.engine = 'RENDERFARMFI_RENDER'
        _do_refresh(op)
        return {'FINISHED'}