Skip to content
Snippets Groups Projects
Commit b31f724e authored by nimrod's avatar nimrod
Browse files

Work on future feature (full ingest of DCP from removable media).

parent 234ffe5b
No related branches found
No related tags found
No related merge requests found
#!/usr/bin/env python3
from hashlib import sha1
from base64 import b64encode
from os import stat, listdir
from os import stat, listdir, mkdir, statvfs
from os.path import isdir, basename
from shutil import copyfile
from xml.dom.minidom import parse
import sys
import vlc
class Asset(object):
class Asset (object):
'''A simple asset that's part of whole DCP package.'''
def verifySize(self):
def verifySize (self):
'''verify that the size of the file is correct, if present.'''
try:
return stat(self.fullpath).st_size == self.size
return stat (self.fullpath).st_size == self.size
except AttributeError:
return True
def verifyHash(self):
def verifyHash (self):
'''verify that the hash is correct, if present.'''
if hasattr(self, 'hash') == False:
if hasattr (self, 'hash') == False:
return True
#from os import stat
#try:
......@@ -25,13 +27,38 @@ class Asset(object):
#except:
#buffersize = 2**16
buffersize = 2 ** 16
fh = open(self.fullpath, 'rb')
buffer = fh.read(buffersize)
hash = sha1()
fh = open (self.fullpath, 'rb')
buffer = fh.read (buffersize)
hash = sha1 ()
while buffer:
hash.update(buffer)
buffer = fh.read(buffersize)
return(self.hash == b64encode (hash.digest()).decode())
hash.update (buffer)
buffer = fh.read (buffersize)
fh.close ()
return (self.hash == b64encode (hash.digest ()).decode ())
def copyAndVerify (self, destination):
'''Copies the file to the destination directory and verifies the hash
during.'''
newfilepath = destination + '/' + self.filename
if hasattr (self, 'hash') == False:
copyfile (self.fullpath, newfilepath)
self.fullpath = newfilepath
self.rootpath = destination
return True
buffersize = 2 ** 16
in_fh = open (self.fullpath, 'rb')
out_fh = open (newfullpath, 'wb')
buffer = in_fh.read (buffersize)
hash = sha1 ()
while buffer:
hash.update (buffer)
out_fh.write (buffer)
buffer = in_fh.read (buffersize)
in_fh.close ()
out_fh.close ()
self.rootpath = destination
self.fullpath = newfullpath
return (self.hash == b64encode (hash.digest ()).decode ())
def add_duration (self):
if hasattr (self, 'type') and self.type.find ('mxf') > -1:
......@@ -40,7 +67,8 @@ class Asset(object):
media.parse ()
self.duration = media.get_duration ()
def __init__(self, rootpath, filename, id=None, hash=None, size=None, packinglist=False, type=None):
def __init__(self, rootpath, filename, id=None, hash=None, size=None,\
packinglist=False, type=None):
'''Initialize an asset, has to have a filename and path.'''
self.rootpath = rootpath
if filename[0:8] == 'file:///':
......@@ -60,15 +88,15 @@ class Asset(object):
class DCP:
'''A complete DCP package.'''
def verify(self):
def verify (self):
'''Verifies that all assets are of the correct size and have
the correct hash.'''
for asset in self.assets:
try:
if asset.verifySize() == False:
if asset.verifySize () == False:
return False
except BaseException as e:
raise RuntimeError ('Failed size comparisement for ' +
raise RuntimeError ('Failed size comparisement for ' +\
asset.filename) from e
#Sort the assets by size before calculating hashes, for performance.
def sortkey(x):
......@@ -76,71 +104,75 @@ class DCP:
return x.size
except AttributeError:
return 0
self.assets.sort(key=sortkey)
self.assets.sort (key=sortkey)
for asset in self.assets:
try:
if asset.verifyHash() == False:
if asset.verifyHash () == False:
return False
except BaseException as e:
raise RuntimeError ('Failed hash calculation for ' +
raise RuntimeError ('Failed hash calculation for ' +\
asset.filename) from e
return True
def _parse_assetmap (self):
'''Adds the asset map file to the list of assets and parses it.'''
if 'ASSETMAP.xml' in listdir(self.directory):
if 'ASSETMAP.xml' in listdir (self.directory):
filename = 'ASSETMAP.xml'
elif 'ASSETMAP' in listdir(self.directory):
elif 'ASSETMAP' in listdir (self.directory):
filename = 'ASSETMAP'
else:
raise RuntimeError ('Couldn\'t find assetmap file')
self.assets = [Asset(self.directory, filename, type='text/xml')]
self.assets = [Asset (self.directory, filename, type='text/xml')]
assetmap = self.assets[0].fullpath
try:
assetmap = parse(assetmap).getElementsByTagName('Asset')
self.assets.append(Asset(self.directory, filename))
assetmap = parse (assetmap).getElementsByTagName ('Asset')
self.assets.append (Asset (self.directory, filename))
for element in assetmap:
id = element.getElementsByTagName('Id')[0].firstChild.data
id = id.split(':')[-1]
filename = element.getElementsByTagName('Path')[0].firstChild.data
packinglist = len(element.getElementsByTagName('PackingList')) > 0
id = element.getElementsByTagName ('Id')[0].firstChild.data
id = id.split (':')[-1]
filename = element.getElementsByTagName ('Path')[0].firstChild.data
packinglist = len (element.getElementsByTagName ('PackingList')) > 0
if packinglist:
self.assets.append(Asset(self.directory, filename, id=id, packinglist=packinglist, type='text/xml'))
self.assets.append (Asset (self.directory, filename,\
id=id, packinglist=packinglist, type='text/xml'))
else:
self.assets.append(Asset(self.directory, filename, id=id, packinglist=packinglist))
self.assets.append (Asset (self.directory, filename,\
id=id, packinglist=packinglist))
except BaseException as e:
raise RuntimeError ('Failed to parse assetmap file') from e
def _add_volindex (self):
'''Adds the volume index file to the list of assets.'''
if 'VOLINDEX.xml' in listdir(self.directory):
if 'VOLINDEX.xml' in listdir (self.directory):
filename = 'VOLINDEX.xml'
elif 'VOLINDEX' in listdir(self.directory):
elif 'VOLINDEX' in listdir (self.directory):
filename = 'VOLINDEX'
else:
#raise RuntimeError ('Couldn\'t find volindex file')
return
self.assets.append(Asset(self.directory, filename, type='text/xml'))
self.assets.append (Asset (self.directory, filename, type='text/xml'))
def _parse_packinglist (self):
'''Parses the packing list.'''
try:
pkls = (parse(x.fullpath) for x in self.assets if x.packinglist)
pkls = (parse (x.fullpath) for x in self.assets if x.packinglist)
for pkl in pkls:
if hasattr(self, 'signed') == False:
self.signed = len(pkl.getElementsByTagName('Signer')) > 0
if hasattr (self, 'signed') == False:
self.signed = len (pkl.getElementsByTagName ('Signer')) > 0
try:
if hasattr(self, 'name') == False:
self.name = pkl.getElementsByTagName('AnnotationText')[0].firstChild.data.strip()
if hasattr (self, 'name') == False:
self.name = pkl.getElementsByTagName\
('AnnotationText')[0].firstChild.data.strip()
except:
pass
for element in pkl.getElementsByTagName('Asset'):
id = element.getElementsByTagName('Id')[0].firstChild.data
id = id.split(':')[-1]
hash = element.getElementsByTagName('Hash')[0].firstChild.data
type = element.getElementsByTagName('Type')[0].firstChild.data
size = int(element.getElementsByTagName('Size')[0].firstChild.data)
asset = [x for x in self.assets if hasattr(x, 'id') and x.id == id][0]
for element in pkl.getElementsByTagName ('Asset'):
id = element.getElementsByTagName ('Id')[0].firstChild.data
id = id.split (':')[-1]
hash = element.getElementsByTagName ('Hash')[0].firstChild.data
type = element.getElementsByTagName ('Type')[0].firstChild.data
size = int(element.getElementsByTagName ('Size')[0].firstChild.data)
asset = [x for x in self.assets if hasattr (x, 'id') and\
x.id == id][0]
asset.hash = hash
asset.size = size
asset.type = type
......@@ -148,23 +180,57 @@ class DCP:
except BaseException as e:
raise RuntimeError ('Failed to parse packinglist file') from e
def __init__(self, directory):
def __init__ (self, directory):
'''Parses the DCP in the directory specified.'''
self.directory = directory
self._parse_assetmap ()
self._add_volindex ()
self._parse_packinglist ()
try:
self.duration = max ([x.duration for x in self.assets if hasattr (x, 'duration')])
self.duration = max ([x.duration for x in self.assets if hasattr\
(x, 'duration')])
except:
self.duration = 'Unknown'
def copyAndVerify (self, destination):
'''Copies the DCP to the destination directory and verifies during.'''
for asset in self.assets:
totalsize = stat (asset.fullpath).st_size
try:
if asset.verifySize () == False:
return False
except BaseException as e:
raise RuntimeError ('Failed size comparisement for ' +\
asset.filename) from e
freespace = statvfs (destination).f_bavail * statvfs\
(destination).f_bsize
if freespace < totalsize:
return False
#Sort the assets by size before calculating hashes, for performance.
def sortkey(x):
try:
return x.size
except AttributeError:
return 0
self.assets.sort (key=sortkey)
newdirectory = destination + '/' + basename (self.directory)
mkdir (newdirectory)
for asset in self.assets:
try:
if asset.copyAndVerify (newdirectory) == False:
return False
except BaseException as e:
raise RuntimeError ('Failed hash calculation for ' +\
asset.filename) from e
self.directory = newdirectory
return True
if __name__ == '__main__':
if (len(sys.argv) == 2):
dcp = DCP(sys.argv[1])
if len (sys.argv) == 2:
dcp = DCP (sys.argv[1])
else:
dcp = DCP('./')
dcp = DCP ('./')
try:
print ('Name:', dcp.name)
except:
......@@ -174,8 +240,8 @@ if __name__ == '__main__':
else:
print ('DCP is unsigned')
print ('Duration:', dcp.duration)
if (dcp.verify()):
if dcp.verify ():
print ('Verification succeeded.')
else:
print ('Verification failed.')
exit(0)
exit (0)
......@@ -6,6 +6,8 @@ from PyQt4 import QtCore
from dcp import DCP
import ui
import sys
import syslog
import time
class verifyThread(QtCore.QThread):
'''A seperate thread to verify the DCP (IO intensive).
......@@ -20,8 +22,12 @@ class verifyThread(QtCore.QThread):
result = dcp.verify()
if result:
window.verifyLine.setText('OK')
syslog.syslog (syslog.LOG_INFO, time.ctime () + dcp.name +\
' verification succeeded.')
else:
window.verifyLine.setText('Corrupted!')
syslog.syslog (syslog.LOG_INFO, time.ctime () + dcp.name +\
' verification failed.')
except BaseException as exception:
window.verifyLine.setText(str(exception))
......@@ -38,6 +44,7 @@ def verify_in_thread():
if __name__ == '__main__':
syslog.openlog (ident = 'dcpman', facility = syslog.LOG_USER)
app = QtGui.QApplication(sys.argv)
icon = QtGui.QIcon('/usr/share/icons/oxygen/16x16/apps/kmplayer.png')
app.setWindowIcon(icon)
......@@ -51,8 +58,18 @@ if __name__ == '__main__':
dcp = DCP(directory)
try:
window.nameLine.setText(dcp.name)
syslog.syslog (syslog.INFO, time.ctime () + dcp.name +\
'parsed.')
except AttributeError:
window.nameLine.setText(directory.split('/')[-1])
if dcp.signed:
syslog.syslog (syslog.INFO, time.ctime () + dcp.name +\
' is signed.')
else:
syslog.syslog (syslog.INFO, time.ctime () + dcp.name +\
' is not sigend.')
syslog.syslog (syslog.INFO, time.ctime () + dcp.name +\
' duration is ' + dcp.duration)
if dcp.signed and dcp.duration == 0:
window.encryptedLine.setText('Most likely')
elif dcp.signed or dcp.duration == 0:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment