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

Initial commit.

Very simple GUI to show PKL name, whether package is signed or not and ability to verify the package.
Also included is the debian packaging infrastructure.
parents
No related branches found
No related tags found
No related merge requests found
~*
debian/dcpman
debian/dcpman.debhelper.log
debian/dcpman.links
debian/dcpman.postinst.debhelper
debian/dcpman.prerm.debhelper
debian/dcpman.substvars
dcpman/__pycache__
dcpman/ui.py
This diff is collapsed.
[Desktop Entry]
Name=DCPman
GenericName=DCP verifier
Comment=Information about Digital Cinema Packages
Type=Application
Exec=dcpman
Icon=kmplayer
Categories=Qt;AudioVideo
Terminal=false
\ No newline at end of file
dcpman.ui 0 → 100644
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>518</width>
<height>153</height>
</rect>
</property>
<property name="windowTitle">
<string>DCP verifier</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>11</x>
<y>11</y>
<width>491</width>
<height>131</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="nameLabel">
<property name="minimumSize">
<size>
<width>45</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="nameLine">
<property name="frame">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="signedLabel">
<property name="minimumSize">
<size>
<width>45</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Signed</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="signedLine">
<property name="frame">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="verifyLabel">
<property name="minimumSize">
<size>
<width>45</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Verify</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="verifyLine">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="verifyButton">
<property name="text">
<string>verify</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</widget>
<resources/>
<connections/>
</ui>
#!/usr/bin/env python3
#!/usr/bin/env python3
from hashlib import sha1
from base64 import b64encode
from os import stat, listdir
from xml.dom.minidom import parse
import sys
class Asset(object):
'''A simple asset that's part of whole DCP package.'''
def verifySize(self):
'''verify that the size of the file is correct, if present.'''
try:
return stat(self.fullpath).st_size == self.size
except AttributeError:
return True
def verifyHash(self):
'''verify that the hash is correct, if present.'''
if hasattr(self, 'hash') == False:
return True
#from os import stat
#try:
#buffersize = stat(self.fullpath).st_blksize
#except:
#buffersize = 2**16
buffersize = 2 ** 16
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())
def __init__(self, rootpath, filename, id=None, hash=None, size=None, packinglist=False):
'''Initialize an asset, has to have a filename and path.'''
self.rootpath = rootpath
if filename[0:8] == 'file:///':
filename = filename[8:]
self.filename = filename
self.fullpath = rootpath + '/' + filename
if id != None:
self.id = id
if hash != None:
self.hash = hash
if size != None:
self.size = size
self.packinglist = packinglist
class DCP:
'''A complete DCP package.'''
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:
return False
except BaseException as e:
raise RuntimeError ('Failed size comparisement for ' +
asset.filename) from e
#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)
for asset in self.assets:
try:
if asset.verifyHash() == False:
return False
except BaseException as e:
raise RuntimeError ('Failed hash calculation for ' +
asset.filename) from e
return True
def __init__(self, directory):
'''Parses the DCP in the directory specified.'''
self.root = directory
if 'ASSETMAP.xml' in listdir(directory):
filename = 'ASSETMAP.xml'
elif 'ASSETMAP' in listdir(directory):
filename = 'ASSETMAP'
else:
raise RuntimeError ('Couldn\'t find assetmap file')
self.assets = [Asset(directory, filename)]
assetmap = self.assets[0].fullpath
if 'VOLINDEX.xml' in listdir(directory):
filename = 'VOLINDEX.xml'
elif 'VOLINDEX' in listdir(directory):
filename = 'VOLINDEX'
#else:
#raise RuntimeError ('Couldn\'t find volindex file')
self.assets.append(Asset(directory, filename))
try:
assetmap = parse(assetmap).getElementsByTagName('Asset')
self.assets.append(Asset(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
self.assets.append(Asset(directory, filename, id=id, packinglist=packinglist))
except BaseException as e:
raise RuntimeError ('Failed to parse assetmap file') from e
try:
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
try:
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
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
except BaseException as e:
raise RuntimeError ('Failed to parse packinglist file') from e
if __name__ == '__main__':
if (len(sys.argv) == 2):
dcp = DCP(sys.argv[1])
else:
dcp = DCP('./')
try:
print ('Name:', dcp.name)
except:
pass
if dcp.signed:
print ('DCP is signed')
else:
print ('DCP is unsigned')
if (dcp.verify()):
print ('Verification succeeded.')
else:
print ('Verification failed.')
exit(0)
#!/usr/bin/env python3
from PyQt4 import QtGui
from PyQt4 import QtCore
from dcpman.dcp import DCP
from dcpman import ui #autogenerated file from Qt Designer to setup the window
import sys
class verifyThread(QtCore.QThread):
'''A seperate thread to verify the DCP (IO intensive).
Verify the assets of DCP (reads all of the files to
calculate the hash) in a seperate thread from the GUI to keep the GUI
responsive. At the end update the verifyLine test to reflect the result.'''
def run(self):
'''The action the thread takes.'''
result = dcp.verify()
try:
result = dcp.verify()
if result:
window.verifyLine.setText('OK')
else:
window.verifyLine.setText('Corrupted!')
except BaseException as exception:
window.verifyLine.setText(str(exception))
def verify_in_thread():
'''Verifys the DCP in a differenet thread.
Firstly disable the button and change the verifyLine to reflect that the
verification is running (in the same thread to update the window immediately
then calls the seperate thread to verify the assets of DCP.'''
window.verifyLine.setText('Verifying, please wait...')
window.verifyButton.setEnabled(False)
thread.start()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
icon = QtGui.QIcon('/usr/share/icons/oxygen/16x16/apps/kmplayer.png')
app.setWindowIcon(icon)
directory = QtGui.QFileDialog.getExistingDirectory( \
caption='Please select the location of the DCP')
mainwindow = QtGui.QMainWindow()
window = ui.Ui_MainWindow()
window.setupUi(mainwindow)
thread = verifyThread()
try:
dcp = DCP(directory)
try:
window.nameLine.setText(dcp.name)
except AttributeError:
window.nameLine.setText(directory.split('/')[-1])
if dcp.signed:
window.signedLine.setText('Signed')
else:
window.signedLine.setText('Not signed')
window.verifyLine.setText('Click button to start verification')
window.verifyButton.clicked.connect(verify_in_thread)
except BaseException as exception:
window.nameLine.setText(str(exception))
window.verifyButton.setEnabled(False)
mainwindow.show()
mainwindow.activateWindow()
exit(app.exec_())
dcpman (0.1) UNRELEASED; urgency=medium
* Initial release.
-- Nimrod Adar <nimrod@shore.co.il> Sun, 29 Jun 2014 16:19:53 +0300
9
Source: dcpman
Section: video
Priority: optional
Build-Depends: dh-python, python3 (>= 3.2), pyqt4-dev-tools
Maintainer: Nimrod Adar <nimrod@shore.co.il>
X-Python3-Version: >= 3.2
Standards-Version: 3.9.5.0
Package: dcpman
Depends: python3 (>= 3.2), python3-pyqt4 | python3-pyqt5, oxygen-icon-theme
Architecture: all
Description: Management tool for Digital Cinema Packages.
A tool to manage DCPs (Digital Cinema Packages), although currently it only
lists the name of the DCP, whether it's signed or not and verifies the
integrity of the package.
Files: *
Copyright: 2014 Nimrod Adar
License: AGPL-3+
dcpman_0.1_all.deb video optional
dcpman/* usr/lib/python3/dist-packages/dcpman
dcpman.desktop usr/share/applications
#!/usr/bin/make -f
%:
pyuic4 dcpman.ui > dcpman/ui.py
dh $@ --with python3
3.0 (native)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment