From 9235669ba5a5a583c93ba9ece8b3eb04f94ee189 Mon Sep 17 00:00:00 2001 From: gabriel becker Date: Thu, 24 Aug 2023 23:34:57 +1000 Subject: [PATCH] Initial commit. Creates valid json for database. Properlly laods yaml input. Properly downloads video and creates short clips --- create_open_workout_database.py | 91 +++++++++++++++++++++++++ data/databaseSchema.json | 108 ++++++++++++++++++++++++++++++ data/input/samples/exercises.yaml | 25 +++++++ load_exercises.py | 43 ++++++++++++ readme.md | 17 +++++ requirements.txt | 4 ++ 6 files changed, 288 insertions(+) create mode 100644 create_open_workout_database.py create mode 100644 data/databaseSchema.json create mode 100644 data/input/samples/exercises.yaml create mode 100644 load_exercises.py create mode 100644 readme.md create mode 100644 requirements.txt diff --git a/create_open_workout_database.py b/create_open_workout_database.py new file mode 100644 index 0000000..5c10f0a --- /dev/null +++ b/create_open_workout_database.py @@ -0,0 +1,91 @@ +import json + +WORKOUT_DATABASE = { + "countFinishedTraining": 0, + "imagePath": "defaultTraining.png", + "isImagePathExternal": False, + "name": "lol2", + "orderNr": -1, + "trainingPlanId": 0, + "workoutSessions": [] +} + + +def create_work_database(name, workout_sessions): + new_db = dict(WORKOUT_DATABASE) + new_content = { + "name": name, + "workoutSessions": workout_sessions + } + + new_db.update(new_content) + return new_db + + +WORKOUT_SESSION = { + "finished": False, + "name": "1. day", + "orderNr": -1, + "trainingPlanId": 13, + "workoutItems": [], + "workoutSessionId": 0 +} + + +def create_work_session(workout_items: list): + new_session = dict(WORKOUT_SESSION) + new_session_content = { + "workoutItems": workout_items + } + new_session.update(new_session_content) + return new_session + + +WOPRKOUT_ITEM = { + "breakTime": 2, + "description": "Stand up with your legs spread and your hands touching overhead. Then as you jump, bring your legs back together and put your arms to your sides. You can speed these up or slow them down to suit your fitness level.", + "elapsedTime": 0, + "finished": False, + "imagePath": "jumping_jack.png", + "isImagePathExternal": False, + "isTimeMode": True, + "isVideoMode": True, + "isVideoPathExternal": False, + "name": "Jumping Jack", + "orderNr": -1, + "prepTime": 5, + "repetitionCount": 5, + "videoPath": "jumping_jack.mp4", + "workoutItemId": 0, + "workoutSessionId": 1, + "workoutTime": 30 +} + + +def create_item(): + new_item = dict(WOPRKOUT_ITEM) + new_item_content = { + "description": "Stand up with your legs spread and your hands touching overhead. Then as you jump, bring your legs back together and put your arms to your sides. You can speed these up or slow them down to suit your fitness level.", + "elapsedTime": 0, + "imagePath": "jumping_jack.png", + "isImagePathExternal": False, + "isTimeMode": True, + "isVideoMode": True, + "prepTime": 5, + "repetitionCount": 5, + "videoPath": "jumping_jack.mp4", + "workoutTime": 30 + } + new_item.update(new_item_content) + return new_item + + +def lasdikfsh(): + items = [create_item()] + sessions = [create_work_session(items)] + db = create_work_database('urdur', sessions) + with open("your_json_file.json", "w") as fp: + json.dump(db , fp) + +if __name__ == '__main__': + lasdikfsh() diff --git a/data/databaseSchema.json b/data/databaseSchema.json new file mode 100644 index 0000000..bd73ca1 --- /dev/null +++ b/data/databaseSchema.json @@ -0,0 +1,108 @@ +{ + "type": "object", + "required": [], + "properties": { + "countFinishedTraining": { + "type": "string" + }, + "imagePath": { + "type": "string" + }, + "isImagePathExternal": { + "type": "string" + }, + "name": { + "type": "string" + }, + "orderNr": { + "type": "number" + }, + "trainingPlanId": { + "type": "string" + }, + "workoutSessions": { + "type": "array", + "items": { + "type": "object", + "required": [], + "properties": { + "finished": { + "type": "boolean" + }, + "name": { + "type": "string" + }, + "orderNr": { + "type": "string" + }, + "trainingPlanId": { + "type": "number" + }, + "workoutItems": { + "type": "array", + "items": { + "type": "object", + "required": [], + "properties": { + "breakTime": { + "type": "number" + }, + "description": { + "type": "string" + }, + "elapsedTime": { + "type": "number" + }, + "finished": { + "type": "boolean" + }, + "imagePath": { + "type": "string" + }, + "isImagePathExternal": { + "type": "string" + }, + "isTimeMode": { + "type": "boolean" + }, + "isVideoMode": { + "type": "boolean" + }, + "isVideoPathExternal": { + "type": "string" + }, + "name": { + "type": "string" + }, + "orderNr": { + "type": "string" + }, + "prepTime": { + "type": "number" + }, + "repetitionCount": { + "type": "number" + }, + "videoPath": { + "type": "string" + }, + "workoutItemId": { + "type": "string" + }, + "workoutSessionId": { + "type": "number" + }, + "workoutTime": { + "type": "number" + } + } + } + }, + "workoutSessionId": { + "type": "string" + } + } + } + } + } + } \ No newline at end of file diff --git a/data/input/samples/exercises.yaml b/data/input/samples/exercises.yaml new file mode 100644 index 0000000..9c5ca2f --- /dev/null +++ b/data/input/samples/exercises.yaml @@ -0,0 +1,25 @@ +exercises: + - name: nomoney + video: "https://www.youtube.com/watch?v=etAwQ4jzyNY" + start: 74 + end: 80 + - name: plank shoulder tap + video: "https://www.youtube.com/watch?v=etAwQ4jzyNY" + start: 113 + end: 116 + - name: standing ws + video: "https://www.youtube.com/watch?v=etAwQ4jzyNY" + start: 243 + end: 248 + - name: side plank + video: "https://www.youtube.com/watch?v=etAwQ4jzyNY" + start: 277 + end: 280 + - name: face pulls + video: "https://www.youtube.com/watch?v=etAwQ4jzyNY" + start: 413 + end: 417 + - name: recruitment pulls + video: "https://www.youtube.com/watch?v=etAwQ4jzyNY" + start: 541 + end: 548 diff --git a/load_exercises.py b/load_exercises.py new file mode 100644 index 0000000..450f08a --- /dev/null +++ b/load_exercises.py @@ -0,0 +1,43 @@ +import yaml +import pytube +import tempfile +import imageio + +from pathlib import Path +from functools import lru_cache +import moviepy.editor as mpy + + +FILE = 'data/input/samples/exercises.yaml' + + +@lru_cache(5) +def get_video(url): + output_dir = tempfile.mktemp() + video = pytube.YouTube(url) + video.streams.filter(res="360p").first().download(filename=output_dir) + return output_dir + +def create_gif_from_video_and_timestamps(video_path, start, end): + video = mpy.VideoFileClip(video_path) + clip: mpy.VideoFileClip = video.subclip(start, end) + output_dir = tempfile.mktemp() + '.mp4' + clip.set_fps(2) + clip.without_audio().write_videofile(output_dir) + return output_dir + + +if __name__ == '__main__': + file = open(FILE) + exercises = yaml.safe_load(file) + for ex in exercises['exercises']: + name = ex['name'] + video_url = ex['video'] + start = ex['start'] + end = ex['end'] + + video_path = get_video(video_url) + gif = create_gif_from_video_and_timestamps(video_path, start, end) + ex['gif_path'] = gif + + print(gif) diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..6a036b9 --- /dev/null +++ b/readme.md @@ -0,0 +1,17 @@ +# Cli-Coach +Creates a workout plan form an yaml file that can be imported to [openWorkout](https://github.com/oliexdev/openWorkout) android app. + +Inputs can be of format +```yaml +exercises: + - name: nomoney + video: "https://www.youtube.com/watch?v=etAwQ4jzyNY" + start: 74 + end: 80 + - name: plank shoulder tap + video: "https://www.youtube.com/watch?v=etAwQ4jzyNY" + start: 113 + end: 116 +``` + +The script will download the video and create short clips. \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..eeea973 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +pyyaml +pytube +moviepy +imageio \ No newline at end of file