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?
-
I've been looking at the photos module documentation and have managed some rudimentary scripts to access images on my iphone. The documentation seems to indicate that videos can also be accessed but when I've tried:
videos = photos.get_assets(media_type='video', include_hidden=True)
last_video = videos[-1]
video = last_video.get_ui_image()
video.show()It only displays the last photo, not video. What am I doing wrong? Thanks
-
@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?