fronzbot / blinkpy

A Python library for the Blink Camera system

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support for downloading videos from USB storage

muelli01 opened this issue · 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.

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

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. =)

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?

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

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

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)