Forum Archive

having trouble writing a video file to local storage -- ends up corrupted

jeff_melton

My end goal is to use Pythonista to upload video taken on my iPhone to Vimeo by way of their API. Using their official library (installed via StaSH), uploads are easy: Just provide a path to a local file. Generally, I'd like to select a video from Photo Roll, write it to Pythonista's local storage, upload it to Vimeo, then remove it from local storage. I'm writing the file, and it has the correct length in bytes, but the upload fails (it creates a tombstone at Vimeo, a zero-length file), and the file in local storage doesn't play when I try opening it in an external resource (Quick Look -> Save in Dropbox; won't play on phone or desktop). Here's what I have so far (truncating a few bits related to patching metadata into the uploaded file, plus local storage file removal):

# coding: utf-8
import vimeo
import photos
from io import BytesIO
from objc_util import ObjCInstance

client = vimeo.VimeoClient(token='my_api_token')

video_asset = photos.pick_asset()
video_data = video_asset.get_image_data()
video_bytes = video_data.getvalue()
filename = str(ObjCInstance(video_asset).filename())

with open(filename, 'wb') as video:
    video.write(video_bytes)
video.close()

video_uri = client.upload(filename)

I feel like I'm missing something obvious here, but I can't figure out what it is. Any help is much obliged. :)

omz

The photos module doesn't really support video out of the box, and get_image_data will always return image data – for videos, it returns just one frame (the preview image in the Photos library).

You can use objc_util to expose more functionality of the underlying Photos framework though. Here's a quick demo of how you could use the ObjC bridge to get at the file of a video asset. This isn't tested very thoroughly, and I believe it won't work for e.g. timelapse videos, but it might be good enough for your purposes.

from objc_util import *
import threading
import photos

def get_video_path(asset):
    if asset.media_type != 'video':
        raise ValueError('Not a video asset')
    PHImageManager = ObjCClass('PHImageManager')
    mgr = PHImageManager.defaultManager()
    e = threading.Event()
    result = {}
    def handler_func(_cmd, _av_asset, _audio_mix, _info):
        av_asset = ObjCInstance(_av_asset)
        if av_asset.isKindOfClass_(ObjCClass('AVURLAsset')):
            asset_url = av_asset.URL()
            asset_path = str(asset_url.path())
            result['path'] = asset_path
        e.set()
    ph_asset = ObjCInstance(asset)
    handler = ObjCBlock(handler_func, restype=None, argtypes=[c_void_p]*4)
    mgr.requestAVAssetForVideo_options_resultHandler_(ph_asset, None, handler)
    e.wait()
    return result.get('path', None)

# Demo: Pick a video asset from the library, then copy the video file to Pythonista, and show a QuickLook preview...
if __name__ == '__main__':
    asset = photos.pick_asset()
    video_path = get_video_path(asset)
    if video_path:
        import shutil, console, os
        shutil.copy(video_path, 'video.m4v')
        console.quicklook(os.path.abspath('video.m4v'))
    else:
        print('Could not get video file path')
jeff_melton

@omz: Thank you very much. I'd wondered if I wasn't just getting one frame of the video. This does indeed get me started.