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.
how to get last video on iphone?
-
@frankL said:
video = last_video.get_ui_image()
This line implies that you take only one image.
What do you want to do?
Copy a video from the camera roll to....? -
@frankL this copies picked video to Pythonista local files
import photos from objc_util import * import time import shutil import os videos = photos.get_assets(media_type='video') assets = photos.pick_asset(assets=videos,multi=True) #print(dir) options=ObjCClass('PHVideoRequestOptions').new() options.version=1 #PHVideoRequestOptionsVersionOriginal, use 0 for edited versions. image_manager=ObjCClass('PHImageManager').defaultManager() handled_assets=[] def handleAsset(_obj,asset, audioMix, info): A=ObjCInstance(asset) '''I am just appending to handled_assets to process later''' handled_assets.append(A) ''' # alternatively, handle inside handleAsset. maybe need a threading.Lock here to ensure you are not sending storbinaries in parallel with open(str(A.resolvedURL().resourceSpecifier()),'rb') as fp: fro.storbinary(......) ''' handlerblock=ObjCBlock(handleAsset, argtypes=[c_void_p,]*4) for A in assets: #these are PHAssets image_manager.requestAVAssetForVideo(A, options=options, resultHandler=handlerblock) while len(handled_assets) < len(assets): '''wait for the asynchronous process to complete''' time.sleep(1) for A in handled_assets: url = str(A.resolvedURL().resourceSpecifier()) local = os.path.basename(url) print(url) shutil.copy(url,local)
-
Thank you. That allows me to find my videos, but how do I play them either in the console or otherwise?
-
-
The player works perfectly. If I wanted to pick a video and get it's url and save that for later, how would I pass that url to the player directly and not from the pick_asset call?
-
@frankL in the script, you can print(u) to get an url like assets-library://asset/asset.MOV?id=C03ABFB4-08F3-4A00-B626-BB9A01B293B2&ext=MOV
So, if you save this string in a file, you can reuse with these lines
url = nsurl('assets-library://asset/asset.MOV?id=C03ABFB4-08F3-4A00-B626-BB9A01B293B2&ext=MOV') u=ObjCClass('AVURLAsset').alloc().initWithURL_options_(url,None) i=AVPlayerItem.playerItemWithAsset_(u) p=AVPlayer.playerWithPlayerItem_(i) videolayer=AVPlayerLayer.playerLayerWithPlayer_(p)
-
I printed the url, substituted it as you suggested, but after:
url = nsurl('assets-library://asset/asset.MOV?id=7C43F2B6-68DB-4314-80A7-34C6F92E8C8A&ext=MOV')
C03ABFB4-08F3-4A00-B626-BB9A01B293B2&ext=MOV')
u=ObjCClass('AVURLAsset').alloc().initWithURL_options_(url,None)print(u) returns None
I must still be missing something. -
@frankL ok, sorry, print(u) is erroneous, I wanted to say print(url), what you did.
But now you have the url as a string, you can save it where you want and read it later to only
Display via some script lines of my post, without passing via pick -
@frankL this script only plays a video. Up to you to save the url in a file in the initial pick script and to read it and play in this script
from objc_util import * AVPlayerItem=ObjCClass('AVPlayerItem') AVPlayer=ObjCClass('AVPlayer') AVPlayerLayer=ObjCClass('AVPlayerLayer') url = nsurl('assets-library://asset/asset.MOV?id=C03ABFB4-08F3-4A00-B626-BB9A01B293B2&ext=MOV') u=ObjCClass('AVURLAsset').alloc().initWithURL_options_(url,None) i=AVPlayerItem.playerItemWithAsset_(u) p=AVPlayer.playerWithPlayerItem_(i) videolayer=AVPlayerLayer.playerLayerWithPlayer_(p) import ui v=ui.View(frame=(0,0,500,500)) V=ObjCInstance(v) videolayer.frame=V.bounds() V.layer().addSublayer_(videolayer) v.present('sheet') p.play()
-
That works perfectly, thank you.
Is there some documentation on video players and objc that you could point me to so that I can get a better understanding of how this all works? -
-
Thank you. I had only been looking at Pythonista 3 documentation and just recently came across the photos module. Photos doesn't appear to support much in the way of videos.
I appreciate all of your help. -
Is there a simple way to add controls to the player such as pause, play, etc.?
-
@frankL try this
from objc_util import * AVPlayerItem=ObjCClass('AVPlayerItem') AVPlayer=ObjCClass('AVPlayer') AVPlayerLayer=ObjCClass('AVPlayerLayer') url = nsurl('assets-library://asset/asset.MOV?id=71F89028-7FA0-4C53-B6AD-6659BC8458D8&ext=MOV') u=ObjCClass('AVURLAsset').alloc().initWithURL_options_(url,None) i=AVPlayerItem.playerItemWithAsset_(u) p=AVPlayer.playerWithPlayerItem_(i) videolayer=AVPlayerLayer.playerLayerWithPlayer_(p) import ui v=ui.View(frame=(0,0,500,500)) V=ObjCInstance(v) videolayer.frame=V.bounds() V.layer().addSublayer_(videolayer) v.present('sheet') p.play() bplay = ui.ButtonItem() bplay.title = 'play' def b_play_action(sender): p.play() bplay.action = b_play_action bpause = ui.ButtonItem() bpause.title = 'pause' def b_pause_action(sender): p.pause() bpause.action = b_pause_action v.right_button_items = (bplay, bpause)
-
@frankL you can even find an example of a slider in the minimal à player Of @JonB
-
Thank you. All that is running correctly now. How can I update the slider to indicate the elapsed time status of the video? The Apple documentation is very difficult to understand.
-
@frankL said:
How can I update the slider to indicate the elapsed time status of the video
from objc_util import * import ui AVPlayerItem=ObjCClass('AVPlayerItem') AVPlayer=ObjCClass('AVPlayer') AVPlayerLayer=ObjCClass('AVPlayerLayer') url = nsurl('assets-library://asset/asset.MOV?id=71F89028-7FA0-4C53-B6AD-6659BC8458D8&ext=MOV') u=ObjCClass('AVURLAsset').alloc().initWithURL_options_(url,None) i=AVPlayerItem.playerItemWithAsset_(u) p=AVPlayer.playerWithPlayerItem_(i) videolayer=AVPlayerLayer.playerLayerWithPlayer_(p) #define cmtime, for seeking import ctypes CMTimeValue=ctypes.c_int64 CMTimeScale=ctypes.c_int32 CMTimeFlags=ctypes.c_uint32 CMTimeEpoch=ctypes.c_int64 class CMTime(Structure): _fields_=[('value',CMTimeValue), ('timescale',CMTimeScale), ('flags',CMTimeFlags), ('epoch',CMTimeEpoch)] def __init__(self,value=0,timescale=1,flags=0,epoch=0): self.value=value self.timescale=timescale self.flags=flags self.epoch=epoch c.CMTimeMakeWithSeconds.argtypes=[ctypes.c_double,ctypes.c_int32] c.CMTimeMakeWithSeconds.restype=CMTime c.CMTimeGetSeconds.argtypes=[CMTime] c.CMTimeGetSeconds.restype=c_double class MyView(ui.View): def __init__(self, *args, **kwargs): ui.View.__init__(self, *args, **kwargs) self.background_color = 'white' V=ObjCInstance(self) videolayer.frame=V.bounds() V.layer().addSublayer_(videolayer) bplay = ui.ButtonItem() bplay.title = 'play' def b_play_action(sender): p.play() bplay.action = b_play_action self.update_interval = 0 bpause = ui.ButtonItem() bpause.title = 'pause' def b_pause_action(sender): p.pause() bpause.action = b_pause_action self.right_button_items = (bplay, bpause) slider=ui.Slider(frame=(0,0,self.width,20)) #sender.superview.name = str(sender.value*duration_sec) slider.action=self.slider_action slider.bring_to_front() self.slider = slider self.add_subview(slider) def update(self): s = p.currentTime().a/p.currentTime().b self.slider.value = s/duration_sec def slider_action(self, sender): self.seek(sender.value*duration_sec) @on_main_thread def seek(self,t): T=c.CMTimeMakeWithSeconds(t,1) p.seekToTime_(T,argtypes=[CMTime],restype=None) v=MyView(frame=(0,0,500,500)) v.present('sheet') duration=i.duration() duration_sec=duration.a/duration.b #print(duration_sec) p.play() v.update_interval = 0.1
-
@frankL try until
v.update_interval = 1/60
-
@frankL Is it what you wanted?
-
I see that your code works but I'm trying to implement it in a subview and I can't figure out how to use the v.update_interval method.