blinkpy icon indicating copy to clipboard operation
blinkpy copied to clipboard

Support for downloading videos from USB storage

Open muelli01 opened this issue 2 years ago • 3 comments

Is your feature request related to a problem? Please describe. As far as I understand, download_videos() only downloads videos from the cloud server. Despite check_new_videos() is telling me "True" and there are in fact new videos on the local USB storage, they are not being downloaded.

Describe the solution you'd like A feature should be implemented like download_videos_usb() to download the video files from attached USB storage.

muelli01 avatar Jun 27 '22 14:06 muelli01

Yeah I agree that needs to be added. As of right now, I don't think the API endpoint for local storage has been uncovered so we'd need that before anything could be added to the library

fronzbot avatar Jun 27 '22 21:06 fronzbot

I was bored and did a little traffic snooping last night. Found 2 new endpoints while looking at videos on my sync module:

Send an empty (0 byte) POST to {blink.urls.base_url}/api/v1/accounts/{blink.account_id}/networks/{network}/sync_modules/{sync_module_id}/local_storage/manifest/request (sync_module_id gets returned from blink.sync.attributes) This should return a JSON "id" (the request_id)

Poll api.request_command_status with the request_id (I check every 2 sec) until "complete": true comes back.

Then, do a GET to {blink.urls.base_url}/api/v1/accounts/{blink.account_id}/networks/{network}/sync_modules/{sync_module_id}/local_storage/manifest/request/{request_id} and you'll get a JSON of clip IDs off the sync module.

To download a clip:

Another blank (0 byte) POST to this endpoint: {blink.urls.base_url}/api/v1/accounts/{blink.account_id}/networks/{network}/sync_modules/{sync_module_id}/local_storage/manifest/{manifest_id}/clip/request/{clip_id}

(clip_id's are from the local manifest)

This should return another request "id"

Just like above, poll api.request_command_status with that id until "complete": true comes back.

Download the MP4 with GET {blink.urls.base_url}/api/v1/accounts/{blink.account_id}/networks/{network}/sync_modules/{sync_module_id}/local_storage/manifest/{manifest_id}/clip/request/{clip_id}

I have some bad python running right now, slowly uploading the 600 videos from my local sync module, up to the servers, and then downloading them back. =)

technoweenie314 avatar Jun 28 '22 07:06 technoweenie314

Is this supported now - it seems Jul 26 release became 0.19.2 - not 0.20.0 Is there an example on how to get the file from USB?

Panda88CO avatar Oct 06 '22 00:10 Panda88CO

Wow, great job @technoweenie314. I would love to know how you captured these, I had zero luck with Pi-ITM

webbpage avatar Nov 09 '22 18:11 webbpage

I added a PR for supporting this functionality. I didn't see the above message until now. Oh well, reverse engineering is fun anyhow.

perdue avatar Dec 02 '22 22:12 perdue

This is awesome, thanks @technoweenie314 for reverse engineering and @perdue for re-reverse engineering and implementing :)

I was able to figure out, by pure guess work, that replacing "request" with "delete" in the (0 byte) POST with the {clip_id} can also be used to delete the clip from local storage.

Here is some very crude code to download and delete videos one-by-one from the sync module, this will save me having to painfully go through the Blink app everyday to clear out the videos:

print("Going through syncs...")
for name, sync in blink.sync.items():
  print(name)
  #print(sync.attributes)
  #print(sync._local_storage)
  totalItems = len(sync._local_storage["manifest"])
  itemNumber = 1
  for manifestItem in sync._local_storage["manifest"]:
    manifestItem.prepare_download(blink)
    urlForVideo = blink.urls.base_url+manifestItem.url()
    cameraName = manifestItem._camera_name
    fileName = cameraName + manifestItem._created_at.strftime("%Y%m%d-%H%M%S") + ".mp4"
    fileName = fileName.replace("/", "-")
    print(str(itemNumber)+"/"+str(totalItems)+": "+fileName)
    media = blink.cameras[cameraName].get_video_clip(urlForVideo)
    if media.status_code == 200:
      with open(path+fileName, "wb") as clip_file:
        copyfileobj(media.raw, clip_file)
      urlForDelete = urlForVideo
      urlForDelete = urlForDelete.replace("request", "delete")
      blink.auth.session.send(blink.auth.prepare_request(urlForDelete, blink.auth.header, None, "post"))
    else:
      print(media)
    itemNumber = itemNumber+1
    time.sleep(10)

mustafazr avatar Dec 16 '22 01:12 mustafazr

Can someone please explain how to use with asyncio?

dersparkiie avatar Sep 09 '23 12:09 dersparkiie