├── README.md ├── assets └── davinci-wakatime.mov ├── davinci-hackatime.py ├── free └── davinci-hackatime.py └── paid - WIP └── davinci-hackatime.py /README.md: -------------------------------------------------------------------------------- 1 | # DaVinci Resolve WakaTime 2 | 3 | 2 scripts to send WakaTime heartbeats, the free version of DaVinci Resolve does not allow you to use the `DaVinciResolveScript` API, BUT we can watch changes in AppData folder. 4 | 5 | However, I haven't tested the paid version, I don't have the money for that. If you have paid version, you can also use free version. 6 | 7 | Pull Requests and Issues are welcome! 8 | 9 | ## Setup 10 | 11 | Clone the repo (or download [DaVinci Resolve WakaTime](/free/davinci-hackatime.py)): 12 | 13 | ``` 14 | git clone https://github.com/LucasHT22/davinci-resolve-wakatime.git 15 | ``` 16 | 17 | In `davinci-hackatime.py`, change `WAKATIME_API_KEY` to your WakaTime API Key and change `WATCH_FOLDER`. 18 | 19 | 20 | Your file path should look like something like this: 21 | 22 | ### Windows 23 | ``` 24 | C:\\Users\\{USER}\\AppData\\Roaming\\Blackmagic Design\\DaVinci Resolve\\Support\\Resolve Project Library\\Resolve Projects\\Users\\guest\\Projects\\{PROJECT_NAME} 25 | ``` 26 | 27 | ### Mac 28 | ``` 29 | \\Users\\{USER}\\Library\\Application Support\\Blackmagic Design\\DaVinci Resolve\\Support\\Resolve Disk Database\\Resolve Projects\\\Users\\guest\\Projects\\{PROJECT_NAME} 30 | ``` 31 | 32 | Make sure there is a `Project.db` inside, if you just created the project in DaVinci Resolve, it may not appear, add something to the timeline and it should work. 33 | 34 | > Don't forget to use `\\` for file path! \ or / won't work. 35 | 36 | ## Compatibility 37 | 38 | I (Lucas) have a Windows machine, tests are welcome! 39 | 40 | | OS | Tested | 41 | | -- | ------ | 42 | | Windows | ✅ | 43 | | Mac | | 44 | 45 | ## How It Works? 46 | 47 | 1. Script look for changes in `WATCH_FOLDER` every 30 seconds 48 | 2. If change is detected, a WakaTime heartbeat is sent 49 | 3. Done! 50 | 51 | 52 | 53 | ## Main References 54 | 55 | - [WakaTime Developers](https://wakatime.com/developers) 56 | - [Resolve Scripting Documentation - Blackmagic Design Forum](https://forum.blackmagicdesign.com/viewtopic.php?f=21&t=78611) 57 | - [Python3 DaVinci Resolve 19 Console Not Found - alexthecreative](https://alexthecreative.com/python3-davinci-resolve-19-console-not-found/) 58 | - [Scripting in the free version? - Blackmagic Design Forum](https://forum.blackmagicdesign.com/viewtopic.php?f=21&t=113252) 59 | - [Where Does Davinci Resolve Save Projects and Project Files? - contentcreatortemplates](https://www.contentcreatortemplates.com/learn/where-does-davinci-resolve-save-projects) 60 | - [Scripting: DaVinciResolveScript module not found - Blackmagic Design Forum](https://forum.blackmagicdesign.com/viewtopic.php?f=21&t=137340) 61 | -------------------------------------------------------------------------------- /assets/davinci-wakatime.mov: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LucasHT22/davinci-resolve-wakatime/8d3fd12161569330dd87f1558cc9dea15edae029/assets/davinci-wakatime.mov -------------------------------------------------------------------------------- /davinci-hackatime.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import time 4 | import base64 5 | import urllib.request 6 | import urllib.error 7 | import json 8 | import psutil 9 | from datetime import datetime 10 | 11 | WAKATIME_API_KEY = 'YOUR_WAKATIME_API_KEY' 12 | WATCH_FOLDER = "ADD_YOUR_FOLDER" 13 | HEARTBEAT_INTERVAL = 120 14 | CHECK_INTERVAL = 60 15 | 16 | def send_heartbeat(file_path, project_name): 17 | encoded_key = base64.b64encode(WAKATIME_API_KEY.encode()).decode() 18 | headers = { 19 | "Authorization": f"Basic {encoded_key}", 20 | "Content-Type": "application/json" 21 | } 22 | payload = { 23 | "time": datetime.utcnow().timestamp(), 24 | "entity": file_path, 25 | "type": "file", 26 | "category": "coding", 27 | "is_write": True, 28 | "project": project_name, 29 | "language": "video editing", 30 | "plugin": "davinci-resolve-wakatime" 31 | } 32 | req = urllib.request.Request( 33 | url='https://hackatime.hackclub.com/api/hackatime/v1/users/current/heartbeats', 34 | data=json.dumps(payload).encode('utf-8'), 35 | headers=headers, 36 | method='POST' 37 | ) 38 | try: 39 | with urllib.request.urlopen(req) as response: 40 | print(f"[{datetime.now()}] Sent heartbeat: {response.status}") 41 | except urllib.error.HTTPError as e: 42 | print(f"[{datetime.now()}] Error sending heartbeat: {e.code} - {e.reason}") 43 | except urllib.error.URLError as e: 44 | print("[{}] Network error: {}".format(datetime.now(), e.reason)) 45 | 46 | def main(): 47 | last_sent = 0 48 | last_mod_time = 0 49 | 50 | while True: 51 | most_recent_file = None 52 | most_recent_time = 0 53 | 54 | for root, _, files in os.walk(WATCH_FOLDER): 55 | for file in files: 56 | if file.endswith(".db"): 57 | full_path = os.path.join(root, file) 58 | try: 59 | mod_time = os.path.getmtime(full_path) 60 | except FileNotFoundError: 61 | continue 62 | if mod_time > most_recent_time: 63 | most_recent_time = mod_time 64 | most_recent_file = full_path 65 | 66 | now = time.time() 67 | if most_recent_file and most_recent_time > last_mod_time and now - last_sent > HEARTBEAT_INTERVAL: 68 | project_name = os.path.basename(os.path.dirname(most_recent_file)) 69 | send_heartbeat(most_recent_file, project_name) 70 | last_sent = now 71 | last_mod_time = most_recent_time 72 | 73 | time.sleep(CHECK_INTERVAL) 74 | 75 | if __name__ == "__main__": 76 | main() -------------------------------------------------------------------------------- /free/davinci-hackatime.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import time 4 | import base64 5 | import urllib.request 6 | import urllib.error 7 | import json 8 | import psutil 9 | from datetime import datetime 10 | 11 | WAKATIME_API_KEY = 'YOUR_WAKATIME_API_KEY' 12 | WATCH_FOLDER = "ADD_YOUR_FOLDER" 13 | HEARTBEAT_INTERVAL = 120 14 | CHECK_INTERVAL = 60 15 | 16 | def send_heartbeat(file_path, project_name): 17 | encoded_key = base64.b64encode(WAKATIME_API_KEY.encode()).decode() 18 | headers = { 19 | "Authorization": f"Basic {encoded_key}", 20 | "Content-Type": "application/json" 21 | } 22 | payload = { 23 | "time": datetime.utcnow().timestamp(), 24 | "entity": file_path, 25 | "type": "file", 26 | "category": "coding", 27 | "is_write": True, 28 | "project": project_name, 29 | "language": "video editing", 30 | "plugin": "davinci-resolve-wakatime" 31 | } 32 | req = urllib.request.Request( 33 | url='https://hackatime.hackclub.com/api/hackatime/v1/users/current/heartbeats', 34 | data=json.dumps(payload).encode('utf-8'), 35 | headers=headers, 36 | method='POST' 37 | ) 38 | try: 39 | with urllib.request.urlopen(req) as response: 40 | print(f"[{datetime.now()}] Sent heartbeat: {response.status}") 41 | except urllib.error.HTTPError as e: 42 | print(f"[{datetime.now()}] Error sending heartbeat: {e.code} - {e.reason}") 43 | except urllib.error.URLError as e: 44 | print("[{}] Network error: {}".format(datetime.now(), e.reason)) 45 | 46 | def main(): 47 | last_sent = 0 48 | last_mod_time = 0 49 | 50 | while True: 51 | most_recent_file = None 52 | most_recent_time = 0 53 | 54 | for root, _, files in os.walk(WATCH_FOLDER): 55 | for file in files: 56 | if file.endswith(".db"): 57 | full_path = os.path.join(root, file) 58 | try: 59 | mod_time = os.path.getmtime(full_path) 60 | except FileNotFoundError: 61 | continue 62 | if mod_time > most_recent_time: 63 | most_recent_time = mod_time 64 | most_recent_file = full_path 65 | 66 | now = time.time() 67 | if most_recent_file and most_recent_time > last_mod_time and now - last_sent > HEARTBEAT_INTERVAL: 68 | project_name = os.path.basename(os.path.dirname(most_recent_file)) 69 | send_heartbeat(most_recent_file, project_name) 70 | last_sent = now 71 | last_mod_time = most_recent_time 72 | 73 | time.sleep(CHECK_INTERVAL) 74 | 75 | if __name__ == "__main__": 76 | main() -------------------------------------------------------------------------------- /paid - WIP/davinci-hackatime.py: -------------------------------------------------------------------------------- 1 | 2 | import os 3 | import time 4 | import base64 5 | import json 6 | import urllib.request 7 | import urllib.error 8 | from datetime import datetime 9 | 10 | import sys 11 | 12 | script_path = r"C:\ProgramData\Blackmagic Design\DaVinci Resolve\Support\Developer\Scripting\Modules" 13 | 14 | 15 | if script_path not in sys.path: 16 | sys.path.append(script_path) 17 | 18 | import DaVinciResolveScript as dvr 19 | 20 | 21 | WAKATIME_API_KEY = 'YOUR_WAKATIME_API_KEY' 22 | PROJECT_NAME = 'DaVinci Resolve Project' 23 | HEARTBEAT_INTERVAL = 120 24 | CHECK_INTERVAL = 60 25 | 26 | resolve = dvr.scriptapp("Resolve") 27 | if not resolve: 28 | print("Can't connect to DaVinci Resolve Script.") 29 | exit(1) 30 | 31 | project_manager = resolve.GetProjectManager() 32 | media_storage = resolve.GetMediaStorage() 33 | 34 | def send_heartbeat(file_path, project_name): 35 | encoded_key = base64.b64encode(WAKATIME_API_KEY.encode()).decode() 36 | headers = { 37 | "Authorization": f"Basic {encoded_key}", 38 | "Content-Type": "application/json" 39 | } 40 | payload = { 41 | "time": datetime.utcnow().timestamp(), 42 | "entity": file_path, 43 | "type": "file", 44 | "category": "coding", 45 | "is_write": True, 46 | "project": project_name, 47 | "language": "video editing", 48 | "plugin": "davinci-resolve-wakatime" 49 | } 50 | req = urllib.request.Request( 51 | url='https://hackatime.hackclub.com/api/hackatime/v1/users/current/heartbeats', 52 | data=json.dumps(payload).encode('utf-8'), 53 | headers=headers, 54 | method='POST' 55 | ) 56 | try: 57 | with urllib.request.urlopen(req) as response: 58 | print(f"[{datetime.now()}] Sent heartbeat: {response.status}") 59 | except urllib.error.HTTPError as e: 60 | print(f"[{datetime.now()}] Error sending heartbeat: {e.code} - {e.reason}") 61 | 62 | def main(): 63 | last_sent = 0 64 | 65 | while True: 66 | project = project_manager.GetCurrentProject() 67 | if not project: 68 | print("No project open found.") 69 | time.sleep(CHECK_INTERVAL) 70 | continue 71 | 72 | project_name = project.GetName() 73 | timeline = project.GetCurrentTimeline() 74 | 75 | if timeline: 76 | timeline_name = timeline.GetName() 77 | else: 78 | timeline_name = "NoTimeline" 79 | 80 | file_path = f"{project_name}/{timeline_name}" 81 | 82 | now = time.time() 83 | 84 | if now - last_sent > HEARTBEAT_INTERVAL: 85 | send_heartbeat(file_path, project_name) 86 | last_sent = now 87 | 88 | time.sleep(HEARTBEAT_INTERVAL) 89 | 90 | if __name__ == "__main__": 91 | main() 92 | --------------------------------------------------------------------------------