Files
download-athletico/athletico/athletico.py

139 lines
5.3 KiB
Python

import json
from pathlib import Path
import jinja2
import requests
from requests import RequestException
class Athletico:
def fetch_episodes_for_access_code(self, access_code: str) -> dict:
"""
Given a home access code, fetch exercise information
:param access_code: the access code to a home exercise program from Athletico
:return: a dict containing the exercise information
"""
session = requests.Session()
home_url = "https://athleticopt.medbridgego.com"
fetch_episodes_url = "https://athleticopt.medbridgego.com/api/v4/lite/episodes/"
session.get(home_url)
csrf_token = session.cookies.get("csrf_cookie_name")
session.post(
url="https://athleticopt.medbridgego.com/register_token",
data={
"token": access_code,
"X-CSRF-Token": csrf_token,
"verify_access_code": "Verify+Access+Code",
},
)
response = session.get(fetch_episodes_url)
if response.ok and response.json() and response.json().get("status"):
return response.json().get("episodes")
else:
raise RequestException(
f"Request Failed, {response.status_code}: {response.reason}"
)
def render(self, template: str, output: str, context: dict) -> str:
"""
Renders an HTML page.
:param template: Path to Jinja template
:param output: Destination for .html file
:param context: The context to be passed to the render
:return: Path to html file
"""
output_fpath = Path(output)
template_fpath = Path(output)
environment = jinja2.Environment(loader=jinja2.FileSystemLoader())
template = environment.get_template(str(template_fpath.absolute()))
page = template.render(**context)
output_fpath.mkdir(parents=True, exist_ok=True)
with output_fpath.open("w") as f:
f.write(page)
return str(output_fpath)
def save_episodes(self, episodes: list, destination: str = ".") -> None:
"""
Outputs episodes to disk in a folder structure
├── exercises/
│ ├── {EXERCISE_ID}/
│ │ ├── {EXERCISE_ID}.json
│ │ ├── {EXERCISE_ID}.txt
│ │ ├── description.html
│ │ └── video_file.m3u8
│ └── {EXERCISE_ID}/
│ ├── {EXERCISE_ID}.json
│ ├── {EXERCISE_ID}.txt
│ ├── description.html
│ └── video_file.m3u8
└── programs/
└── {ACCESS_TOKEN}/
└── episodes/
└── {EPISODE_ID}.json
:param episodes: List of episode data, matches output of fetch_episodes_for_access_code
:param destination: Filepath to save to, defaults to current directory
:return: None
"""
destination_fpath = Path(destination)
destination_fpath.joinpath()
for episode in episodes:
episode_id = str(episode["id"])
program = episode["program"]
exercises = []
episode_fpath = destination_fpath.joinpath(
"programs", episode["token"], "episodes"
)
episode_fpath.mkdir(parents=True, exist_ok=True)
for exercise in program["program_exercises"]:
exercises.append(exercise)
exercise_id = str(exercise["id"])
exercise_fpath = destination_fpath.joinpath("exercises", exercise_id)
thumbnails_fpath = exercise_fpath.joinpath("thumbnails")
thumbnails_fpath.mkdir(parents=True, exist_ok=True)
for i, image in enumerate(exercise["exercise"]["thumbnails"]):
r = requests.get(image["image_filepath"])
fname = image["image_filepath"].split("/")[-1]
with thumbnails_fpath.joinpath(fname).open("wb") as f:
f.write(r.content)
json_fpath = exercise_fpath.joinpath(exercise_id).with_suffix(".json")
with json_fpath.open("w") as f:
json.dump(exercise, f)
txt_fpath = exercise_fpath.joinpath(exercise_id).with_suffix(".txt")
with txt_fpath.open("w") as f:
f.write(exercise["name"])
if exercise["exercise"]["video_file"]:
video_fpath = exercise_fpath.joinpath("video_file").with_suffix(
".m3u8"
)
with video_fpath.open("wb") as f:
r = requests.get("http:" + exercise["exercise"]["video_file"])
f.write(r.content)
description_fpath = exercise_fpath.joinpath("description").with_suffix(
".html"
)
with description_fpath.open("w") as f:
f.write(exercise["exercise"]["description"])
episode_json_fpath = episode_fpath.joinpath(episode_id).with_suffix(".json")
with episode_json_fpath.open("w") as f:
json.dump(episode, fp=f)
return
if __name__ == "__main__":
pass