Closed ZachBenz closed 6 years ago
A thought on recordingUrl vs. the ringDevice.recording_download
method: if you think about how the Ring app works, it allows you to immediately see video associated with an event. But, looking at the list of events in the app, if you haven't pulled up the video in response to the notification, you actually can't pull the video up from the list without waiting for a minute or two, presumably for the video to get archived in your cloud account following the live event.
This leads me to wonder if the recordingUrl is essentially a proxy for a live stream, staged temporarily on AWS but not yet permanently parked in the cloud account of the user. That is, recordingUrl, with its randomly generated authentication credentials and one hour expiry, allows the app to rapidly pull up the 'live' event video. Minutes later, the backend copies over and archives the video on the cloud, which allows you to download it via the API (as exposed by the ringDevice.recording_download
method). That said, there must be some kind of live SIP connection at some point to allow for the two way audio... so maybe recordingUrl has some other purpose...
All to say for this feature request either the method exposed by ringDevice.recording_download
or the recordingUrl could work (assuming you hit up the recordingUrl within the hour window). And it's possible that the video is available via the recordingUrl is available sooner than the other method (even immediately perhaps?).
Ignore my last comment - it's completely wrong. :-)
recordingUrl and ringDevice.recording_download
in tchellomello's library are exactly the same - both are using the dings/recordingID/recordings Ring API. What happens, is this API returns you a URL that is only good for one hour, with randomly generated authentication credentials. That is, after requesting the URL, you have no more than an hour to download the video, otherwise you need to request a new URL from the API.
Using this method you may need to wait until the video becomes available (?) after an event to download it, which could take at least a minute or two.
True live/immediate viewing would need to go the SIP connection route.
To implement this suggested feature, Ring.py's GetReordingUrl (or a new method associated with it) could be modified to work similarly to tchellomellos' ringDevice.recording_download:
def recording_download(self, recording_id, filename=None, override=False):
"""Save a recording in MP4 format to a file or return raw."""
if not self.has_subscription:
_LOGGER.warning("Your Ring account does not have" +
" an active subscription.")
return False
url = API_URI + URL_RECORDING.format(recording_id)
try:
req = self._ring.query(url, raw=True)
if req and req.status_code == 200:
if filename:
if os.path.isfile(filename) and not override:
_LOGGER.error("%s", FILE_EXISTS.format(filename))
return False
with open(filename, 'wb') as recording:
recording.write(req.content)
return True
else:
return req.content
except IOError as error:
_LOGGER.error("%s", error)
raise
That is, optionally download the file after getting the URL (block of code from if filename
on).
The main issue is figuring what, if any, delay there needs to be between the event firing and the video actually being available to download.
Just played around a bit, and looks like the recordingUrl currently retrieved by the Indigo plugin may not match up to the last event, probably because the url isn't 'ready' yet when the event is processed. I think the recordingUrl might be better retrieved on the fly when needed (e.g. add an Indigo action to the plugin to download video for the last event for a device, which would then do what is laid out in recording_download
above - that is, retrieve the URL just before downloading, checking to see that video is really ready, or perhaps waiting some user defined timeout before attempting the download to ensure it is ready.
Ok, I created a downloadVideo action for the plugin; creating a pull request now...
See pull request #6
I just tested out the v0.1.27 release with the downloadVideo action. I can't figure out why, but it always downloads a JSON file, not the mp4 content. The file is consistently a ~384 byte JSON file, consisting of the URL that the video download is supposed to come from - e.g. {"url":"https://ring-transcoded-videos.s3.amazonaws.com/
Just for a quick sanity check, I pulled up the URL that is appearing in the JSON download... and it loads in a browser just fine, pulling up the video.
Tried commenting out the call to self.Ring.downloadVideo in v0.1.27's plugin.py
, and replaced it with copy-pasted code from my forked version of the plugin (as copied below; also had to import requests in plugin.py
), and still getting the JSON back, not a video.
#Perform the work
#self.Ring.downloadVideo(dev, filename, eventId)
#DEBUG_ZOB__vvvvvvv
try:
if filename:
# Copied headers from Ring.py temporarily until this code is moved in there
theHeaders = {'content-type': 'application/json', 'User-Agent': 'ring/3.6.4 (iPhone; iOS 10.2; Scale/2.00)'}
url = self.Ring.GetRecordingUrl(eventId)
response = requests.get(url, headers=theHeaders, verify=False)
if response and response.status_code == 200:
with open(filename, 'wb') as recording:
recording.write(response.content)
indigo.server.log(u"Downloaded video of event for \"%s\" to %s" % (dev.name, filename))
return
elif response:
indigo.server.log(u"Failed to download for \"%s\", response status code was %s" % (dev.name, response.status_code), isError=True)
else:
indigo.server.log(u"Failed to download for \"%s\", no response for url %s" % (dev.name, url), isError=True)
else:
indigo.server.log(u"Missing filename setting in action settings for video download of event for \"%s\"" % (dev.name), isError=True)
return
except IOError as error:
indigo.server.log(u"%s" % (error), isError=True)
So perhaps it is the changes to GetRecordingUrl in v0.1.27 that is causing the problem? I noted that the url being constructed to make the API request for the recordingUrl has changed from:
url = '%sdings/%s/recording?api_version=8&auth_token=%s' % (self.baseUrl, recordingId, self.sessionID)
to:
url = '%sdings/%s/recording?api_version=9&disable_redirect=true&auth_token=%s' % (self.baseUrl, recordingId, self.sessionID)
Also, the associated request header has changed from:
request.add_header('User-Agent', 'AppleCoreMedia/1.0.0.14C92 (iPhone; U; CPU OS 10_2 like Mac OS X; en_us)')
to:
request.add_header('User-Agent', 'ring/4.1.8 (iPhone; iOS 11.2.5; Scale/2.00)')
As a minor aside, Ring.py's downloadVideo doesn't handle when GetRecordingUrl returns 'No Subscription' as the url
If I revert the url construction in Ring.py's GetRecordingUrl to:
url = '%sdings/%s/recording?api_version=8&auth_token=%s' % (self.baseUrl, recordingId, self.sessionID)
The actual video file downloads again!
Ok, the new url construction will also work if you change disable_redirect=true
to false; that is, this url construction works in GetRecordingUrl:
url = '%sdings/%s/recording?api_version=9&disable_redirect=false&auth_token=%s' % (self.baseUrl, recordingId, self.sessionID)
When the redirect disabled, it appears the video itself never gets loaded, and you just get the JSON with the URL. Allowing redirect causes the video content to be retrieved.
Fixed in 0.1.29
Note: this has to do with video recorded and saved by Ring in the cloud based on a triggered event (e.g. motion, doorbell press, or manual live view ideo trigger), and requires that the user have a cloud subscription. This is different than triggering the viewing of live video in real time, which would be a wholly separate endeavor, likely involving setting up SIP sessions directly with the Ring device (or somehow triggering a manual live view and then grabbing that video from e.g. the recordingUrl)
Using the python library @mpoulson pointed out on the Indigo support forms (https://github.com/tchellomello/python-ring-doorbell), it's fairly straightforward to grab a recorded video:
A number of things need to be figured out for this to be useful: