Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
having trouble writing a video file to local storage -- ends up corrupted
-
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. :)
-
The
photos
module doesn't really support video out of the box, andget_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')
-
@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.