Browse Source

initial commit

Michael Hoskins 5 years ago
commit
d828fb378c

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
+.vs/
+bin/
+obj/
+packages/
+
+*.suo

+ 28 - 0
MovieBarcodeGenerator.sln

@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26430.13
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MovieBarcodeGenerator", "MovieBarcodeGenerator\MovieBarcodeGenerator.csproj", "{97B87120-43C1-4D85-8D95-FCF56B5FA0F8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTestProject1\UnitTests.csproj", "{EA169873-EADE-4ED2-B87A-80E622231D0C}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{97B87120-43C1-4D85-8D95-FCF56B5FA0F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{97B87120-43C1-4D85-8D95-FCF56B5FA0F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{97B87120-43C1-4D85-8D95-FCF56B5FA0F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{97B87120-43C1-4D85-8D95-FCF56B5FA0F8}.Release|Any CPU.Build.0 = Release|Any CPU
+		{EA169873-EADE-4ED2-B87A-80E622231D0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{EA169873-EADE-4ED2-B87A-80E622231D0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{EA169873-EADE-4ED2-B87A-80E622231D0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{EA169873-EADE-4ED2-B87A-80E622231D0C}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

+ 38 - 0
MovieBarcodeGenerator/App.config

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+  <configSections>
+    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
+  </configSections>
+  <startup> 
+      <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
+  </startup>
+  
+  <appSettings>
+    <add key="folderImagick" value="C:\utils\ImageMagick-6.9.2-Q16" />
+    <add key="folderFFMpeg" value="C:\utils\ffmpeg\bin" />
+  </appSettings>
+  
+  <log4net>
+    <appender name="ColoredConsoleAppenderAll" type="log4net.Appender.ColoredConsoleAppender">
+      <mapping>
+        <level value="WARN" />
+        <foreColor value="Red" />
+      </mapping>
+      <mapping>
+        <level value="ERROR" />
+        <foreColor value="Red, HighIntensity" />
+      </mapping>
+      <filter type="log4net.Filter.LevelRangeFilter">
+        <levelMin value="DEBUG" />
+        <levelMax value="FATAL" />
+      </filter>
+      <layout type="log4net.Layout.PatternLayout">
+        <conversionPattern value="%date [%level] %logger - %message%newline" />
+      </layout>
+    </appender>
+    <root>
+      <level value="ALL" />
+      <appender-ref ref="ColoredConsoleAppenderAll" />
+    </root>
+  </log4net>
+</configuration>

+ 68 - 0
MovieBarcodeGenerator/BarcodeGenerator.cs

@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MovieBarcodeGenerator {
+    public class BarcodeGenerator {
+        public static string imagickPath;
+        private static log4net.ILog log = log4net.LogManager.GetLogger("Generator");
+
+        public static string ffmpegPath {
+            get {
+                return SharpFF.ffmpegPath;
+            }
+            set {
+                SharpFF.ffmpegPath = value;
+            }
+        }
+
+        public static void Generate(string inputFile, string outputFile, int height, int width) {
+            Generate(inputFile, outputFile, height, 1, width);
+        }
+
+        public static void Generate(string inputFile, string outputFile, int height, int barWidth, int iterations) {
+            log.Debug("Generate()");
+            if (!Directory.Exists(imagickPath)) {
+                log.ErrorFormat("ImageMagick was not found at '{0}'.", imagickPath);
+                throw new Exception("ImageMagick was not found.");
+            }
+            if (File.Exists(outputFile)) {
+                log.InfoFormat("Output file '{0}' exists. Deleting file.", outputFile);
+                File.Delete(outputFile);
+            }
+
+            // set the path because .Net uses the "convert" utility on Windows by default
+            System.Environment.SetEnvironmentVariable("Path", imagickPath);
+
+            decimal videoLength = SharpFF.GetDuration(inputFile);
+
+            // run these in parallel to save time
+            // TODO: do all this work in a temp folder, then move the finished file to the destination
+            Parallel.For(0, iterations, i => {
+                string timecodeAt = SharpFF.SecondsToTimecode(i * (videoLength / iterations));
+                SharpFF.ExecuteCommand(string.Format("-hide_banner -loglevel panic -nostats -y -ss {1} -i \"{0}\" -vframes 1 -an -f rawvideo -vcodec png -vf scale={4}:{5} \"{2}\\out_{3:000}.png\"", inputFile, timecodeAt, Path.GetDirectoryName(outputFile), i, barWidth, height));
+            });
+
+            log.Debug("Scrunching PNGs together.");
+            // use ImageMagick to crush the generated PNGs together
+            Process p = new Process();
+            p.StartInfo.FileName = Path.Combine(imagickPath, "convert.exe");
+            p.StartInfo.WorkingDirectory = Path.GetDirectoryName(outputFile);
+            p.StartInfo.Arguments = String.Format("out_*.png +append \"{0}\"", outputFile);
+            p.StartInfo.UseShellExecute = false;
+            p.Start();
+            p.WaitForExit();
+
+            log.Debug("Deleting temporary work files.");
+            // clean up the work files
+            string[] files = Directory.GetFiles(Path.GetDirectoryName(outputFile), "out_???.png");
+            foreach (string file in files) {
+                File.Delete(file);
+            }
+        }
+    }
+}

+ 72 - 0
MovieBarcodeGenerator/MovieBarcodeGenerator.csproj

@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{97B87120-43C1-4D85-8D95-FCF56B5FA0F8}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>MovieBarcodeGenerator</RootNamespace>
+    <AssemblyName>MovieBarcodeGenerator</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="log4net, Version=1.2.14.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
+      <HintPath>..\packages\log4net.2.0.4\lib\net45-full\log4net.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="NDesk.Options, Version=0.2.1.0, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\packages\NDesk.Options.0.2.1\lib\NDesk.Options.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Configuration" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="BarcodeGenerator.cs" />
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="SharpFF.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config">
+      <SubType>Designer</SubType>
+    </None>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 55 - 0
MovieBarcodeGenerator/Program.cs

@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using NDesk.Options;
+using log4net;
+
+namespace MovieBarcodeGenerator {
+    class Program {
+        private static log4net.ILog log = log4net.LogManager.GetLogger("Main");
+
+        static void Main(string[] args) {
+            log4net.Config.XmlConfigurator.Configure();
+
+            string inputFile = null;
+            string outputFile = null;
+            int outputHeight = 100;
+            int iterations = 1000;
+            int barWidth = 1;
+
+
+            OptionSet op = new OptionSet()
+                .Add("i=|input=", delegate(string v) { inputFile = v; })
+                .Add("o=|output=", delegate(string v) { outputFile = v; })
+                .Add("h:|height:", delegate(int v) { outputHeight = v; })
+                .Add("s:|slices:", delegate(int v) { iterations = v; })
+                .Add("bw:|barwidth:", delegate(int v) { barWidth = v; });
+
+            op.Parse(args);
+            if (inputFile == null || outputFile == null) {
+                ShowHelp();
+                return;
+            }
+
+            BarcodeGenerator.imagickPath = System.Configuration.ConfigurationManager.AppSettings["folderImagick"];
+            BarcodeGenerator.ffmpegPath = System.Configuration.ConfigurationManager.AppSettings["folderFFMpeg"];
+            BarcodeGenerator.Generate(inputFile, outputFile, outputHeight, barWidth, iterations);
+            log.Info("Complete.");
+        }
+
+        private static void ShowHelp() {
+            Console.WriteLine("Movie Barcode Generator v1.0, Created 2015 pixelbath.com");
+            Console.WriteLine();
+            Console.WriteLine(" -i, --input FILE\t(required) the input video file path");
+            Console.WriteLine(" -o, --output FILE\t(required) the output image file path");
+            Console.WriteLine(" -h, --height HEIGHT\tthe output image height, in pixels (default: 100)");
+            Console.WriteLine(" -s, --slices WIDTH\tnumber of slices used to generate image (default: 1000)");
+            Console.WriteLine(" -bw, --barwidth WIDTH\tindividual slice width, in pixels (default: 1)");
+            Console.WriteLine();
+        }
+    }
+}

+ 36 - 0
MovieBarcodeGenerator/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("MovieBarcodeGenerator")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("MovieBarcodeGenerator")]
+[assembly: AssemblyCopyright("Copyright ©  2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("3f432827-8239-4a41-8e3d-ff5b31e68871")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 80 - 0
MovieBarcodeGenerator/SharpFF.cs

@@ -0,0 +1,80 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using log4net;
+
+namespace MovieBarcodeGenerator {
+    public class SharpFF {
+        public static string ffmpegPath { get; set; }
+        private static log4net.ILog log = log4net.LogManager.GetLogger("SharpFF");
+
+        public static void ExecuteCommand(string parameters) {
+            if (String.IsNullOrEmpty(ffmpegPath)) {
+                log.ErrorFormat("FFmpeg was not found at '{0}'.", ffmpegPath);
+                throw new Exception("Path to ffmpeg not specified. Set ffmpeg path (excluding exe) before attempting to use SharpFF.");
+            }
+            Process p = new Process();
+            p.StartInfo.FileName = Path.Combine(ffmpegPath, "ffmpeg");
+            p.StartInfo.Arguments = parameters;
+            p.StartInfo.UseShellExecute = false;
+            p.Start();
+            // this could potentially be speeded up by not waiting, but then we end up trying to delete files before ffmpeg
+            // is actually finished
+            p.WaitForExit();
+        }
+
+        public static decimal GetDuration(string videoPath) {
+            if (String.IsNullOrEmpty(ffmpegPath)) {
+                log.ErrorFormat("FFmpeg was not found at '{0}'.", ffmpegPath);
+                throw new Exception("Path to ffmpeg not specified. Set ffmpeg path (excluding exe) before attempting to use SharpFF.");
+            }
+            Process p = new Process();
+            p.StartInfo.FileName = Path.Combine(ffmpegPath, "ffmpeg");
+            p.StartInfo.Arguments = String.Format("-hide_banner -i \"{0}\"", videoPath);
+            p.StartInfo.RedirectStandardError = true;
+            p.StartInfo.UseShellExecute = false;
+            p.Start();
+            string stderr = p.StandardError.ReadToEnd();
+            List<string> outLines = stderr.Split(System.Environment.NewLine[0])
+                .Where(line => string.IsNullOrWhiteSpace(line) == false && string.IsNullOrEmpty(line.Trim()) == false)
+                .Select(s => s.Trim()).ToList();
+
+            foreach (var line in outLines) {
+                if (line.StartsWith("Duration")) {
+                    string timecode = line.Split(' ')[1].Trim();
+                    return TimecodeToSeconds(timecode);
+                }
+            }
+            p.WaitForExit();
+            return 0.0M;
+        }
+
+        public static decimal TimecodeToSeconds(string timecode) {
+            timecode = timecode.Replace(",", String.Empty);
+
+            decimal totalSeconds = 0;
+            string[] timeElements = timecode.Split(':');
+            totalSeconds += Int32.Parse(timeElements[0]) * 3600;
+            totalSeconds += Int32.Parse(timeElements[1]) * 60;
+            totalSeconds += Math.Floor(decimal.Parse(timeElements[2]) * 100) / 100;
+            return totalSeconds;
+        }
+
+        public static string SecondsToTimecode(object seconds) {
+            decimal betterSeconds = System.Convert.ToDecimal(seconds);
+            decimal hours = Math.Floor(betterSeconds / 3600);
+            if (hours >= 1) {
+                betterSeconds -= hours * 3600;
+            }
+            decimal minutes = Math.Floor(betterSeconds / 60);
+            if (minutes >= 1) {
+                betterSeconds -= minutes * 60;
+            }
+            return String.Format("{0:00}:{1:00}:{2:00.00}", hours, minutes, Math.Floor(betterSeconds * 100) / 100);
+        }
+    }
+}

+ 5 - 0
MovieBarcodeGenerator/packages.config

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="log4net" version="2.0.4" targetFramework="net45" />
+  <package id="NDesk.Options" version="0.2.1" targetFramework="net45" />
+</packages>

+ 36 - 0
UnitTestProject1/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("UnitTestProject1")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("UnitTestProject1")]
+[assembly: AssemblyCopyright("Copyright ©  2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("701ba4b1-5ae7-40d6-94ce-e4225895160b")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 39 - 0
UnitTestProject1/TimecodeTests.cs

@@ -0,0 +1,39 @@
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using MovieBarcodeGenerator;
+
+namespace UnitTests {
+
+    [TestClass]
+    public class TimecodeTests {
+
+        [TestMethod]
+        public void Test_SecondsToTimecode() {
+            Assert.AreEqual("00:00:20.00", SharpFF.SecondsToTimecode(20));
+
+            Assert.AreEqual("00:02:05.00", SharpFF.SecondsToTimecode(125));
+
+            Assert.AreEqual("00:31:53.00", SharpFF.SecondsToTimecode(1913));
+
+            Assert.AreEqual("01:02:05.00", SharpFF.SecondsToTimecode(3725));
+
+            Assert.AreEqual("00:00:35.47", SharpFF.SecondsToTimecode(35.47M));
+
+            Assert.AreEqual("00:00:35.64", SharpFF.SecondsToTimecode(35.649M));
+        }
+
+        [TestMethod]
+        public void Test_TimecodeToSeconds() {
+            Assert.AreEqual(20M, SharpFF.TimecodeToSeconds("00:00:20.00"));
+
+            Assert.AreEqual(125M, SharpFF.TimecodeToSeconds("00:02:05.00"));
+
+            Assert.AreEqual(3725M, SharpFF.TimecodeToSeconds("01:02:05.00"));
+
+            Assert.AreEqual(35.47M, SharpFF.TimecodeToSeconds("00:00:35.47"));
+
+            Assert.AreEqual(35.64M, SharpFF.TimecodeToSeconds("00:00:35.649"));
+        }
+
+    }
+}

+ 89 - 0
UnitTestProject1/UnitTests.csproj

@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{EA169873-EADE-4ED2-B87A-80E622231D0C}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>UnitTestProject1</RootNamespace>
+    <AssemblyName>UnitTestProject1</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
+    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+    <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
+    <IsCodedUITest>False</IsCodedUITest>
+    <TestProjectType>UnitTest</TestProjectType>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+  </ItemGroup>
+  <Choose>
+    <When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
+      <ItemGroup>
+        <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+      </ItemGroup>
+    </When>
+    <Otherwise>
+      <ItemGroup>
+        <Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" />
+      </ItemGroup>
+    </Otherwise>
+  </Choose>
+  <ItemGroup>
+    <Compile Include="TimecodeTests.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\MovieBarcodeGenerator\MovieBarcodeGenerator.csproj">
+      <Project>{97b87120-43c1-4d85-8d95-fcf56b5fa0f8}</Project>
+      <Name>MovieBarcodeGenerator</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Choose>
+    <When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
+      <ItemGroup>
+        <Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+          <Private>False</Private>
+        </Reference>
+        <Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+          <Private>False</Private>
+        </Reference>
+        <Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+          <Private>False</Private>
+        </Reference>
+        <Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+          <Private>False</Private>
+        </Reference>
+      </ItemGroup>
+    </When>
+  </Choose>
+  <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>