├── .DS_Store ├── Download ├── .DS_Store └── dailiespipev0.1a.zip ├── DaVinciAccess ├── __init__.py ├── __main__.py ├── python_get_resolve.py └── DaVinciAccess.py ├── SilverstackAccess ├── __init__.py ├── __main__.py └── SilverstackAccess.py ├── test.json ├── LICENSE.md ├── .gitignore ├── dailiespipe.py └── README.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andostini/DailiesPipe/HEAD/.DS_Store -------------------------------------------------------------------------------- /Download/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andostini/DailiesPipe/HEAD/Download/.DS_Store -------------------------------------------------------------------------------- /DaVinciAccess/__init__.py: -------------------------------------------------------------------------------- 1 | from DaVinciAccess.DaVinciAccess import Project, getProjects, getSubfolderByName -------------------------------------------------------------------------------- /Download/dailiespipev0.1a.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andostini/DailiesPipe/HEAD/Download/dailiespipev0.1a.zip -------------------------------------------------------------------------------- /SilverstackAccess/__init__.py: -------------------------------------------------------------------------------- 1 | from SilverstackAccess.SilverstackAccess import findSilverstackInstances, getProjectList, Project -------------------------------------------------------------------------------- /DaVinciAccess/__main__.py: -------------------------------------------------------------------------------- 1 | import DaVinciAccess as DaVinci 2 | 3 | 4 | 5 | 6 | if __name__ == "__main__": 7 | print(DaVinci.getProjects()) 8 | 9 | #Master/Source/Audi/ 10 | 11 | project = DaVinci.Project('TRAINING') 12 | input() 13 | print(project.checkConnection()) -------------------------------------------------------------------------------- /SilverstackAccess/__main__.py: -------------------------------------------------------------------------------- 1 | import SilverstackAccess as Silverstack 2 | import subprocess 3 | 4 | 5 | 6 | 7 | 8 | if __name__ == "__main__": 9 | instances = Silverstack.findSilverstackInstances() 10 | projects = Silverstack.getProjectList(0) 11 | 12 | project = Silverstack.Project(projects[3]) 13 | project.fetchLibrary() 14 | 15 | project.fetchFolderStructure() 16 | -------------------------------------------------------------------------------- /test.json: -------------------------------------------------------------------------------- 1 | [{"PK": 9, "name": "SD01", "type": "folder", "sortIndex": 0, "parent": 6, "subFolders": [{"PK": 10, "name": "A_CAM", "type": "folder", "sortIndex": 0, "parent": 9, "subFolders": [{"PK": 13, "name": "StillsA_trial", "type": "bin", "sortIndex": 0, "parent": 10, "subFolders": null}, {"PK": 28, "name": "A999_trial", "type": "bin", "sortIndex": 1, "parent": 10, "subFolders": null}]}, {"PK": 24, "name": "B_Cam", "type": "folder", "sortIndex": 1, "parent": 9, "subFolders": [{"PK": 26, "name": "B001_trial", "type": "bin", "sortIndex": 0, "parent": 24, "subFolders": null}]}]}] -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2021 Fabian 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /DaVinciAccess/python_get_resolve.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | This file serves to return a DaVinci Resolve object 5 | """ 6 | 7 | import sys 8 | 9 | def GetResolve(): 10 | try: 11 | # The PYTHONPATH needs to be set correctly for this import statement to work. 12 | # An alternative is to import the DaVinciResolveScript by specifying absolute path (see ExceptionHandler logic) 13 | import DaVinciResolveScript as bmd 14 | except ImportError: 15 | if sys.platform.startswith("darwin"): 16 | expectedPath="/Library/Application Support/Blackmagic Design/DaVinci Resolve/Developer/Scripting/Modules/" 17 | elif sys.platform.startswith("win") or sys.platform.startswith("cygwin"): 18 | import os 19 | expectedPath=os.getenv('PROGRAMDATA') + "\\Blackmagic Design\\DaVinci Resolve\\Support\\Developer\\Scripting\\Modules\\" 20 | elif sys.platform.startswith("linux"): 21 | expectedPath="/opt/resolve/libs/Fusion/Modules/" 22 | 23 | # check if the default path has it... 24 | print("Unable to find module DaVinciResolveScript from $PYTHONPATH - trying default locations") 25 | try: 26 | import imp 27 | bmd = imp.load_source('DaVinciResolveScript', expectedPath+"DaVinciResolveScript.py") 28 | except ImportError: 29 | # No fallbacks ... report error: 30 | print("Unable to find module DaVinciResolveScript - please ensure that the module DaVinciResolveScript is discoverable by python") 31 | print("For a default DaVinci Resolve installation, the module is expected to be located in: "+expectedPath) 32 | sys.exit() 33 | 34 | return bmd.scriptapp("Resolve") 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | db.sqlite3 61 | db.sqlite3-journal 62 | 63 | # Flask stuff: 64 | instance/ 65 | .webassets-cache 66 | 67 | # Scrapy stuff: 68 | .scrapy 69 | 70 | # Sphinx documentation 71 | docs/_build/ 72 | 73 | # PyBuilder 74 | target/ 75 | 76 | # Jupyter Notebook 77 | .ipynb_checkpoints 78 | 79 | # IPython 80 | profile_default/ 81 | ipython_config.py 82 | 83 | # pyenv 84 | .python-version 85 | 86 | # pipenv 87 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 88 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 89 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 90 | # install all needed dependencies. 91 | #Pipfile.lock 92 | 93 | # celery beat schedule file 94 | celerybeat-schedule 95 | 96 | # SageMath parsed files 97 | *.sage.py 98 | 99 | # Environments 100 | .env 101 | .venv 102 | env/ 103 | venv/ 104 | ENV/ 105 | env.bak/ 106 | venv.bak/ 107 | 108 | # Spyder project settings 109 | .spyderproject 110 | .spyproject 111 | 112 | # Rope project settings 113 | .ropeproject 114 | 115 | # mkdocs documentation 116 | /site 117 | 118 | # mypy 119 | .mypy_cache/ 120 | .dmypy.json 121 | dmypy.json 122 | 123 | # Pyre type checker 124 | .pyre/ 125 | -------------------------------------------------------------------------------- /DaVinciAccess/DaVinciAccess.py: -------------------------------------------------------------------------------- 1 | from DaVinciAccess.python_get_resolve import GetResolve 2 | 3 | 4 | resolve = GetResolve() 5 | try: 6 | projectManager = resolve.GetProjectManager() 7 | except: 8 | print('You must open DaVinci first') 9 | exit() 10 | mediaStorage = resolve.GetMediaStorage() 11 | projectManager.GotoRootFolder() 12 | 13 | def getProjects(): 14 | return projectManager.GetProjectListInCurrentFolder() 15 | 16 | def getSubfolderNames(Folders): 17 | Folders = Folders.GetSubFolderList() 18 | list = [] 19 | for Folder in Folders: 20 | list.append(Folder.GetName()) 21 | return list 22 | 23 | def getSubfolderByName(Folder, name): 24 | subFolders = Folder.GetSubFolderList() 25 | 26 | if subFolders != None: 27 | for subFolder in subFolders: 28 | if subFolder.GetName() == name: 29 | return subFolder 30 | 31 | return None 32 | 33 | 34 | 35 | class Project: 36 | def __init__(self, projectName): 37 | self.projectName = projectName 38 | self.project = projectManager.LoadProject(projectName) 39 | self.mediaPool = self.project.GetMediaPool() 40 | self.RootFolder = self.mediaPool.GetRootFolder() 41 | 42 | subFolders = getSubfolderNames(self.RootFolder) 43 | if 'Source' not in subFolders: 44 | self.mediaPool.AddSubFolder(self.RootFolder, 'Source') 45 | 46 | if 'Timelines' not in subFolders: 47 | self.mediaPool.AddSubFolder(self.RootFolder, 'Timelines') 48 | 49 | self.SourceFolder = getSubfolderByName(self.RootFolder, 'Source') 50 | self.TimelineFolder = getSubfolderByName(self.RootFolder, 'Timeline') 51 | 52 | def GetName(self): 53 | return self.project.GetName() 54 | 55 | def checkConnection(self): 56 | #Check if project has been changed in the meantime 57 | try: 58 | test = resolve.GetProjectManager() 59 | except: 60 | return False 61 | else: 62 | if self.projectName == projectManager.GetCurrentProject().GetName(): 63 | return True 64 | else: 65 | return False 66 | 67 | def getSourceFolder(self): 68 | return self.SourceFolder 69 | 70 | def CreateFolder(self, Parent, Name): 71 | return self.mediaPool.AddSubFolder(Parent, Name) 72 | 73 | def AddToMediaPool(self, Items): 74 | if self.checkConnection(): 75 | return mediaStorage.AddItemListToMediaPool(Items) 76 | -------------------------------------------------------------------------------- /dailiespipe.py: -------------------------------------------------------------------------------- 1 | import SilverstackAccess as Silverstack 2 | import json 3 | import time 4 | import DaVinciAccess as DaVinci 5 | 6 | instances = Silverstack.findSilverstackInstances() 7 | 8 | if instances == False: 9 | print("Error: No compatible Silverstack Instance has been found installed on your System") 10 | exit() 11 | 12 | print("""Welcome to DailiesPipe. This tool is developed to synchronize your Silverstack Library to DaVinci in realtime. 13 | Your current version of DailiesPipe is v0.1 and is tested with Silverstack (Lab) 7.3.1 and Resolve Studio 17.1. Check for updates under fabian-decker.de/dailiespipe. 14 | 15 | Not sure what to do? Check out the manual under fabian-decker.de/dailiespipe 16 | 17 | STEP 1: Choose your Silverstack instance to sync from""") 18 | 19 | 20 | 21 | i = 0 22 | while i < len(instances): 23 | print("[{}] - {}".format(i, instances[i])) 24 | i += 1 25 | 26 | print("Type the number of the silverstack instance that you want to sync from") 27 | UserInstance = input() 28 | 29 | 30 | instanceFound = False 31 | while instanceFound == False: 32 | try: 33 | ProjectList = Silverstack.getProjectList(int(UserInstance)) 34 | except: 35 | print("Error: choose a number from the list above. Go to website for help") 36 | UserInstance = input() 37 | else: 38 | instanceFound = True 39 | ProjectList = Silverstack.getProjectList(int(UserInstance)) 40 | 41 | 42 | i = 0 43 | while i < len(ProjectList): 44 | print("[{}] - {} - {}".format(i, ProjectList[i]['name'],ProjectList[i]['id'])) 45 | i += 1 46 | 47 | print("Type the number of the silverstack project that you want to sync from") 48 | UserProject = input() 49 | Project = False 50 | 51 | projectFound = False 52 | while projectFound == False: 53 | try: 54 | Project = Silverstack.Project(ProjectList[int(UserProject)]) 55 | except: 56 | print("Error: choose a number from the list above. Go to website for help") 57 | UserProject = input() 58 | else: 59 | projectFound = True 60 | 61 | 62 | print('STEP 2: Lets choose the DaVinci Project that you want to sync to.') 63 | 64 | davinciprojects = DaVinci.getProjects() 65 | i = 0 66 | while i < len(davinciprojects): 67 | print("[{}] - {}".format(i, davinciprojects[i])) 68 | i += 1 69 | 70 | UserDaVinciProject = input() 71 | projectFound = False 72 | 73 | 74 | while projectFound == False: 75 | try: 76 | DaVinciProject = DaVinci.Project(DaVinci.getProjects()[int(UserDaVinciProject)]) 77 | except Exception as e: 78 | print("Error: choose a number from the list above. Go to website for help") 79 | print(e) 80 | UserDaVinciProject = input() 81 | else: 82 | projectFound = True 83 | 84 | Project.fetchLibrary() 85 | Project.fetchFolderStructure() 86 | 87 | 88 | def SyncToDavinci(SilFolders, DaFolder, TopLevel): 89 | #peeeew 90 | #SilFolders refers to the Silverstack Folder tree, DaFolder refers to the Folder in Davinci will relate to the Silverstack Root Folder, given as an DaVinci Folder Object 91 | 92 | if TopLevel: 93 | Project.fetchLibrary() 94 | Project.fetchFolderStructure() 95 | 96 | if DaVinciProject.checkConnection(): 97 | if TopLevel: 98 | print('Syncing...') 99 | 100 | for Folder in SilFolders: 101 | if Folder["type"] == 'folder': 102 | #Create complement in Resolve 103 | DaSubFolder = DaVinci.getSubfolderByName(DaFolder, Folder['name']) 104 | if DaSubFolder == None: 105 | DaVinciProject.CreateFolder(DaFolder, Folder['name']) 106 | DaSubFolder = DaVinci.getSubfolderByName(DaFolder, Folder['name']) 107 | 108 | if len(Folder['subFolders']) > 0: 109 | SyncToDavinci(Folder['subFolders'], DaSubFolder, False) 110 | 111 | elif Folder["type"] == 'bin': 112 | DaSubFolder = DaVinci.getSubfolderByName(DaFolder, Folder['name']) 113 | if DaSubFolder == None: 114 | if Project.MediaBinOffloaded(Folder["PK"]): 115 | Bin = DaVinciProject.CreateFolder(DaFolder, Folder['name']) 116 | Items = Project.FetchBinItems(Folder) 117 | DaVinciProject.AddToMediaPool(Items) 118 | 119 | else: 120 | print('You have closed your associated DaVinci Project. Close this window, open the project in DaVinci and restart this script') 121 | exit() 122 | 123 | if TopLevel: 124 | time.sleep(5) 125 | SyncToDavinci(Project.FolderStructure, DaVinciProject.getSourceFolder(), True) 126 | 127 | 128 | SyncToDavinci(Project.FolderStructure, DaVinciProject.getSourceFolder(), True) 129 | 130 | 131 | 132 | 133 | f = open("test.json", "w") 134 | f.write(json.dumps(Project.FolderStructure)) 135 | f.close() 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DailiesPipeCLI 2 | This is a 3rd Party CLI Tool to realtime sync your Silverstack Library to a DaVinci Studio Project. Developed in my freetime. Use at own risk. 3 | This is version 0.1alpha and is tested with DaVinci 17.1.1 Build 9 and Silverstack and Silverstack Lab 7.3.1 4 | 5 | 6 | ## Quick Start Guide 7 | ### Prerequisites 8 | - This script only works with the paid version of DaVinci Resolve Studio. A version for the free Lite version might be possible in future 9 | - You need to enable external scripting in DaVinci. Go to 'DaVinci Resolve' -> 'Preferences' (Cmd + ,) -> 'General' and switch 'External scripting usage' to 'Local'. 10 | - Download the .zip file from the Download folder above and extract it to somewhere on your computer. 11 | - Find the file named 'Python' within the folder and right-click open it (only works with right click open). Close the window when it says 'Process completed' 12 | - Find the file named 'dailiespipe' within the folder and right-click open it (has to be right click opened only the first time you start it.) 13 | 14 | ### Start syncing 15 | - Open Silverstack and DaVinci and create the projects that you want to associate. Existing projects can be used of course. 16 | - Start the 'dailiespipe' executable 17 | - Every new version of Silverstack will create a new Silverstack instance. Silverstack Lab will create its own instances. In the first step of the script you have to choose the instance in which you have created your project in. 18 | - It will now list all the projects in your chosen instance. If you can't find your Silverstack project here, close the terminal window and reopen the 'dailiespipe' executable and try another instance. 19 | - In the last step the script will let you choose a DaVinci Project. Important: your project must be found in the root of the project manager 20 | - Your projects will now be synced as long as you have the terminal window running in the background or until you close DaVinci. 21 | 22 | ### Things you should now 23 | - This is a one way road. Your Silverstack project will not be manipulated or edited in any way. It will be just read out. 24 | - Should your script crash, usually indicated with a Traceback, please take a screenshot and mail it to me or post it as an issue on Github describing the circumstances 25 | - Only new folders will be imported into DaVinci. If your folder already exists in DaVinci but is empty it will not be touched 26 | 27 | ## Roadmap / Future features 28 | - History (fast restart of former project links) 29 | - Restart from beginning command 30 | - Create Timeline for every reel 31 | - Auto sync audio at input 32 | - Auto apply Lut / Node Tree to clips 33 | - Auto generate render job with specified render preset 34 | 35 | ## DaVinciAccess Documentation: 36 | 37 | ### getProjects() 38 | Returns a list of all projects in the root folder of the project manager. 39 | 40 | ### getSubfolderNames(Folder) 41 | Returns a list of all subfolder names of a given folder as an array of strings 42 | 43 | ### getSubfolderByName(Folder, Name) 44 | Checks wether a a subfolder with a given name exists within the given folder object. Returns folder as object or None. 45 | 46 | 47 | ## SilverstacAccess Documentation: 48 | ### findSilverstackInstance() 49 | To find all instances of Silverstack, SilverstackLab currently and once installed. Note: This tool is not compatible to all versions of Silverstack. Returns an array with the names of all instances, e.g.: ['Silverstack6', 'Silverstack7', 'SilverstackLab7'] 50 | Currently supported: Silverstack7 51 | 52 | ### getProjectList(instanceKey) 53 | Finds all projects from a specific instance and retrieves first informations about the projects, e.g. their names and creation date. Returns array of dicts, with each dict beeing a project and its information. 54 | 55 | ### Project Class (project) 56 | You can turn a project dict returned from getProjectList() into a Project class. With it's initialization it will store all basic information, but no Clips and Folders from your Silverstack project 57 | 58 | #### fetchVolumes() 59 | Retrieves all Volumes used in your Silverstack project 60 | 61 | #### fetchFolderStructure() 62 | Retrieves all folders, media bins, etc. and their child elements from your Silverstack project tree. Returns as an array of dicts. 63 | 64 | #### fetchLibrary() 65 | Retrieves all your clips from your media library, containing all their meta data and files. All clips have a unique Id called Owner. This is determined by Pomfort developers. Returns array of dicts 66 | 67 | #### getClipFromLibrary(Owner) 68 | Retrieves one clip with a specific Owner ID from your media library, containing all it's meta data and files. Returns dict. 69 | 70 | #### getCurrentBestClip(Owner) 71 | Takes a specific Owner ID, looks wether its available in the library and gives one file which is best for usage. Takes into account the Playback Priority set in Silverstack software and wether the file is online. 72 | Returns the file's path as a string or False, if no file is online. 73 | 74 | #### getJobs() 75 | Returns all the job history of Silverstack as an array of objects with name, progress and state. 76 | 77 | #### FetchBinItems(Folder) 78 | Fetches all the items of a bin and returns only its path to its current best clip. 79 | 80 | 81 | 82 | ## Changelog 83 | ### v0.1 - 2021/01/03 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /SilverstackAccess/SilverstackAccess.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import plistlib 4 | import sqlite3 5 | import operator 6 | 7 | 8 | def findSilverstackInstances(): 9 | if sys.platform != "darwin": 10 | return "Error: You are not working on an compatible OS. Only MacOS 10.15.7 or higher is supported" 11 | else: 12 | pomfortFolder = os.path.expanduser( 13 | '~/Library/Application Support/Pomfort/') 14 | try: 15 | subFolders = os.listdir(pomfortFolder) 16 | except FileNotFoundError: 17 | return "Error: Silverstack doesn't appear to be installed on your system" 18 | 19 | instances = [] 20 | 21 | for Folder in subFolders: 22 | if Folder.startswith('Silverstack'): 23 | instances.append(Folder) 24 | 25 | if len(instances) == 0: 26 | return False 27 | else: 28 | return instances 29 | 30 | 31 | def getProjectList(instanceKey): 32 | instance = findSilverstackInstances()[instanceKey] 33 | pathToInstance = os.path.expanduser( 34 | '~/Library/Application Support/Pomfort/' + instance + '/') 35 | projectFolders = os.listdir(pathToInstance) 36 | list = [] 37 | 38 | for project in projectFolders: 39 | if project.startswith('Project-'): 40 | path = pathToInstance + project 41 | file = open(path + '/Project.plist', 'rb') 42 | plist = plistlib.load(file) 43 | file.close() 44 | list.append({ 45 | 'id': project.rsplit('-')[1], 46 | 'name': plist['name'], 47 | 'instance': instance, 48 | 'creationDate': plist['creationDate'] 49 | }) 50 | return list 51 | 52 | 53 | class Project: 54 | def __init__(self, project): 55 | self.projectName = project['name'] 56 | self.id = project['id'] 57 | self.instance = project['instance'] 58 | self.pathToInstance = os.path.expanduser( 59 | '~/Library/Application Support/Pomfort/' + self.instance + '/') 60 | self.pathToProject = os.path.expanduser( 61 | '~/Library/Application Support/Pomfort/' + self.instance + '/Project-' + self.id) 62 | self.Volumes = None 63 | self.Library = None 64 | self.FolderStructure = None 65 | 66 | def fetchVolumes(self): 67 | # GET ALL VOLUMES on open project. Seperate function which will be called less often then the fetchVolume function 68 | try: 69 | conn = sqlite3.connect(self.pathToProject + '/Silverstack.psdb') 70 | c = conn.cursor() 71 | except: 72 | return 'Error: Could not connect to Project Database.' 73 | 74 | c.execute( 75 | 'SELECT * FROM ZVOLUME ORDER BY ZPLAYBACKPRIORITY DESC, ZLABEL ASC') 76 | results = c.fetchall() 77 | conn.close() 78 | 79 | list = [] 80 | for result in results: 81 | list.append({ 82 | 'label': result[14], 83 | 'priority': result[8], 84 | 'freeCapacity': result[5], 85 | 'totalCapacity': result[9], 86 | 'mountPath': result[15] 87 | }) 88 | self.Volumes = list 89 | return True 90 | 91 | def getVolume(self, PK): 92 | try: 93 | conn = sqlite3.connect(self.pathToProject + '/Silverstack.psdb') 94 | c = conn.cursor() 95 | except: 96 | return 'Error: Could not connect to Project Database.' 97 | 98 | c.execute( 99 | 'SELECT ZPLAYBACKPRIORITY, ZLABEL, ZMOUNTPATH FROM ZVOLUME WHERE Z_PK=?', (PK,)) 100 | result = c.fetchone() 101 | conn.close() 102 | 103 | return { 104 | 'label': result[1], 105 | 'mountPath': result[2], 106 | 'playbackPriority': result[0] 107 | } 108 | 109 | def getCurrentBestClip(self, Owner): 110 | # Gets the file from the highest priority Volume (sorts alphabetically if identical prority), checks wether online (if not goes to next Volume) and returns its complete Path 111 | # Index is Index of Clip in self.Library 112 | 113 | clip = self.getClipFromLibrary(Owner) 114 | files = clip['files'] 115 | 116 | files.sort(key=operator.itemgetter('volumePlaybackPriority')) 117 | files.reverse() 118 | val = "" 119 | for file in files: 120 | fullPath = file['volumeMountPath'] + file['relativePath'] 121 | if os.path.isfile(fullPath): 122 | val = fullPath 123 | break 124 | else: 125 | val = False 126 | 127 | return val 128 | 129 | def fetchFiles(self, Owner): 130 | try: 131 | conn = sqlite3.connect(self.pathToProject + '/Silverstack.psdb') 132 | c = conn.cursor() 133 | except: 134 | return 'Error: Could not connect to Project Database.' 135 | c.execute('SELECT Z_PK, ZFILESIZE, ZOWNER, ZVOLUME, ZFILETYPE, ZRELATIVEPATH FROM ZFILERESOURCE WHERE ZOWNER = ?', (str(Owner),)) 136 | files = c.fetchall() 137 | list = [] 138 | 139 | for file in files: 140 | Volume = self.getVolume(file[3]) 141 | list.append({ 142 | 'relativePath': file[5], 143 | 'fileSize': file[1], 144 | 'fileType': file[4], 145 | 'volumeLabel': Volume['label'], 146 | 'volumeMountPath': Volume['mountPath'], 147 | 'volumePlaybackPriority': Volume['playbackPriority'] 148 | }) 149 | 150 | return list 151 | 152 | def getClipFromLibrary(self, Owner): 153 | # get specific clip from Library by Owner 154 | if self.Library == None: 155 | return False 156 | 157 | for clip in self.Library: 158 | if clip['PK'] == Owner: 159 | return clip 160 | else: 161 | return False 162 | 163 | def MediaBinOffloaded(self, PK): 164 | # Checks if a bin's offload job is completed 165 | try: 166 | conn = sqlite3.connect(self.pathToProject + '/Silverstack.psdb') 167 | c = conn.cursor() 168 | except: 169 | return 'Error: Could not connect to Project Database.' 170 | 171 | c.execute('SELECT ZMEDIABININFO FROM ZFOLDER WHERE Z_PK = ?', (PK, )) 172 | mediabininfo = c.fetchone()[0] 173 | 174 | c.execute( 175 | 'SELECT ZOFFLOADJOB FROM ZMEDIABININFO WHERE Z_PK = ?', (mediabininfo, )) 176 | offloadjob = c.fetchone()[0] 177 | 178 | c.execute( 179 | 'SELECT ZSTATEIDENTIFIER FROM ZACTIVITYJOB WHERE Z_PK = ?', (offloadjob, )) 180 | try: 181 | identifier = c.fetchone()[0] 182 | except: 183 | return False 184 | else: 185 | if identifier == "com.pomfort.workState.finishedSuccessfully": 186 | return True 187 | else: 188 | return False 189 | 190 | print("OFFLOADHJOB:", offloadjob) 191 | return False 192 | 193 | def fetchLibrary(self): 194 | try: 195 | conn = sqlite3.connect(self.pathToProject + '/Silverstack.psdb') 196 | c = conn.cursor() 197 | except: 198 | return 'Error: Could not connect to Project Database.' 199 | 200 | c.execute('SELECT Z_PK,Z_ENT, ZMETADATA, ZUSERINFO, ZRECORDERINFO, ZTIMECODERANGE, ZPIXELHEIGHT, ZPIXELWIDTH, ZWHITEPOINTKELVIN, ZCREATIONDATE, ZSHOOTINGDATE, ZASA, ZDURATION, ZFPS, ZCODEC, ZNAME, ZSCENE, ZSHOT, ZTAKE, ZCAMERA, ZCOLORSPACE, ZIDENTIFIER FROM ZRESOURCEOWNER') 201 | Owners = c.fetchall() 202 | c.close() 203 | 204 | list = [] 205 | 206 | for Owner in Owners: 207 | list.append({ 208 | 'name': Owner[15], 209 | 'PK': Owner[0], 210 | 'ENT': Owner[1], 211 | 'scene': Owner[16], 212 | 'shot': Owner[17], 213 | 'take': Owner[18], 214 | 'camera': Owner[19], 215 | 'files': self.fetchFiles(Owner[0]) 216 | }) 217 | 218 | self.Library = list 219 | 220 | def getJobs(self): 221 | try: 222 | conn = sqlite3.connect(self.pathToProject + '/Silverstack.psdb') 223 | c = conn.cursor() 224 | except: 225 | return 'Error: Could not connect to Project Database.' 226 | 227 | c.execute('SELECT Z_PK,Z_ENT, Z_OPT, ZSOURCEMEDIABININFO, ZSOURCEPATH1, ZPROGRESS, ZSTARTDATE, ZTIMEELAPSED, ZJOBQUEUENAME, ZNAME, ZSTATEIDENTIFIER FROM ZACTIVITYJOB') 228 | Jobs = c.fetchall() 229 | c.close() 230 | 231 | list = [] 232 | 233 | for Job in Jobs: 234 | list.append({ 235 | "name": Job[9], 236 | "progress": Job[5], 237 | "startdarte": Job[6], 238 | "state": Job[10], 239 | "jobquename": Job[8], 240 | }) 241 | 242 | return list 243 | 244 | def fetchFolderStructure(self): 245 | try: 246 | conn = sqlite3.connect(self.pathToProject + '/Silverstack.psdb') 247 | c = conn.cursor() 248 | except: 249 | return 'Error: Could not connect to Project Database.' 250 | 251 | c.execute('SELECT Z_PK FROM ZFOLDER WHERE ZFOLDERTYPE = ?', (20014,)) 252 | RootPK = c.fetchone()[0] 253 | 254 | c.execute( 255 | 'SELECT Z_PK, ZFOLDERTYPE, ZSORTINDEX, ZMEDIABININFO, ZPARENT, ZNAME FROM ZFOLDER ORDER BY ZSORTINDEX') 256 | Folders = c.fetchall() 257 | 258 | def findFolderType(typeNumber): 259 | if typeNumber == 20004: 260 | return "folder" 261 | elif typeNumber == 20005: 262 | return "bin" 263 | else: 264 | return "Unknown" 265 | 266 | def findItems(folderPK): 267 | c.execute( 268 | 'SELECT ZOFFLOADJOB FROM ZMEDIABININFO WHERE ZFOLDER = ?', (folderPK,)) 269 | offloadjob = c.fetchone()[0] 270 | 271 | c.execute( 272 | 'SELECT ZSOURCERESOURCE FROM ZACTIVITYTASK WHERE ZJOB = ?', (offloadjob,)) 273 | tasks = c.fetchall() 274 | 275 | for task in tasks: 276 | c.execute( 277 | 'SELECT ZOWNER FROM ZFILERESOURCE WHERE Z_PK = ?', (task[0],)) 278 | Owner = c.fetchone()[0] 279 | Path = self.getCurrentBestClip(Owner) 280 | 281 | return'Cool' 282 | 283 | def findSubFolder(parentPK): 284 | list = [] 285 | for Folder in Folders: 286 | 287 | if Folder[4] == parentPK: 288 | # Adding Subfolder to tree 289 | 290 | subfolder = { 291 | 'PK': Folder[0], 292 | 'name': Folder[5], 293 | 'type': findFolderType(Folder[1]), 294 | 'sortIndex': Folder[2], 295 | 'parent': Folder[4], 296 | 'subFolders': None 297 | } 298 | 299 | if subfolder['type'] == 'folder': 300 | subfolder['subFolders'] = findSubFolder(Folder[0]) 301 | 302 | list.append(subfolder) 303 | 304 | list.sort(key=operator.itemgetter('sortIndex')) 305 | return list 306 | 307 | Tree = findSubFolder(RootPK) 308 | self.FolderStructure = Tree 309 | 310 | c.close() 311 | 312 | return True 313 | 314 | def FetchBinItems(self,Folder): 315 | try: 316 | conn = sqlite3.connect(self.pathToProject + '/Silverstack.psdb') 317 | c = conn.cursor() 318 | except: 319 | return 'Error: Could not connect to Project Database.' 320 | 321 | c.execute( 322 | 'SELECT ZOFFLOADJOB FROM ZMEDIABININFO WHERE ZFOLDER = ?', (Folder['PK'],)) 323 | offloadjob = c.fetchone()[0] 324 | 325 | c.execute( 326 | 'SELECT ZSOURCERESOURCE FROM ZACTIVITYTASK WHERE ZJOB = ?', (offloadjob,)) 327 | tasks = c.fetchall() 328 | 329 | items = [] 330 | 331 | for task in tasks: 332 | c.execute('SELECT ZOWNER FROM ZFILERESOURCE WHERE Z_PK = ?', (task[0],)) 333 | Owner = c.fetchone()[0] 334 | Path = self.getCurrentBestClip(Owner) 335 | items.append(Path) 336 | 337 | return items --------------------------------------------------------------------------------