Add dcpomatic_combine tool (#1245).
authorCarl Hetherington <cth@carlh.net>
Thu, 10 Sep 2020 23:07:46 +0000 (01:07 +0200)
committerCarl Hetherington <cth@carlh.net>
Sat, 19 Sep 2020 23:30:41 +0000 (01:30 +0200)
37 files changed:
cscript
graphics/linux/128/dcpomatic2_combiner.png [new file with mode: 0644]
graphics/linux/16/dcpomatic2_combiner.png [new file with mode: 0644]
graphics/linux/22/dcpomatic2_combiner.png [new file with mode: 0644]
graphics/linux/256/dcpomatic2_combiner.png [new file with mode: 0644]
graphics/linux/32/dcpomatic2_combiner.png [new file with mode: 0644]
graphics/linux/48/dcpomatic2_combiner.png [new file with mode: 0644]
graphics/linux/512/dcpomatic2_combiner.png [new file with mode: 0644]
graphics/linux/64/dcpomatic2_combiner.png [new file with mode: 0644]
graphics/osx/dcpomatic2_combiner.icns [new file with mode: 0644]
graphics/osx/dcpomatic2_combiner.iconset/icon_128x128.png [new file with mode: 0644]
graphics/osx/dcpomatic2_combiner.iconset/icon_128x128@2x.png [new file with mode: 0644]
graphics/osx/dcpomatic2_combiner.iconset/icon_16x16.png [new file with mode: 0644]
graphics/osx/dcpomatic2_combiner.iconset/icon_16x16@2x.png [new file with mode: 0644]
graphics/osx/dcpomatic2_combiner.iconset/icon_256x256.png [new file with mode: 0644]
graphics/osx/dcpomatic2_combiner.iconset/icon_256x256@2x.png [new file with mode: 0644]
graphics/osx/dcpomatic2_combiner.iconset/icon_32x32.png [new file with mode: 0644]
graphics/osx/dcpomatic2_combiner.iconset/icon_32x32@2x.png [new file with mode: 0644]
graphics/osx/dcpomatic2_combiner.iconset/icon_512x512.png [new file with mode: 0644]
graphics/osx/dcpomatic2_combiner.iconset/icon_512x512@2x.png [new file with mode: 0644]
graphics/src/dcpomatic2_combiner.svg [new file with mode: 0644]
graphics/update
graphics/windows/dcpomatic2_combiner.ico [new file with mode: 0644]
platform/linux/dcpomatic_combiner.desktop.in [new file with mode: 0644]
platform/linux/wscript
platform/osx/dcpomatic2_combiner.Info.plist.in [new file with mode: 0644]
platform/osx/make_dmg.sh
platform/osx/wscript
platform/windows/dcpomatic2_combiner_debug.bat [new file with mode: 0644]
platform/windows/dcpomatic_combiner.rc [new file with mode: 0644]
platform/windows/wscript
run/dcpomatic_combiner [new file with mode: 0755]
src/lib/combine_dcp_job.cc [new file with mode: 0644]
src/lib/combine_dcp_job.h [new file with mode: 0644]
src/lib/wscript
src/tools/dcpomatic_combiner.cc [new file with mode: 0644]
src/tools/wscript

diff --git a/cscript b/cscript
index 241a9580ac97ee20ec05cb6503e9ed1a7a5d0a25..5aeba3da30db0a3ffc243a63a0fe53a71c78a2ac 100644 (file)
--- a/cscript
+++ b/cscript
@@ -373,8 +373,8 @@ def dependencies(target, options):
             (target.platform == 'osx' and target.bits == 64) or
             (target.platform == 'windows')) else {}
 
-    deps.append(('libdcp', '184bdd2', cpp_lib_options))
-    deps.append(('libsub', '72bf4fc', cpp_lib_options))
+    deps.append(('libdcp', '40f6f7d', cpp_lib_options))
+    deps.append(('libsub', 'c5b7a36', cpp_lib_options))
     deps.append(('leqm-nrt', 'carl'))
     deps.append(('rtaudio', 'carl'))
     # We get our OpenSSL libraries from the environment, but we
diff --git a/graphics/linux/128/dcpomatic2_combiner.png b/graphics/linux/128/dcpomatic2_combiner.png
new file mode 100644 (file)
index 0000000..27ad5d0
Binary files /dev/null and b/graphics/linux/128/dcpomatic2_combiner.png differ
diff --git a/graphics/linux/16/dcpomatic2_combiner.png b/graphics/linux/16/dcpomatic2_combiner.png
new file mode 100644 (file)
index 0000000..ae6f698
Binary files /dev/null and b/graphics/linux/16/dcpomatic2_combiner.png differ
diff --git a/graphics/linux/22/dcpomatic2_combiner.png b/graphics/linux/22/dcpomatic2_combiner.png
new file mode 100644 (file)
index 0000000..4fa3d3b
Binary files /dev/null and b/graphics/linux/22/dcpomatic2_combiner.png differ
diff --git a/graphics/linux/256/dcpomatic2_combiner.png b/graphics/linux/256/dcpomatic2_combiner.png
new file mode 100644 (file)
index 0000000..7388b2e
Binary files /dev/null and b/graphics/linux/256/dcpomatic2_combiner.png differ
diff --git a/graphics/linux/32/dcpomatic2_combiner.png b/graphics/linux/32/dcpomatic2_combiner.png
new file mode 100644 (file)
index 0000000..e24e0e0
Binary files /dev/null and b/graphics/linux/32/dcpomatic2_combiner.png differ
diff --git a/graphics/linux/48/dcpomatic2_combiner.png b/graphics/linux/48/dcpomatic2_combiner.png
new file mode 100644 (file)
index 0000000..10fcca2
Binary files /dev/null and b/graphics/linux/48/dcpomatic2_combiner.png differ
diff --git a/graphics/linux/512/dcpomatic2_combiner.png b/graphics/linux/512/dcpomatic2_combiner.png
new file mode 100644 (file)
index 0000000..3451669
Binary files /dev/null and b/graphics/linux/512/dcpomatic2_combiner.png differ
diff --git a/graphics/linux/64/dcpomatic2_combiner.png b/graphics/linux/64/dcpomatic2_combiner.png
new file mode 100644 (file)
index 0000000..4c52f59
Binary files /dev/null and b/graphics/linux/64/dcpomatic2_combiner.png differ
diff --git a/graphics/osx/dcpomatic2_combiner.icns b/graphics/osx/dcpomatic2_combiner.icns
new file mode 100644 (file)
index 0000000..8f4f5c2
Binary files /dev/null and b/graphics/osx/dcpomatic2_combiner.icns differ
diff --git a/graphics/osx/dcpomatic2_combiner.iconset/icon_128x128.png b/graphics/osx/dcpomatic2_combiner.iconset/icon_128x128.png
new file mode 100644 (file)
index 0000000..27ad5d0
Binary files /dev/null and b/graphics/osx/dcpomatic2_combiner.iconset/icon_128x128.png differ
diff --git a/graphics/osx/dcpomatic2_combiner.iconset/icon_128x128@2x.png b/graphics/osx/dcpomatic2_combiner.iconset/icon_128x128@2x.png
new file mode 100644 (file)
index 0000000..27ad5d0
Binary files /dev/null and b/graphics/osx/dcpomatic2_combiner.iconset/icon_128x128@2x.png differ
diff --git a/graphics/osx/dcpomatic2_combiner.iconset/icon_16x16.png b/graphics/osx/dcpomatic2_combiner.iconset/icon_16x16.png
new file mode 100644 (file)
index 0000000..ae6f698
Binary files /dev/null and b/graphics/osx/dcpomatic2_combiner.iconset/icon_16x16.png differ
diff --git a/graphics/osx/dcpomatic2_combiner.iconset/icon_16x16@2x.png b/graphics/osx/dcpomatic2_combiner.iconset/icon_16x16@2x.png
new file mode 100644 (file)
index 0000000..ae6f698
Binary files /dev/null and b/graphics/osx/dcpomatic2_combiner.iconset/icon_16x16@2x.png differ
diff --git a/graphics/osx/dcpomatic2_combiner.iconset/icon_256x256.png b/graphics/osx/dcpomatic2_combiner.iconset/icon_256x256.png
new file mode 100644 (file)
index 0000000..7388b2e
Binary files /dev/null and b/graphics/osx/dcpomatic2_combiner.iconset/icon_256x256.png differ
diff --git a/graphics/osx/dcpomatic2_combiner.iconset/icon_256x256@2x.png b/graphics/osx/dcpomatic2_combiner.iconset/icon_256x256@2x.png
new file mode 100644 (file)
index 0000000..7388b2e
Binary files /dev/null and b/graphics/osx/dcpomatic2_combiner.iconset/icon_256x256@2x.png differ
diff --git a/graphics/osx/dcpomatic2_combiner.iconset/icon_32x32.png b/graphics/osx/dcpomatic2_combiner.iconset/icon_32x32.png
new file mode 100644 (file)
index 0000000..e24e0e0
Binary files /dev/null and b/graphics/osx/dcpomatic2_combiner.iconset/icon_32x32.png differ
diff --git a/graphics/osx/dcpomatic2_combiner.iconset/icon_32x32@2x.png b/graphics/osx/dcpomatic2_combiner.iconset/icon_32x32@2x.png
new file mode 100644 (file)
index 0000000..e24e0e0
Binary files /dev/null and b/graphics/osx/dcpomatic2_combiner.iconset/icon_32x32@2x.png differ
diff --git a/graphics/osx/dcpomatic2_combiner.iconset/icon_512x512.png b/graphics/osx/dcpomatic2_combiner.iconset/icon_512x512.png
new file mode 100644 (file)
index 0000000..3451669
Binary files /dev/null and b/graphics/osx/dcpomatic2_combiner.iconset/icon_512x512.png differ
diff --git a/graphics/osx/dcpomatic2_combiner.iconset/icon_512x512@2x.png b/graphics/osx/dcpomatic2_combiner.iconset/icon_512x512@2x.png
new file mode 100644 (file)
index 0000000..3451669
Binary files /dev/null and b/graphics/osx/dcpomatic2_combiner.iconset/icon_512x512@2x.png differ
diff --git a/graphics/src/dcpomatic2_combiner.svg b/graphics/src/dcpomatic2_combiner.svg
new file mode 100644 (file)
index 0000000..8a150c5
--- /dev/null
@@ -0,0 +1,231 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   viewBox="0 0 1000 1000"
+   sodipodi:docname="dcpomatic2_combine.svg"
+   inkscape:version="1.0 (4035a4f, 2020-05-01)"
+   version="1.1"
+   id="svg2"
+   height="1066.6666"
+   width="1066.6666">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="Arrow1Mstart"
+       refX="0.0"
+       refY="0.0"
+       orient="auto"
+       inkscape:stockid="Arrow1Mstart">
+      <path
+         transform="scale(0.4) translate(10,0)"
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         id="path893" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible;"
+       id="marker1193"
+       refX="0.0"
+       refY="0.0"
+       orient="auto"
+       inkscape:stockid="Arrow1Lend">
+      <path
+         transform="scale(0.8) rotate(180) translate(12.5,0)"
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         id="path1191" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker1189"
+       refX="0.0"
+       refY="0.0"
+       orient="auto"
+       inkscape:stockid="Arrow1Lstart">
+      <path
+         transform="scale(0.8) translate(12.5,0)"
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         id="path1187" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="marker1167"
+       refX="0.0"
+       refY="0.0"
+       orient="auto"
+       inkscape:stockid="Arrow1Lstart">
+      <path
+         transform="scale(0.8) translate(12.5,0)"
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         id="path1165" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible;"
+       id="Arrow1Lend"
+       refX="0.0"
+       refY="0.0"
+       orient="auto"
+       inkscape:stockid="Arrow1Lend">
+      <path
+         transform="scale(0.8) rotate(180) translate(12.5,0)"
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         id="path890" />
+    </marker>
+    <marker
+       inkscape:isstock="true"
+       style="overflow:visible"
+       id="Arrow1Lstart"
+       refX="0.0"
+       refY="0.0"
+       orient="auto"
+       inkscape:stockid="Arrow1Lstart">
+      <path
+         transform="scale(0.8) translate(12.5,0)"
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         id="path887" />
+    </marker>
+    <linearGradient
+       id="linearGradient3594"
+       y2="742.5"
+       gradientUnits="userSpaceOnUse"
+       x2="-886.76001"
+       gradientTransform="matrix(-0.84033,-0.84033,-0.84033,0.84033,136.32259,-691.39649)"
+       y1="742.5"
+       x1="-772.01001">
+      <stop
+         id="stop4687"
+         stop-color="#fff"
+         offset="0" />
+      <stop
+         id="stop4689"
+         stop-color="#fff"
+         stop-opacity="0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3601"
+       y2="613.94"
+       gradientUnits="userSpaceOnUse"
+       x2="385.04001"
+       gradientTransform="matrix(0.70711,-0.70711,0.70711,0.70711,-203.97741,756.21351)"
+       y1="63.870998"
+       x1="386.39001">
+      <stop
+         id="stop3797"
+         stop-color="#ffe800"
+         offset="0" />
+      <stop
+         id="stop3799"
+         stop-color="#dfb300"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3609"
+       y2="161.84"
+       gradientUnits="userSpaceOnUse"
+       x2="212.92999"
+       y1="358.29999"
+       x1="409.38"
+       gradientTransform="translate(-77.797413,384.00351)">
+      <stop
+         id="stop4034"
+         stop-color="#dfb300"
+         offset="0" />
+      <stop
+         id="stop3374"
+         stop-color="#dfb300"
+         offset=".5" />
+      <stop
+         id="stop3376"
+         stop-color="#dfb300"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3632"
+       y2="448.35001"
+       gradientUnits="userSpaceOnUse"
+       x2="382.89999"
+       gradientTransform="matrix(0.70711,-0.70711,0.70711,0.70711,-203.97741,756.21351)"
+       y1="448.35001"
+       x1="403.63">
+      <stop
+         id="stop3636"
+         stop-color="#ffe800"
+         stop-opacity=".39216"
+         offset="0" />
+      <stop
+         id="stop3638"
+         stop-color="#dfb300"
+         stop-opacity=".39216"
+         offset="1" />
+    </linearGradient>
+  </defs>
+  <sodipodi:namedview
+     inkscape:snap-midpoints="true"
+     inkscape:document-rotation="0"
+     inkscape:window-maximized="1"
+     inkscape:window-y="27"
+     inkscape:window-x="0"
+     inkscape:window-height="986"
+     inkscape:window-width="1680"
+     showgrid="false"
+     inkscape:current-layer="layer1"
+     inkscape:document-units="px"
+     inkscape:cy="589.03894"
+     inkscape:cx="-138.64759"
+     inkscape:zoom="0.34926264"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     borderopacity="1.0"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     transform="translate(0,-52.362188)"
+     id="layer1"
+     inkscape:groupmode="layer"
+     inkscape:label="Layer 1">
+    <image
+       sodipodi:absref="/home/carl/src/dcpomatic/graphics/src/dcpomatic.png"
+       xlink:href="dcpomatic.png"
+       id="image4358"
+       preserveAspectRatio="none"
+       height="960.00006"
+       width="960.00006"
+       x="10.670144"
+       y="80.386467" />
+    <path
+       d="m 490.67101,280.27778 a 46.083593,46.083593 0 0 0 -46.08414,46.08418 V 514.30317 H 256.6457 a 46.083685,46.083685 0 0 0 -46.08423,46.08417 46.083685,46.083685 0 0 0 46.08423,46.08249 h 187.94117 v 187.94291 a 46.083593,46.083593 0 0 0 46.08414,46.08248 46.083593,46.083593 0 0 0 46.08256,-46.08248 V 606.46983 h 187.94284 a 46.083685,46.083685 0 0 0 46.08247,-46.08249 46.083685,46.083685 0 0 0 -46.08247,-46.08417 H 536.75357 V 326.36196 a 46.083593,46.083593 0 0 0 -46.08256,-46.08418 z"
+       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#5e5e5e;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:15.5608;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;stop-color:#000000;opacity:0.79289843"
+       id="path1288" />
+  </g>
+</svg>
index cee8a3ca152cb1d8054b4f00f9d27a83854f849e..5896867bc17fa295dbe788f43e93380ea6bac1f2 100755 (executable)
@@ -23,7 +23,7 @@ function required_font()
     fi
 }
 
-svg_apps="dcpomatic2_kdm dcpomatic2_server dcpomatic2_batch dcpomatic2_player dcpomatic2_playlist dcpomatic2_disk"
+svg_apps="dcpomatic2_kdm dcpomatic2_server dcpomatic2_batch dcpomatic2_player dcpomatic2_playlist dcpomatic2_disk dcpomatic2_combiner"
 required_font "Libre Baskerville"
 
 
diff --git a/graphics/windows/dcpomatic2_combiner.ico b/graphics/windows/dcpomatic2_combiner.ico
new file mode 100644 (file)
index 0000000..0db1eb1
Binary files /dev/null and b/graphics/windows/dcpomatic2_combiner.ico differ
diff --git a/platform/linux/dcpomatic_combiner.desktop.in b/platform/linux/dcpomatic_combiner.desktop.in
new file mode 100644 (file)
index 0000000..2b875ee
--- /dev/null
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Encoding=UTF-8
+Version=1.0
+Type=Application
+Terminal=false
+Exec=@INSTALL_PREFIX@/bin/dcpomatic2_combiner
+Name=DCP-o-matic 2 Combiner
+Icon=dcpomatic2_combiner
+Comment=DCP combiner
+Categories=AudioVideo;Video
index 0fc593f56aef65bd15c409e57ccc01b24b7e2bef..dad5778a6d51128f241f0338cfcf40ffc80bae8c 100644 (file)
@@ -51,4 +51,11 @@ def build(bld):
     obj.VERSION = bld.env.VERSION
     desktops.append(obj.target)
 
+    obj = bld(features='subst')
+    obj.source = 'dcpomatic_combiner.desktop.in'
+    obj.target = 'dcpomatic2_combiner.desktop'
+    obj.INSTALL_PREFIX = bld.env.INSTALL_PREFIX
+    obj.VERSION = bld.env.VERSION
+    desktops.append(obj.target)
+
     bld.install_files('${PREFIX}/share/applications', desktops)
diff --git a/platform/osx/dcpomatic2_combiner.Info.plist.in b/platform/osx/dcpomatic2_combiner.Info.plist.in
new file mode 100644 (file)
index 0000000..a9ab619
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>English</string>
+       <key>CFBundleExecutable</key>
+       <string>dcpomatic2_combiner</string>
+       <key>CFBundleGetInfoString</key>
+       <string>DCP-o-matic 2 Combiner</string>
+       <key>CFBundleIconFile</key>
+       <string>dcpomatic2_combiner.icns</string>
+       <key>CFBundleIdentifier</key>
+       <string>com.dcpomatic.combiner</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundleName</key>
+       <string>DCP-o-matic 2 Combiner</string>
+       <key>CFBundlePackageType</key>
+       <string>APPL</string>
+       <key>CFBundleShortVersions</key>
+       <string>@VERSION@</string>
+       <key>CFBundleSignature</key>
+       <string>DOMC</string>
+       <key>CFBundleVersion</key>
+       <string>@VERSION@</string>
+       <key>CFBundleAllowMixedLocalizations</key>
+       <true/>
+       <key>LSUIElement</key>
+       <string>0</string>
+       <key>NSMainNibFile</key>
+       <string>MainMenu</string>
+       <key>NSPrincipalClass</key>
+       <string>NSApplication</string>
+</dict>
+</plist>
index f55a0e8b14cfe969e6632f30c81e22385d366e34..643e6af3348e6ed5166241ed623f918ed9f926c7 100644 (file)
@@ -191,6 +191,7 @@ function copy_resources {
     cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_batch.icns "$dest"
     cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_playlist.icns "$dest"
     cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_disk.icns "$dest"
+    cp $prefix/src/dcpomatic/graphics/osx/dcpomatic2_combiner.icns "$dest"
     cp $prefix/src/dcpomatic/graphics/osx/preferences/defaults.png "$dest"
     cp $prefix/src/dcpomatic/graphics/osx/preferences/defaults@2x.png "$dest"
     cp $prefix/src/dcpomatic/graphics/osx/preferences/kdm_email.png "$dest"
@@ -522,6 +523,15 @@ rl=("$approot/MacOS/dcpomatic2_playlist" "$approot/Frameworks/"*.dylib)
 relink_relative "${rl[@]}"
 make_dmg "$appdir" "" "DCP-o-matic Playlist Editor" com.dcpomatic.playlist
 
+# DCP-o-matic Combiner
+setup "DCP-o-matic 2 Combiner.app"
+copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_combiner "$approot/MacOS"
+copy $ROOT src/openssl/apps/openssl "$approot/MacOS"
+cp $prefix/src/dcpomatic/build/platform/osx/dcpomatic2_combiner.Info.plist "$approot/Info.plist"
+rl=("$approot/MacOS/dcpomatic2_combiner" "$approot/Frameworks/"*.dylib)
+relink_relative "${rl[@]}"
+make_dmg "$appdir" "" "DCP-o-matic Combiner" com.dcpomatic.combiner
+
 # DCP-o-matic Disk Writer .app
 setup "DCP-o-matic 2 Disk Writer.app"
 copy $ROOT src/dcpomatic/build/src/tools/dcpomatic2_disk "$approot/MacOS"
index da20065f5622e297b223e0167451c3a99a06268c..c0b3d9d10a69545470ab3e0410f118713875804a 100644 (file)
@@ -6,3 +6,4 @@ def build(bld):
     obj = bld(features='subst', source='dcpomatic2_player.Info.plist.in', target='dcpomatic2_player.Info.plist', version=bld.env.VERSION)
     obj = bld(features='subst', source='dcpomatic2_playlist.Info.plist.in', target='dcpomatic2_playlist.Info.plist', version=bld.env.VERSION)
     obj = bld(features='subst', source='dcpomatic2_disk.Info.plist.in', target='dcpomatic2_disk.Info.plist', version=bld.env.VERSION)
+    obj = bld(features='subst', source='dcpomatic2_combiner.Info.plist.in', target='dcpomatic2_combiner.Info.plist', version=bld.env.VERSION)
diff --git a/platform/windows/dcpomatic2_combiner_debug.bat b/platform/windows/dcpomatic2_combiner_debug.bat
new file mode 100644 (file)
index 0000000..a4aae9f
--- /dev/null
@@ -0,0 +1 @@
+gdb.exe -x gdb_script dcpomatic2_combiner.exe > %HOMEPATH%/Documents/dcpomatic_debug_log.txt
diff --git a/platform/windows/dcpomatic_combiner.rc b/platform/windows/dcpomatic_combiner.rc
new file mode 100644 (file)
index 0000000..1b72bf5
--- /dev/null
@@ -0,0 +1,2 @@
+id ICON "../../graphics/windows/dcpomatic2_combiner.ico"
+#include "wx-3.0/wx/msw/wx.rc"
index 64f0a57cf9a47de1e62a6b57a7717986e2cca68b..e4603808711027f321861b112f4235f14dd44a80 100644 (file)
@@ -10,7 +10,8 @@ def write_installer(bits, dcpomatic_version, debug, variant, disk):
         ('player', 'Player'),
         ('cli', 'CLI'),
         ('create', 'Creator'),
-        ('playlist', 'Playlist Editor')
+        ('playlist', 'Playlist Editor'),
+        ('combiner', 'Combiner'),
     ]
 
     if disk:
diff --git a/run/dcpomatic_combiner b/run/dcpomatic_combiner
new file mode 100755 (executable)
index 0000000..3cf60d8
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+export LD_LIBRARY_PATH=build/src/lib:build/src/wx:build/src/asdcplib/src:/home/c.hetherington/lib:$LD_LIBRARY_PATH
+export DYLD_LIBRARY_PATH=build/src/lib:build/src/wx:build/src/asdcplib/src:/Users/c.hetherington/osx-environment/64/lib
+if [ "$1" == "--debug" ]; then
+    shift
+    gdb --args build/src/tools/dcpomatic2_combiner $*
+elif [ "$1" == "--valgrind" ]; then
+    shift
+    valgrind --tool="memcheck" --suppressions=suppressions --track-fds=yes build/src/tools/dcpomatic2_combiner $*
+elif [ "$1" == "--callgrind" ]; then
+    shift
+    valgrind --tool="callgrind" build/src/tools/dcpomatic2_combiner $*
+elif [ "$1" == "--massif" ]; then
+    shift
+    valgrind --tool="massif" build/src/tools/dcpomatic2_combiner $*
+elif [ "$1" == "--i18n" ]; then
+    shift
+    LANGUAGE=fr_FR.UTF8 LANG=fr_FR.UTF8 LC_ALL=fr_FR.UTF8 build/src/tools/dcpomatic2_combiner "$*"
+elif [ "$1" == "--perf" ]; then
+    shift
+    perf record build/src/tools/dcpomatic2_combiner $*
+else
+    build/src/tools/dcpomatic2_combiner $*
+fi
diff --git a/src/lib/combine_dcp_job.cc b/src/lib/combine_dcp_job.cc
new file mode 100644 (file)
index 0000000..b3c29b2
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+    Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    DCP-o-matic is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "combine_dcp_job.h"
+#include <dcp/combine.h>
+#include <dcp/exceptions.h>
+
+#include "i18n.h"
+
+
+using std::string;
+using std::vector;
+using boost::shared_ptr;
+
+
+CombineDCPJob::CombineDCPJob (vector<boost::filesystem::path> inputs, boost::filesystem::path output)
+       : Job (shared_ptr<Film>())
+       , _inputs (inputs)
+       , _output (output)
+{
+
+}
+
+
+string
+CombineDCPJob::name () const
+{
+       return _("Combine DCPs");
+}
+
+
+string
+CombineDCPJob::json_name () const
+{
+       return N_("combine_dcps");
+}
+
+
+void
+CombineDCPJob::run ()
+{
+       try {
+               dcp::combine (_inputs, _output);
+       } catch (dcp::CombineError& e) {
+               set_state (FINISHED_ERROR);
+               set_error (e.what(), "");
+               return;
+       } catch (dcp::ReadError& e) {
+               set_state (FINISHED_ERROR);
+               set_error (e.what(), e.detail().get_value_or(""));
+               return;
+       }
+
+       set_progress (1);
+       set_state (FINISHED_OK);
+}
diff --git a/src/lib/combine_dcp_job.h b/src/lib/combine_dcp_job.h
new file mode 100644 (file)
index 0000000..97bf201
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+    Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    DCP-o-matic is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "job.h"
+#include <boost/filesystem.hpp>
+
+
+class CombineDCPJob : public Job
+{
+public:
+       CombineDCPJob (std::vector<boost::filesystem::path> inputs, boost::filesystem::path output);
+
+       std::string name () const;
+       std::string json_name () const;
+       void run ();
+
+private:
+       std::vector<boost::filesystem::path> _inputs;
+       boost::filesystem::path _output;
+};
+
index 0c9cddfa44456db8f352f5d24e208a49059f075e..04044a8c3f4f82011ac37a4c2f8041e0ac5fe522 100644 (file)
@@ -55,6 +55,7 @@ sources = """
           config.cc
           content.cc
           content_factory.cc
+          combine_dcp_job.cc
           copy_dcp_details_to_film.cc
           create_cli.cc
           cross_common.cc
diff --git a/src/tools/dcpomatic_combiner.cc b/src/tools/dcpomatic_combiner.cc
new file mode 100644 (file)
index 0000000..5f1a772
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+    Copyright (C) 2020 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    DCP-o-matic is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "wx/dir_picker_ctrl.h"
+#include "wx/editable_list.h"
+#include "wx/wx_signal_manager.h"
+#include "lib/combine_dcp_job.h"
+#include "lib/config.h"
+#include "lib/cross.h"
+#include "lib/job_manager.h"
+#include "lib/util.h"
+#include <dcp/combine.h>
+DCPOMATIC_DISABLE_WARNINGS
+#include <wx/filepicker.h>
+DCPOMATIC_ENABLE_WARNINGS
+#include <wx/wx.h>
+#include <boost/bind.hpp>
+#include <boost/filesystem.hpp>
+#include <exception>
+
+
+using std::exception;
+using std::string;
+using std::vector;
+using boost::dynamic_pointer_cast;
+using boost::optional;
+using boost::shared_ptr;
+
+
+static string
+display_string (boost::filesystem::path p, int)
+{
+       return p.filename().string();
+}
+
+
+class DirDialogWrapper : public wxDirDialog
+{
+public:
+       DirDialogWrapper (wxWindow* parent)
+               : wxDirDialog (parent, _("Choose a DCP folder"), wxT(""), wxDD_DIR_MUST_EXIST)
+       {
+
+       }
+
+       boost::filesystem::path get () const
+       {
+               return boost::filesystem::path(wx_to_std(GetPath()));
+       }
+
+       void set (boost::filesystem::path)
+       {
+               /* Not used */
+       }
+};
+
+
+class DOMFrame : public wxFrame
+{
+public:
+       explicit DOMFrame (wxString const & title)
+               : wxFrame (0, -1, title)
+       {
+               /* Use a panel as the only child of the Frame so that we avoid
+                  the dark-grey background on Windows.
+               */
+               wxPanel* overall_panel = new wxPanel (this);
+               wxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+               s->Add (overall_panel, 1, wxEXPAND);
+               SetSizer (s);
+
+               vector<EditableListColumn> columns;
+               columns.push_back(EditableListColumn(_("Input DCP"), 600, true));
+
+               _input = new EditableList<boost::filesystem::path, DirDialogWrapper>(
+                       overall_panel,
+                       columns,
+                       boost::bind(&DOMFrame::inputs, this),
+                       boost::bind(&DOMFrame::set_inputs, this, _1),
+                       &display_string,
+                       false,
+                       true
+                       );
+
+               wxBoxSizer* output = new wxBoxSizer (wxHORIZONTAL);
+               add_label_to_sizer (output, overall_panel, _("Output DCP folder"), true, 0, wxLEFT | wxRIGHT | wxALIGN_CENTRE_VERTICAL);
+               _output = new DirPickerCtrl (overall_panel);
+               output->Add (_output, 1, wxEXPAND);
+
+               _combine = new Button (overall_panel, _("Combine"));
+
+               wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL);
+               sizer->Add (_input, 1, wxALL | wxEXPAND, DCPOMATIC_DIALOG_BORDER);
+               sizer->Add (output, 0, wxALL | wxEXPAND, DCPOMATIC_DIALOG_BORDER);
+               sizer->Add (_combine, 0, wxALL | wxALIGN_RIGHT, DCPOMATIC_DIALOG_BORDER);
+               overall_panel->SetSizer (sizer);
+               Fit ();
+               SetSize (768, GetSize().GetHeight() + 32);
+
+               _combine->Bind (wxEVT_BUTTON, boost::bind(&DOMFrame::combine, this));
+               _output->Bind (wxEVT_DIRPICKER_CHANGED, boost::bind(&DOMFrame::setup_sensitivity, this));
+
+               setup_sensitivity ();
+       }
+
+private:
+       void set_inputs (vector<boost::filesystem::path> inputs)
+       {
+               _inputs = inputs;
+       }
+
+       vector<boost::filesystem::path> inputs () const
+       {
+               return _inputs;
+       }
+
+       void combine ()
+       {
+               boost::filesystem::path const output = wx_to_std(_output->GetPath());
+
+               if (boost::filesystem::is_directory(output) && !boost::filesystem::is_empty(output)) {
+                       if (!confirm_dialog (
+                                   this,
+                                   std_to_wx (
+                                           String::compose(wx_to_std(_("The directory %1 already exists and is not empty.  "
+                                                                       "Are you sure you want to use it?")),
+                                                           output.string())
+                                           )
+                                   )) {
+                               return;
+                       }
+               } else if (boost::filesystem::is_regular_file(output)) {
+                       error_dialog (
+                               this,
+                               String::compose (wx_to_std(_("%1 already exists as a file, so you cannot use it for a DCP.")), output.string())
+                               );
+                       return;
+               }
+
+               JobManager* jm = JobManager::instance ();
+               jm->add (shared_ptr<Job>(new CombineDCPJob(_inputs, output)));
+               bool const ok = display_progress (_("DCP-o-matic Combine"), _("Combining DCPs"));
+               if (!ok) {
+                       return;
+               }
+
+               DCPOMATIC_ASSERT (!jm->get().empty());
+               shared_ptr<CombineDCPJob> last = dynamic_pointer_cast<CombineDCPJob> (jm->get().back());
+               DCPOMATIC_ASSERT (last);
+
+               if (last->finished_ok()) {
+                       message_dialog (this, _("DCPs combined successfully."));
+               } else {
+                       wxString m = std_to_wx(last->error_summary());
+                       if (!last->error_details().empty()) {
+                               m += wxString::Format(" (%s)", std_to_wx(last->error_details()));
+                       }
+                       error_dialog (this, m);
+               }
+       }
+
+       void setup_sensitivity ()
+       {
+               _combine->Enable (!_output->GetPath().IsEmpty());
+       }
+
+       EditableList<boost::filesystem::path, DirDialogWrapper>* _input;
+       DirPickerCtrl* _output;
+       vector<boost::filesystem::path> _inputs;
+       Button* _combine;
+};
+
+
+class App : public wxApp
+{
+public:
+       App ()
+               : _frame (0)
+       {}
+
+       bool OnInit ()
+       {
+               try {
+                       Config::FailedToLoad.connect (boost::bind (&App::config_failed_to_load, this));
+                       Config::Warning.connect (boost::bind (&App::config_warning, this, _1));
+
+                       SetAppName (_("DCP-o-matic Combiner"));
+
+                       if (!wxApp::OnInit()) {
+                               return false;
+                       }
+
+#ifdef DCPOMATIC_LINUX
+                       unsetenv ("UBUNTU_MENUPROXY");
+#endif
+
+#ifdef DCPOMATIC_OSX
+                       make_foreground_application ();
+#endif
+
+                       dcpomatic_setup_path_encoding ();
+
+                       /* Enable i18n; this will create a Config object
+                          to look for a force-configured language.  This Config
+                          object will be wrong, however, because dcpomatic_setup
+                          hasn't yet been called and there aren't any filters etc.
+                          set up yet.
+                       */
+                       dcpomatic_setup_i18n ();
+
+                       /* Set things up, including filters etc.
+                          which will now be internationalised correctly.
+                       */
+                       dcpomatic_setup ();
+
+                       /* Force the configuration to be re-loaded correctly next
+                          time it is needed.
+                       */
+                       Config::drop ();
+
+                       _frame = new DOMFrame (_("DCP-o-matic DCP Combiner"));
+                       SetTopWindow (_frame);
+
+                       _frame->Show ();
+
+                       signal_manager = new wxSignalManager (this);
+                       Bind (wxEVT_IDLE, boost::bind(&App::idle, this, _1));
+               }
+               catch (exception& e)
+               {
+                       error_dialog (0, wxString::Format ("DCP-o-matic DCP Combiner could not start."), std_to_wx(e.what()));
+                       return false;
+               }
+
+               return true;
+       }
+
+       void config_failed_to_load ()
+       {
+               message_dialog (_frame, _("The existing configuration failed to load.  Default values will be used instead.  These may take a short time to create."));
+       }
+
+       void config_warning (string m)
+       {
+               message_dialog (_frame, std_to_wx(m));
+       }
+
+       void idle (wxIdleEvent& ev)
+       {
+               signal_manager->ui_idle ();
+               ev.Skip ();
+       }
+
+       void report_exception ()
+       {
+               try {
+                       throw;
+               } catch (FileError& e) {
+                       error_dialog (
+                               0,
+                               wxString::Format (
+                                       _("An exception occurred: %s (%s)\n\n") + REPORT_PROBLEM,
+                                       std_to_wx (e.what()),
+                                       std_to_wx (e.file().string().c_str ())
+                                       )
+                               );
+               } catch (exception& e) {
+                       error_dialog (
+                               0,
+                               wxString::Format (
+                                       _("An exception occurred: %s.\n\n") + REPORT_PROBLEM,
+                                       std_to_wx (e.what ())
+                                       )
+                               );
+               } catch (...) {
+                       error_dialog (0, _("An unknown exception occurred.") + "  " + REPORT_PROBLEM);
+               }
+       }
+
+       bool OnExceptionInMainLoop ()
+       {
+               report_exception ();
+               /* This will terminate the program */
+               return false;
+       }
+
+       void OnUnhandledException ()
+       {
+               report_exception ();
+       }
+
+       DOMFrame* _frame;
+};
+
+IMPLEMENT_APP (App)
index 7eeeecddfb80d48c909f290c7c14831cd79261e7..9caa84e9bbed59fd60ed245c29da44182c9e2463 100644 (file)
@@ -1,5 +1,5 @@
 #
-#    Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+#    Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
 #
 #    This file is part of DCP-o-matic.
 #
@@ -73,7 +73,7 @@ def build(bld):
         elif bld.env.VARIANT == 'swaroop-studio':
             gui_tools = ['dcpomatic', 'dcpomatic_batch', 'dcpomatic_server', 'dcpomatic_kdm', 'dcpomatic_player', 'swaroop_dcpomatic_playlist']
         else:
-            gui_tools = ['dcpomatic', 'dcpomatic_batch', 'dcpomatic_server', 'dcpomatic_kdm', 'dcpomatic_player', 'dcpomatic_playlist']
+            gui_tools = ['dcpomatic', 'dcpomatic_batch', 'dcpomatic_server', 'dcpomatic_kdm', 'dcpomatic_player', 'dcpomatic_playlist', 'dcpomatic_combiner']
             if bld.env.ENABLE_DISK:
                 gui_tools.append('dcpomatic_disk')