Archiv für den Monat März 2015

MP3s zusammenführen / mergen mit dem copy Befehl und der csid3lib


Manchmal sind es die kleinen Dinge über die man sich als Softwareentwickler freut. So z.B. am letzten Wochenende an dem ich beschlossen hatte ein paar alte Hörspiele die immer auf mehrere MP3 Dateien aufgeteilt waren zu einer Datei zusammenzufügen und zumindest den Titel und das Album entsprechend einzutragen, denn der Ordnername in dem sich die Dateien befanden war einheitlich benannt aber die Dateien darin meist nur mit 01 – XX. Da es sich bei meinem Vorhaben nicht nur um ein paar Hörspiele handelte habe ich im Netz eigentlich nach einer Library gesucht mit der ich MP3 Dateien zusammenführen kann oder nach einem passenden Programm.

Ein Programm findet man schnell, nur muss man hier meist alles per Hand einstellen und dafür waren es mir dann doch zu viele Ordner. Dann habe ich es fast nicht glauben können das man mit der normalen Windows Kommandozeile und dem “copy” Befehl auch MP3 Dateien zusammenfügen kann.

copy /b “c:\Temp1.mp3” + “c:\Temp2.mp3” + “c:\Temp3.mp3” “c:\Temp\merged.mp3”

Bitte beachten das die “+” Zeichen auch benötigt werden, denn hier wird angegeben welche Dateien zusammengeführt werden sollen und beim letzten Pfad wird kein “+” mehr verwendet denn hier steht die Zieldatei die erstellt werden soll.

Aufgrund des einfachen Befehls gepaart mit einer Library (csid3lib) mit der man MP3 Tags setzen kann und einem Konsolenprogramm ging es dann sehr schnell um alle Hörspiele entsprechend zusammenzufügen. Der Download für die csid3lib enthält direkt eine fertige SLN die man mit VS öffnen kann und hier habe ich einfach ein Konsolenprogramm hinzugefügt mit dem folgenden Codeschnipsel.

  foreach (string directory in System.IO.Directory.GetDirectories("c:\\Temp\\Hoerspiele"))
            {
                string files = string.Empty;
                DirectoryInfo directoryInfo = new DirectoryInfo(directory);
                foreach (string file in System.IO.Directory.GetFiles(directory))
                {
                    FileInfo info = new FileInfo(file);
                    files += "\"" + file + "\" + ";
                }

                files = files.Trim().TrimEnd('+');
                string newFile = "c:\\Temp\\Merged\\" + directoryInfo.Name + ".mp3";

                strCmdText = "copy /b " + files + " \"c:\\Temp\\Merged\\" + directoryInfo.Name + ".mp3\"";
                System.Diagnostics.ProcessStartInfo procStartInfo =
                    new System.Diagnostics.ProcessStartInfo("cmd", "/c " + strCmdText);

                procStartInfo.RedirectStandardOutput = true;
                procStartInfo.UseShellExecute = false;
                procStartInfo.CreateNoWindow = true;
                System.Diagnostics.Process proc = new System.Diagnostics.Process();
                proc.StartInfo = procStartInfo;
                proc.Start();
                string result = proc.StandardOutput.ReadToEnd();
                Console.WriteLine(result);

                var mp3File = new Mp3File(newFile);
                mp3File.TagHandler.Artist = "SelfRipped";
                mp3File.TagHandler.Title = directoryInfo.Name;
                mp3File.TagHandler.Album = directoryInfo.Name;
                mp3File.Update();
            }

Vielleicht nicht “schön” aber dafür sehr funktionell und für die einmalige Verwendung sehr gut geeignet und nicht overengineered.

ACHTUNG: Auf jeden fall manuell überprüfen ob das zusammenfügen auch geklappt hat, d.h. ob die Gesamtlänge des Titels stimmt. Denn es wird leider kein Fehler angezeigt wenn mit Copy etwas nicht “geklappt” hat sondern dann ist die Datei zwar 130MB groß aber nur 3 Minuten lang statt 50 Minuten. Ist bei mir bei einigen Dateien vorgekommen, diese habe ich dann manuell mit einem Programm mergen müssen.

Unit Test AngularJS directive mit templateUrl in VS und Chutzpah


Damit meine eigenen Direktiven übersichtlich bleiben, lege ich die Templates immer in einer extra HTML Datei ab und verwende entsprechend die “templateUrl” in meinen Direktiven.

Leider ist das Füllen des AngularJS Templatecaches mit Chutzpah ein klein wenig umständlicher als wenn man Karma und dem ng-html2js Präprozessor zum füllen des AngularJS Templatecaches verwendet. Da ich bei meiner Testumgebung aber nicht mehr Tools als notwendig einsetzen möchte und ich aktuell Chutzpah verwende, stelle ich im Folgenden einen möglichen Lösungsweg vor.

(Wie man ChutzPah in VS verwendet habe ich bereit hier erläutert.)

Als Grundlage verwende ich eine einfache Direktive mit TemplateUrl

angular.module("testTemplateDirective", [])
    .directive("testTemplate", function() {
        return {
            restrict: 'A',
            replace: true,
            templateUrl: 'ScriptsApp/directives/Templates/testTemplate.html'
        }
});

dazu gehört das folgende HTML Template “testTemplate.html” im Root Element muss eine eindeutige CSS Klasse hinterlegt werden die das Template identifiziert (leider notwendig für die Unit Tests).

<div class="row templateTestTemplate">
    <div class="col-lg-12">
        <h1>Tempalte Test</h1>
    </div>
</div>

Dazu muss man wissen das ChutzPah die Möglichkeit bietet neben den Referenzen zu den Script Dateien im Header des Tests ebenfalls den Pfad zu “templates” anzugeben mit einem “template” Tag.

/// <template path="../../MvcAngularJs1_3/ScriptsApp/directives/Templates/testTemplate.html" />
/// <reference path="../../mvcangularjs1_3/scripts/jquery-2.1.1.js" />
/// <reference path="../../mvcangularjs1_3/scripts/angular.js" />
/// <reference path="../../mvcangularjs1_3/scripts/angular-mocks.js" />
/// <reference path="../../mvcangularjs1_3/scriptsapp/directives/testtemplatedirective.js" />

... Unit Test

Der Komplette Inhalt des Templates wird dann in der HTML Testdatei eingefügt und kann über jQuery und unserer eindeutigen CSS Klasse ermittelt werden.

Da AngularJS immer erst prüft ob das Template bereits im TemplateCache enthalten ist, müssen wir unser Template nur noch manuell dem TemplateCache hinzufügen. Als Key wird hier die aufzurufende TemplateUrl verwendet. In meinem Beispiel wird der Key “ScriptsApp/directives/Templates/testTemplate.html” von AngularJS verwendet.

 var template = jQuery(".templateTestTemplate")[0].outerHTML;
 $templateCache.put("ScriptsApp/directives/Templates/testTemplate.html", template);

Wenn das Template einmal dem TemplateCache hinzugefügt wurde, ist der Rest des Unit Tests für Direktiven so wie jeder andere Unit Tests für Direktiven.

ACHTUNG: Der Unit Tests ist nur erfolgreich, wenn der Test mit ChutzPah ausgeführt wird. Wenn der Unit Test mit Resharper ausgeführt wird, dann schlägt der Unit Test fehl, denn Resharper kennt das “template” Tag nicht und fügt damit auch keinen HTML Code in der Testdatei ein wie ChutzPah.

Der komplette Test sieht dann folgendermaßen aus

/// <template path="../../MvcAngularJs1_3/ScriptsApp/directives/Templates/testTemplate.html" />
/// <reference path="../../mvcangularjs1_3/scripts/jquery-2.1.1.js" />
/// <reference path="../../mvcangularjs1_3/scripts/angular.js" />
/// <reference path="../../mvcangularjs1_3/scripts/angular-mocks.js" />
/// <reference path="../../mvcangularjs1_3/scriptsapp/directives/testtemplatedirective.js" />

var siteRoot = "";

angular.module("app.testtmplate", ["testTemplateDirective"]);

describe("Unit Tests für die Direktive 'testTemplateDirective' ", function() {
    var $rootScope, $compile;

   beforeEach(function() {
        //Unsere Main App laden
        module('app.testtmplate');
        inject(function(_$rootScope_, _$compile_) {
            $rootScope = _$rootScope_;
            $compile = _$compile_;
        });
    });

    describe('Tests für Direktive "testTemplateDirective"', function () {
        beforeEach(function () {
            inject(function ($templateCache) {
                //mit jQuery unseren HTML Code inkl. des Selectors ermitteln.
                var template = jQuery(".templateTestTemplate")[0].outerHTML;
                $templateCache.put("ScriptsApp/directives/Templates/testTemplate.html", template);
            });
        });

        it('Es wurde ein H1 mit dem Text "Template Test" angelegt', function () {
            var element = $compile("<div><div test-template></div>")($rootScope);
            $rootScope.$digest();

            expect(element.html()).toContain("<h1>Tempalte Test</h1>");
            expect(element.find("h1").length).toBe(1);
        });
    });
})