Tuesday, October 7, 2008

Friday, September 19, 2008

Code Review Vol. 7 - Automated JS Loading, Reformatting and Injecting with ActionScript 3.0

Based on my previous post I then abstracted a bit further. Instead of having the JavaScript embedded directly in the ActionScript I read it in, reformat it to fit my needs and then inject it into the page. I use the same instance for all file loadings so I don't store any of the values in class properties and I make my own asynchronous token pattern since it's pure AS3 and not Flex. I'll do a full walk through at a later date.


public class JSInjector extends EventDispatcher
{
public function JSInjector()
{}


public function loadExternalJS(url:String):void
{
readFile(url);
}


public function readFile(url:String):void
{
var ldr:ASyncTokenURLLoader = new ASyncTokenURLLoader()
ldr.ASyncToken = url
ldr.addEventListener(Event.COMPLETE , loadComplete,false,0,true);
ldr.addEventListener(IOErrorEvent.IO_ERROR, jsloadError,false,0,true);

var urlReq:URLRequest = new URLRequest(url);
ldr.load(urlReq)
}

private function loadComplete(event:Event):void
{
var e:AsynTokenEvent = new AsynTokenEvent(AsynTokenEvent.ASYNC_LOADED)
e.token = event.target.ASyncToken
dispatchEvent(e);
formatJS(event.target.data)
}

private function jsloadError(e:IOErrorEvent):void
{

}

private function formatJS(js:String):void
{
js = formatFunctionDefinitions(js);
js = formatGlobalVarDefinition(js)
js = js.split("var ").join(" ")
injectJS(js)
}

private function formatGlobalVarDefinition(js:String):String
{
var gvarSCPattern:RegExp =/var\s\w+\s?[^=](?=;\r|\n)/gixm
js = js.replace(gvarSCPattern, '$& = ""')
return js
}

private function formatFunctionDefinitions(js:String):String
{

var functionPattern:RegExp =/function[\s?](\w+)/gim
var newFunctionSource:String = js.replace(functionPattern, "$1 = function")
return newFunctionSource
}

private function injectJS(js:String):void
{
var externalJS:String = "function(){ "+js+" }";

try{
ExternalInterface.call(externalJS);
}catch(e:Error){

}
}

Monday, September 8, 2008

Code Review Vol. 6 - Injecting JavaScript from ActionScript

A project that I have been working on at my new company - which I will post about soon, I've just been waiting to take pictures so I can document how incredible the facilities are - is an ActionScript video player that allows us to play videos from multiple sources, including videos from vendors who require us to load in their player for the actual video playback. The external players, of course, require JavaScript to interact with them. In order to make my player self contained I needed a way to source the JavaScript directly from the ActionScript - this player is to used in a multitude of applications and projects company-wide and I couldn't guarantee that each project would have these necessary .js files.

I found this excellent article by Peter McBride - JavaScript and VBScript Injection in ActionScript 3

I based my solution off of the methodology in the article and it worked beautifully.

I created several AS classes, each with static public functions that could be invoked to load their respective JS into memory. Like the article describes, anonymous functions assigned to variables.


import flash.external.ExternalInterface;

public class JSONInjector
{
public function JSONInjector()
{
}

static public function registerPlayer():void
{
var js:XML = <script>
<![CDATA[
function()
{
JSPLAYER = new Object()
JSPLAYER.obs = {}; // Observers
JSPLAYER.addListener = function( eventType, callbackObj, callbackFunc){ .. }


Rsize = function(divID, height, width)
{
var element = document.getElementById(divID);
element.style.height = height + "px";
element.style.width = width + "px";
}

OpenNewWindow = function(URLtoOpen, windowName, windowFeatures)
{
var newWindow=window.open(URLtoOpen, windowName, windowFeatures);
}

...
}
]]>
</script>
try{
ExternalInterface.call(js);
}catch(e:Error){}

}



Then within the init of the main application I simply called the functions:


JSONInjector.registerContainer()
JSONInjector.registerPlayer()


And within my external interface files, I simply called the JS objects through external interface:


ExternalInterface.call("JSPLAYER.pause",true)


It's as simple as that, and now the need for any peripheral .js files has been negated and we have a self-contained solution for all video playback.

Wednesday, September 3, 2008

New lesson plans

Over the long weekend, in between visiting my grandparents and hitting the shore, I wrote a few new lesson plans. One of the classes I'm teaching this semester is the Scripting Languages class that I re-worked. Previously the class was a VBA class, but I determined that VBA was out of place with the rest of the curriculum. I re-wrote it to cover PHP and .Net. But lately I'd been feeling unsatisfied by the class. It seemed that there really wasn't enough time in one semester to really get as far into each topic as I would like; including both only really allowed a brief primer of each, but I wanted more. So last year I changed it up and concentrated on PHP and it's interaction with MySQL. The results of that were good - except it became almost indistinguishable from my Database Management and Scripting class - which is in PHP and MySQL.

So this year I was determined to give the class an identity of its own while continuing to focus on PHP. This past weekend I got around to it. I wrote up one lesson plan purely as a primer to web services, what they are, how they are used, with examples to several real world ones - specifically last.fm and amazon's. I then concentrate one week going over SOAP and PHP and then finally another week on REST and PHP. I need to create some labs around those topics, and then a multi-week assignment and that will take up the last quarter of the semester.

I'm pretty happy with the results. The next thing I want to do is add some more JavaScript lessons to my HTML/JavaScript class. The class was originally written assuming no prior experience with either technology, but the past few years I've seen the students are all at least comfortable with HTML, so I feel that I can de-emphasize that part and maybe write some more advance topics in JavaScript. I'd love to cover some JSON or Dojo. Hopefully I can work that in this semester as well.

Wednesday, August 27, 2008

Code Review Vol. 5 - DRM Profile Generator in VB.Net

Here is a follow up to the Windows Media Encoder and DRM script I posted previously. I mentioned in the article that I had to export a DRM profile from the DRM server, but I never really elaborated on that. Here is the script that I wrote that I ran on the server which generated the DRM profile.


Imports WMEncoderLib
Imports WMRMOBJSLib
Imports System.IO
Imports System.Xml

Public Class Generate
Shared Sub Main()
Dim profValue
CreateProfile()
End Sub

Shared Sub CreateProfile()
' Create a WMEncoder object.
Dim Encoder As WMEncoder
Encoder = New WMEncoder

' Create an IWMDRMContentAuthor object.
Dim DRM As IWMDRMContentAuthor
DRM = Encoder.EncoderDRMContentAuthor

' Retrieve the collection of DRM profiles.
Dim DRMProColl As IWMDRMProfileCollection
Dim KeyObj = New WMRMKeys


DRMProColl = DRM.DRMProfileCollection

' Declare variables. Specify the provider's Web site, and certificate and
' signing values. The other variables are returned.
Dim sPublicKey As String
Dim sWebURL As String
Dim vProfileID As Object
Dim vSeed As Object
Dim sPrivateKey As String
Dim sSignedPublicKey As String
Dim sSigLSCert As String
Dim sSigRootCert As String
Dim sProfilePublicKey As String

Dim sContentID As String

sContentID = KeyObj.GenerateKeyID()

KeyObj.GenerateSigningKeysEx(sPrivateKey, sPublicKey, sSignedPublicKey)

sWebURL = "http://license3.musicchoice.com/XXXX.asp"

sSigLSCert = KeyObj.GetCertificate("LSLicenseSigningCert")
sSigRootCert = KeyObj.GetCertificate("LSRootCert")

' Create the DRM profile. The public key, DRM profile ID, and
' license key seed are returned.
sProfilePublicKey = DRM.CreateDRMProfile(sWebURL, sPrivateKey, sSignedPublicKey, sSigLSCert, sSigRootCert, vProfileID, vSeed)

' Create an IWMDRMProfile object and retrieve a profile.
Dim DRMPro As IWMDRMProfile
DRMPro = DRM.GetDRMProfile(vProfileID)

' Set the rest of the properties for the DRM profile.
DRMPro.Description = "DRM for MC portable content"
DRMPro.LicenseAcquisitionURL = "http://license3.musicchoice.com/XXXX.asp"
DRMPro.Name = "MC Portable DRM"
DRMPro.V1LicenseAcquisitionURL = "http://go.microsoft.com/fwlink/?LinkId=10141"

' Add the required individualization version as an attribute.
Dim ProAttr As IWMDRMAttributes
ProAttr = DRMPro.Attributes
ProAttr.Add("SECURITYVERSION", "2.2")

ProAttr.Add("ContentID", sContentID)

Dim sDetails As String

Dim oXMLWriter As New XmlTextWriter("C:\Inetpub\wwwroot\wm\IssuedDetails.xml", System.Text.Encoding.UTF8)

oXMLWriter.WriteStartElement("Details")
oXMLWriter.WriteStartElement("Detail")

oXMLWriter.WriteAttributeString("ContentID", sContentID)
oXMLWriter.WriteAttributeString("Seed", vSeed)
oXMLWriter.WriteAttributeString("Key", sProfilePublicKey)
oXMLWriter.WriteAttributeString("PrivateKey", sPrivateKey)


oXMLWriter.WriteEndElement()
oXMLWriter.WriteEndElement()

oXMLWriter.Close()

DRMPro.Update()

DRM.ExportDRMProfile(DRMPro.ID, "password", "C:\Inetpub\wwwroot\wm\ExportProfile.drm")

End Sub

Shared Sub SaveInfo(ByVal nSeed As String, ByVal nPath As String)
Dim objStreamWriter As StreamWriter
objStreamWriter = New StreamWriter(nPath)
objStreamWriter.WriteLine(nSeed)
objStreamWriter.Close()


End Sub

End Class

Saturday, August 23, 2008

Code Review Vol. 4 - Perl Recursive Directory Stepper

Here is a recursive directory browser I wrote in Perl. I later decorated it a bit and used it for a project back at Toll Brothers. The goal was to create a shared directory for the IT Training department, where trainers for each department could store their respective white papers without the admin for IT Training having to manage those documents.

So this script simply reads the contents of the shared directory and recursively steps through all folders inside and lists out all documents.


#!/usr/bin/perl

use DBI;
use CGI;


my $q = new CGI;
my $logFile = "exceptions.txt";
my $showDir= "/EASYT/TRAINING/manuals";
print $q->header( "text/html" );
peruseDir($showDir, '0');

exit(0);

sub peruseDir{
#create local copy of parameters
#second parameter of which is an indirect file handle
#so we can keep making recursive calls to peruseDir
#increment the indirect fhandle as we go so that we
#arent stuck on the same file through each iteration
local($currDir, $input) = @_;
$input++;
if($currDir !~ m/.*\..*/){
opendir($input, $currDir) || die print "Can't open $currDir $!
\n\n";
my @tempArr = grep { $_ ne '.' and $_ ne '..' } readdir($input);
foreach $myDir(@tempArr){
if(-d $currDir."/".$myDir){
#if its a directory make another recursive step
print "$input $myDir
";
peruseDir($currDir."/".$myDir, $input);
}else{
#otherwise its a file, show file
my $linkDir = $currDir;
$linkDir =~ s/\/EASYT\/TRAINING\///;
print "$myDir
";
}
}
closedir($input);
}
}


sub logExceptions{
open(LOG, ">> $logFile") || die print "Couldn't write to log $!";
foreach $exception(@_){
print LOG "$exception\n";
}
close(LOG);
}

Friday, August 22, 2008

Code Review Vol. 3.01 - WMEncode and DRM Walkthrough


Imports WMEncoderLib
Imports System.Windows.Forms
Imports System.IO


Module Module1
Dim WithEvents Encoder As WMEncoder
Dim glbQuitBool As Boolean = False
Dim baseURL As String
Dim DRMProfilePass As String = "XXX"
Dim DRMProfile As String = "ExportProfile.drm"


Sub Main()
Init()
SearchDir(baseURL)
End Sub

Sub Init()
Dim sr As StreamReader = New StreamReader("baseLoc.ini")
Dim line As String
line = sr.ReadLine()
sr.Close()
baseURL = line
End Sub

Function DoEncode(ByVal sourceFile As String, ByVal outputFile As String, ByVal thisProfile As WMEncProfile2, ByVal fileType As String) As Boolean
Try
Encoder = New WMEncoder
Dim SrcGrpColl As IWMEncSourceGroupCollection = Encoder.SourceGroupCollection
Dim SrcGrp As IWMEncSourceGroup = SrcGrpColl.Add("SG_1")
Dim newFile As IWMEncFile = Encoder.File

If fileType = "video" Then
Dim vidFile As IWMEncVideoSource = SrcGrp.AddSource(2)
vidFile.SetInput(sourceFile)
End If


Dim audFile As IWMEncAudioSource = SrcGrp.AddSource(1)



Up until this point it's fairly standard WMEncoder SDK code - create a sourcegroup collection, create a sourcegroup, and add a video or audio source to the source group - but here we introduce the DRM element. It follows the same template



Dim DRM As IWMDRMContentAuthor
Dim DRMProColl As IWMDRMProfileCollection
Dim tempDRMPro As IWMDRMProfile

DRM = Encoder.EncoderDRMContentAuthor

' Retrieve the collection of DRM profiles.
DRMProColl = DRM.DRMProfileCollection

'First remove any DRM Profiles that may still linger
tempDRMPro = DRMProColl.Item(0)
DRM.RemoveDRMProfile(tempDRMPro.ID)

' Import a DRM profile.
DRM.ImportDRMProfile(DRMProfilePass, DRMProfile)

DRMProColl = DRM.DRMProfileCollection
Dim DRMPro As IWMDRMProfile
DRMPro = DRMProColl.Item(0)



Essentially the DRM set up follows the same format as the Encoder, create a ProfileCollection, create a profile object which references the literal .drm profile file that I had exported from the DRM server. A DRMContentAuthor is pulled from the base WMEncoder object and everything is imported into that.



' Set the current DRM profile into the encoding session.
Dim vKeyID As Object = "XXXXX
DRM.SetSessionDRMProfile(DRMPro.ID, vKeyID)

Dim newProfile As WMEncProfile2 = New WMEncProfile2
audFile.SetInput(sourceFile)
newFile.LocalFileName = outputFile
newProfile = thisProfile

' newProfile.LoadFromFile(thisProfile)
SrcGrp.Profile = newProfile
Encoder.AutoStop = True
Encoder.RemoteAdmin = True
Encoder.PrepareToEncode(True)
Encoder.Start()

Dim x As Integer



For the longest time the code didn't work. Until I found out that I needed to put a slight wait before encoding. And even while its encoding down below inside the while loop it still needs a pause between each iteration to register events and change states when necessary.



'HACK - needs this to actually work, bug in WM SDK
For x = 0 To 30
Application.DoEvents()
x = x + 1
Next


While Encoder.RunState = WMENC_ENCODER_STATE.WMENC_ENCODER_RUNNING
Dim temp = Encoder.RunState
Dim y As Integer
For y = 0 To 300
y = y + 1
Next
Application.DoEvents()
End While

Encoder.Stop()
Return True

Catch ex As Exception
Console.WriteLine(ex)
Dim errFile As String = outputFile + ".error"
File.Create(errFile)
File.Delete(outputFile)
Return False
End Try

Encoder = Nothing

End Function



Here I grab the .drm file from the filesystem and load it into the WMEncProfile2 object.



Function RetrieveProfile(ByVal nFile) As WMEncProfile2
Try
Dim BasicEdit As WMEncBasicEdit
BasicEdit = New WMEncBasicEdit

' Specify the input, output, and configuration files.
BasicEdit.MediaFile = nFile

' Retrieve the duration of the input file, in seconds.
Dim lFileDur As Long
lFileDur = BasicEdit.Duration / 1000

' Retrieve the profile used by the input file.
Dim Pro As WMEncProfile2
Pro = BasicEdit.Profile

Return Pro
Catch ex As Exception
Console.WriteLine(ex)
End Try


End Function




And here is the engine - I essentially read the directory that was set aside as the publishing directory, where all raw files to be encoded and distributed were deposited, and encode and drm them.



Sub SearchDir(ByVal dir)
'Environment.CurrentDirectory
Dim di As New DirectoryInfo(dir)
' Create an array representing the files in the current directory.
Dim fi As FileInfo() = di.GetFiles()
Dim tempFile As FileInfo
For Each tempFile In fi
Dim currentProfile As WMEncProfile2
Dim currentSourceFile As String = baseURL + "\" + tempFile.Name
Dim currentOutputFile As String = baseURL + "\DRMed\" + tempFile.Name
Dim currentFType As String
Dim currentFTypeArr = Split(tempFile.Name, ".")
If currentFTypeArr(1) = "wma" Then
currentFType = "audio"
ElseIf currentFTypeArr(1) = "wmv" Then
currentFType = "video"
End If

currentProfile = RetrieveProfile(currentSourceFile)
DoEncode(currentSourceFile, currentOutputFile, currentProfile, currentFType)
Next tempFile

End Sub


End Module


And that's it. Pretty straightforward, but by automating the encoding process and combining it with the DRMing process I was anble to integrate that into the automated workflow that I was building as the infrastructure to the overall Music Choice broadband product.