Skip to content

Instantly share code, notes, and snippets.

@5E7EN
Forked from pmac/ring-videos-download.py
Last active August 18, 2023 06:59
Show Gist options
  • Save 5E7EN/612d25f06909ff41bd131eb597ef2372 to your computer and use it in GitHub Desktop.
Save 5E7EN/612d25f06909ff41bd131eb597ef2372 to your computer and use it in GitHub Desktop.
Download all the videos between two events from Ring
import json
import getpass
import time
import requests
import logging
from pathlib import Path
from pprint import pprint
# https://github.com/tchellomello/python-ring-doorbell
from ring_doorbell import Ring, Auth, RingStickUpCam
from oauthlib.oauth2 import MissingTokenError
# Constants
OLDER_THAN = 7267899867159244279
OLDEST_ONE = 7267176238184312311
CHUNK_SIZE = 500
MAX_RETRIES = 3
CACHE_FILE_PATH = Path("token.cache")
USER_AGENT = "VideoDownloader/1.0"
RETRY_SLEEP_TIME = 5
logging.getLogger('ring_doorbell').setLevel(logging.CRITICAL)
def token_updated(token):
"""Callback function to update token cache."""
with CACHE_FILE_PATH.open('w') as cache_file:
json.dump(token, cache_file)
def otp_callback():
"""Handle 2-factor authentication."""
return input("2FA code (check your email/phone/authenticator): ")
def initialize_auth():
"""Initialize authentication."""
if CACHE_FILE_PATH.is_file():
with CACHE_FILE_PATH.open('r') as cache_file:
auth_token = json.load(cache_file)
return Auth(USER_AGENT, auth_token, token_updated)
username = input("Enter your Ring email address: ")
password = getpass.getpass("Password: ")
auth = Auth(USER_AGENT, None, token_updated)
try:
auth.fetch_token(username, password)
except MissingTokenError:
auth.fetch_token(username, password, otp_callback())
return auth
def download(cam: RingStickUpCam):
"""Download videos from a camera."""
count = 0
eid = OLDER_THAN
while True:
# Fetch the camera event history
events = cam.history(older_than=eid, limit=CHUNK_SIZE)
# Loop through the events and download each video (newest -> oldest)
for event in events:
eid = event['id']
if eid < OLDEST_ONE:
return
success = download_event(cam, eid)
if success:
count += 1
print(f'Downloaded #{count} -> {eid}')
def download_event(cam: RingStickUpCam, eid):
"""Attempt to download a specific event. Returns True if successful."""
retries = 0
video_path = Path(f'videos/{cam.name}')
video_path.mkdir(parents=True, exist_ok=True)
while retries < MAX_RETRIES:
try:
cam.recording_download(eid, filename=video_path/f'{eid}.mp4')
return True
except requests.exceptions.RequestException as e:
if '404' in str(e):
retries += 1
print(f"Download failed for {eid}. Retrying {retries}/{MAX_RETRIES}...")
time.sleep(RETRY_SLEEP_TIME)
else:
raise
print(f"- Failed to download {eid} after {MAX_RETRIES} attempts!")
return False
def main():
"""Main function."""
auth = initialize_auth()
ring = Ring(auth)
ring.update_data()
devices = ring.devices()
pprint(devices)
cameras = devices['stickup_cams']
# Determine the index of the camera to download from the list above and edit this (0 = first camera, 1 = second camera, etc.)
targetCam = cameras[0]
print(f'Downloading videos from {targetCam.name}...')
download(targetCam)
print('\nDONE.')
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment