The Power of One Kind Word

I had a goal as high school student: become a published writer. One of the my earliest experiences in this process of getting published was the receipt of rejection letters. It seemed like every idea I came up with was rejected out of hand and my PO BOX was full of these rejection letters.

Yes we did this all via the post office and the wait from send to end seemed to take forever. After a huge number of these rejections, I received a rejection that changed EVERYTHING! The next letter was a life changer. Check it out….

Picture of a rejection letter from DRAGON magazine.

Yes, it was another rejection letter, but this time it had a personally written note that made all the difference in the world…

“Good idea, but someone else had the same idea first. Don’t be discouraged you have talent.

Roger E. Moore”

Getting a personal note from the editor of DRAGON magazine was an accomplishment in an of itself..

A comment saying “you have talent” was all the fuel that 17-year-old me needed to hear. This statement encouraged me to proceed writing for the rest of my life.

All it took was ONE KIND STATEMENT.

So remember when you are dealing people sometimes all it takes is one kind word to change a life.

Top 10 Favorite Films of 2022

This movie defines what a cinema experience should be. If you have the chance catch it in a theatre (there are screenings from time to time) Do not let the run time scare you the three hours goes quickly.

It was difficult to place this one in the #2 spot. This is a movie that defies description. I saw it at SXSW “cold” and you should too. An amazing cast, story and concept this is a movie not to be missed.

This film starting the amazing Joey King was one of my most recommended this year. If you want to see a film that kicks ass action from start to finish this one is it.

Another film I watched completely cold and I am happy about that. Happy I caught this in the theatre.

Tilda Winton AND Idris Elba in a lovely little film with a big concept and a huge heart. Loved it.

This movie was probably the biggest surprise to me. I took this film as a junky throw away film when I saw the trailer. What I was presented with was a surprising story that delves deep in to our humanity. A touching film.

When I saw the trailer for this film I knew it was one that we needed to watch as a family. My son in law is a huge NASA nut and it was especially important to watch it with him. We were not disappointed. And I cried during a movie about a freaking mars rover! Watch it and you’ll see why.

I am a huge documentary nut and love learning about subjects that have no familiarity with. This was one of those. You will learn about volcanoes and the human quest for knowledge.

This movie was a pure popcorn film. A fun action romp with a great cast and some stellar performances. Pure pleasure!

Huge horror fan here and this one delivers!

What You Put Into The World Is What You Receive

My blog post A Positive End To A Shitty Week initiated several conversations in my house as well as online. One conversation happened via text message and went something like this:

“I love that blog post. I am on a mission to find JOY. Your picture grabbed me. So glad you listed to your gut and went. Love the JOY. That word is so powerful. Not so simple actually”.

This was just one of the conversations and it sparked so many thoughts.

My wife Jessica is fond of saying

“What you put out into the world is what you receive.”

This conversation happens frequently when I “Road Rage” while driving from point to point. When I am in a hurry or just frustrated with traffic, my raging doesn’t have any real effect. It simply makes me rage event more. If I put out rage, I get rage in return, the rage fuels itself. I contrast this with when Jess and I head out for leisurely “drives in the country. ” These drives have no real destination or schedule and I find my self at peace when on these trips. Road rage be gone! I have been using this technique when driving now. I try and drive with peace, calm and understanding. This makes my these trips more pleasant and calm for everyone involved.

There are more experiences that come to mind when thinking about what vibes I put out in the world. When I started as editor in chief of Code Magazine I tended to write my editorials with a good dose of snark. That was until my good friend and publisher Markus Egger IM’d me and made a recommendation that my editorials should take a more positive tone vs the snarky ones I was cranking out. I took this to heart and have written some 100+ editorials since, all the while trying to put positive energy into the world. What I have now is a legacy of positive material that is in the world permanently. Which leads me to another story that has stuck with me.

My friend Scott Hanselman has been blogging for 20+ years and one particular post has stuck with me: Don’t Give Bile A Permalink This post fits perfectly with my theme for this post and has some very valuable points of discussion. The blog post is lengthy and worth a read, but it’s the headline that stuck out the most. When writing (or posting online) do you want to leave a mark of negativity in the world (bile) or leave a positive mark? I am in the later category. I can say without reservation that Scott definitely walks his talk! If you read his posts, watch his videos or see him speak, you can see that he exerts positive energy into the world. His is an example to live by and a beautiful legacy

Now let’s contrast this with some of the other energies being put out into the world. There is an entire industry built around spreading rage into the world. This industry is 24-7-365 and is relentless in turning rage and anger into cash and clicks. I have good friends on social media that I must mute/ignore because their entire being is spreading rage and frustration via with memes, reposts and retweets. These items do nothing to move conversations forward or add positive energy into the world. These people are pissed for the sake of being pissed this is something that choose NOT to participate in. I choose to be positive.  They have a choice too. Heck maybe they will read this and will reflect on what they put out into the world. One can only hope!

The last few weeks have been a time of reflection and introspection for me. I was feeling burned out and miserable and decided I needed to make some changes. One of these changes was to restart my dedication to doing Morning Pages . For one reason of another I stopped doing this regularly and this had a negative impact on my mentality. Not doing this was affecting my: work, life, and creativity. A few weeks back I began anew and the positivity that it brought for so long returned. One of the most positive affects was that it got me writing again which is something I greatly enjoy and sorely missed.

There are other behaviors I am reincorporating as well. One is to refocus on my health. I try and get a 1-3 mile walk in daily. Getting out of the house is good for my mental state even if its only for a short duration. I am always surprised at that. It works so I’ll keep on that 🙂

I am also focusing more of my energy on writing, both professionally and personally. I have been a writer for most of my life and when I am not writing I feel like a bit lost. I have lot to say in case you haven’t guessed. So I have been writing more and more. I have been blogging, writing articles, working on screenplays, and have just started on another book. This is not to toot my own horn but to illustrate how pursuing your own interests can bring joy to your own soul as well as others. Hopefully like this blog post.

I hope this post find you well as we wrap up 2022 and that maybe it brings you a small amount of joy. Or better yet that it brings you joy that you can share with the world. We can all use more!

The Decline and Fall of the Theatrical Experience

The theatrical movie going experience as we know it is dead, and my theatrical movie going behavior is helping to seal its fate. I am a 100% died in the wool movie lover and personally enjoy watching movies in theatres. The problem is that I don’t go to the movies nearly as much as I used to, and I cannot see this changing any time in the foreseeable future. Let me tell you why…

Today I have a basic internal calculus I use to determine whether I see a movie in a theater vs waiting to see it on a streaming platform. The first part of this calculus begins with the question:” Is this a movie that is best enjoyed on the big screen?”  I guess what I really mean is: “Is this a SPECTACLE movie?” Examples of movies that fit this bill, and that I watched in theatres in 2022, are: Top Gun: Maverick, Bullet Train, Black Panther: Wakanda and finally in a few days the new Avatar sequel. In my mind each these films represent the type of spectacle films that I will show up for.

The second criteria in my theatrical vs streaming calculus resides in this question: “How long am I going to need to wait before I can watch this film at home?” For The Fablemans the answer is clear. Not too long!  The Fablemans was released “wide” theatrically few short weeks ago and is available to stream at home NOW, albeit with a $20.00 rental fee.  What I also know is in around 30 days this film will show up on one of my paid services for “free” and that is the most likely time I will view it.  Let me say this… If you told me that I wouldn’t rush to see a new STEVEN SPIELBERG film opening weekend I would tell you that you were crazy, plain, and simple. 

It is the second part of my calculus that is contributing to the decline of movie attendance for “smaller” films. You see the entertainment industry has TRAINED its consumers to be more particular about what types of films they go out to see. Do you really want to spend north of $50.00 to see a film (popcorn and soda included in this price) or do you simply want and watch this with a (much cheaper) bag of microwave popcorn and a can of pop. We all know with certainty that the wait to see films the comfort of your home is not very long so why drag your ass out to see a film? Honestly this makes me sad, but this is the reality we live under.

I would like to mention a little ray of hope. There are some “smaller” films that I will go out of my way to see as I don’t want to wait to see them at home. This generally the result of “buzz” surrounding a film 

Two of films come to mind. The first is called The Menu. I heard a TON of word of mouth about this one, and I left work early to catch it before it left the theatres. The second film is one I wanted to catch in a theatre but was unable to see. This film? Glass Onion. I 100% would have caught this in a theatre but Netflix only released it in theaters for ONE WEEK ONLY! The word of mouth was awesome, but my schedule was not, so I guess streaming it is for that one. 

I really wish this situation was different as I really love seeing films in theatres and will continue to do so albeit at a lower rate than I did in the past. As for the theatres themselves?  Who knows… Their business has been severely disrupted, and they are in sore need of a pivot of some type. Lets see what the future brings… The movie business has gone through multiple disruptions and it will be interesting to see what comes out on the other side of this

A Positive End To A Shitty Week

As you can tell from the title I had a rather shitty week. Monday night members of our team became concerned that there may be an issue with one our mission critical systems. I immediately took action and began checking for issues with servers and software related to this system. I ran down my list… Servers are all up.. Check! CPU utilization is in acceptable ranges… Check! Web process are operational… Check! Load balancer is operational… Check! This issue was a head scratcher. The following morning I added extra logging to our processes hopefully this would provide some insight. Well it did I just didn’t realize how to interpret the data we were receiving to pinpoint the issue. Long story short I was on a call with two team members and I said “I have no idea what the issue is. Maybe I need to just restart the servers.” As I was about to do that I connected to the server and by pure accident opened a screen and found the issue. A server process had had silently failed and we didn’t notice. I turned the process back on and our issue disappeard.

This was just the start of the week and it didn’t get too much better. We had other services that decided to flake out on us all week until we found those issues and resolved them. Funny thing I was really looking forward to getting some code written. We did have some successes delivering some new features but overall I was just happy that this week was just about over. Then today happened…

This morning helped turn what was a shitty week into one that ended on a positive note. Let me tell you why… A few years back I started attending a monthly group called Creative Mornings Austin. Every month a lot of the “creative types” in Austin get together to meet other “creatives”, listen to music and hear an inspirational speaker. I really love attending these monthly talks and find that they help me recharge and rebalance. Well attending today was more important that I ever figured it would be AND I nearly didn’t go. I nearly bailed out at the last minute as my wife may have needed the car and it would be difficult to coordinate. Well Jess was awesome as usual and sensed that I needed this so we figured it out. I would drop her off, go to CM and pick her up on the way home, provided she could get off work early. Coolness I could attend!

After dropping her off around 6:00am I found a coffee shop and worked until the CM meeting started at at 8:00am. When I got to CM I soon realized that this month was the December meeting and THIS IS MY FAVORITE meeting every year. You see December is the month when we get to create our own ornaments. One of the members of our group creates laser cut wood ornaments which we get to decorate. Check out mine…

Wood cut ornaments painted by Rod Paddock

Nothing too fancy from me but it did bring me a certain amount of peace. Then we moved on to the music part of the meeting. I really enjoy this as the types of musicians the organizers find are generally not part of my musical rotation of heavy metal and hard rock LOL. I closed my eyes and let the lovely music from artist named Lisa Marshall wash over me. More peace entered my worn down psyche. Then we moved onto our main speaker. Jodie King. Jodie is a local artist and spoke of her transition from business person to full time artist. It was THIS talk that confirmed that I really NEEDED to go to CM today. There was one statement that stuck out: “Thoughts turn to things” meaning if you think negatively you will get a negative outcome, yet if you think positively, you will get a positive outcome. She added an additional message the XX which to Jodie means cancel, cancel. When negative ideas creep in CANCEL CANCEL and and turn the thoughts positive.

So lets just CANCEL CANCEL the early part of the week and end on a positive note!

Well Hello Rodney!

“Well Hello Rodney!”, was my nana’s favorite greeting. Every time I called or visited; I was greeted with the familiar “Well Hello Rodney!”. I am sitting here reflecting on these words and feeling a bit glum as I will never hear these words again. Thursday night I received word that my nana had passed from a brief but significant illness. Over the last few days many different feelings of sadness overcame me, and many tears were shed. But after the tears abated, my thoughts turned to the happiness and joy my nana brought me and my family.

A lot of who I am today comes from hanging around my maternal grandparents Joyce and Ray.  When I was growing up, I spent many weekends at the grandparents house in Sylmar, CA. It was during these visits that many of my nerd like hobbies and proclivities formed.  One of my favorite memories was sitting in the living room with my grandma as she drank Tab and taught me the how to play blackjack. I was probably 8 or 9 years old. I do believe my degenerate gambling hobby came as result of this particular visit. LOL. I find memories amazing as I can recall this moment with complete clarity. I also believe a lot of my artistic ability comes from my nana’s love of art. Nana was one heck of a painter and many of her works adorn the homes of my mom, aunts, and uncles. She was a damn good painter. While I am not a painter, I do believe that my creativity comes from being around her while she worked. The smell of oil paint brings back memories of her,

While thinking about nana my mind also wanders to my grandfather Ray who also had a huge influence on me. The funny part of this is that I had no real idea of his influence on me until his passing some 20 years ago.

When we were talking about his life, we talked about what he did on Christmas morning before the gathering of our large and expanding family. What he did that morning was to get in his car to spend time driving around Hollywood. You see the lack of traffic on Christmas morning makes it a good time to drive around to just check stuff out.  This story might seem insignificant but in this boy’s life this was an AMAZING revelation. As many of my friends have witnessed first-hand, one of my all-time favorite things to do, is to drive around Hollywood checking stuff out. Every trip to LA is yet another opportunity to check out new and old places behind the wheel of a car. My best friend calls this “Rod’s Reality Tour”. From Vasquez Rocks to The Pink Motel, I love showing my friends the around the LA area. I NEVER get bored doing this. I’m in my 50s now and still love exploring. LA is a huge place and there are always new sites to find.  

Another thing that came from my grandparents was my absolute love of movies.  I do believe that this love of movies was not unique to me but to all of their children. I can remember in high detail watching movies whenever I visited my family’s homes.  I remember watching the Late Great Planet earth at the Winnetka drive in with my aunt Nancy or seeing a double feature of Jaws 2 and Grey Lady Down at yet another drive in (we saw lot so movies at the drive in) with my aunt Lisa. I also remember watching the dawn of MTV at my aunt Carries house and checking out movies on the Z channel that my grandfather had installed in his home instantly. Movies are in my family’s DNA.  The beauty of this is the love of movies has transcended multiple generations of my family. We still go to the movies on a regular basis.

There are many, many more stories just like these. If you ask my cousins, I bet they can share many similar stories. I’m also pretty sure that “Well Hello Rodney” was not a greeting solely for me. I bet that it was also use for my cousins too. It was also “Well Hello… Sara or Rebecka or Kelsey or Brandon or Bridget or Aaron” You get the gist. I am betting we all got the same greeting as our nana loved us all and was always happy to hear or be around us.

Now there is only one greeting left for her and it reads like this: “Well Hello Ray!”

D&D: Role of the Cleric

One question that always seems to pop up when you are beginning a new campaign is: “Who’s gonna play the cleric?”. This job generally goes to the newcomer or the last to arrive. The cleric, as a “role-playable” character has been grossly neglected. Throughout my years as a role-player and dungeonmaster, many people who play clerics have play them incorrectly, not incorrect as in the rules the rules but incorrect as a developed character. Most people play clerics as boring magic-users with healing spells vs. deeply rich characters with an interesting background. This article will discuss some cleric archetypes you can use in your games to give life to a character class deemed by many as boring.

The Deity and the Church

The deity dedicated cleric is the most way people play clerics. The primary focus of this character is to attract followers to their deity’s philosophies. The pinnacle of this archetype is the physical construction of a church or shrine in the name of their deity. However,  clerics of this archetype are never satisfied with the opening of just one church or gaining half a dozen followers, they will be constantly trying to gain more and more followers and opening more churches until the time of they leave to meet their maker.

The Cleric and the Cause

One possible way to liven up the cleric is to give him or her a cause. During the early stages of the characters life they may adopt a particular cause. Examples of a cause might be: the recovery of the “Holy Icon of Mazdar” or the establishment of a new religious outpost in the “Barbarian Tribes of Highdale”. The character’s life would be spent seeking to achieve the goals of their cause. Sometimes this cause might put the characters in jeopardy or might give them potentials for new adventures

The Cleric as Shaman

Usually portrayed as an NPC, the shaman or witchdoctor is a path seldom travelled by a player. The existence of Barbarian class opens possibilities for an interesting barbarian specialty: the barbarian shaman. The barbarian shaman would specialize in the use of primitive spells and would be more of a land or nature orientated cleric, much like a druid. This specialty might also have unique abilities to such as the ability to craft healing balms or salves potions from the land.

The Cleric in Exile

Another possible playing a cleric is to have that cleric fall from grace within their church. The exiled cleric would still spread the word of their deity with or without permission. Or they might preach a very different version of their religion causing them to be labeled a heretic. This archetype  would provide for some interesting adventures especially when the cleric’s party visits a town or city where the cleric’s religion is active.

The Cleric as Wanderer

Another closely related cleric would be the wanderer. The wanderer cleric would have no one place designated as his or her home base. Instead opting to travel about spreading the word of his or her god. The wanderer cleric would be a chief source of news in small towns and would be greatly appreciated. If bards are active within a campaign a wandering cleric and a bard would make for a good combination.

Some examples of refugee and wandering clerics would be: “Warren Borinias” the traveling preacher of Helm and the bringer of good tidings or “Kalox the Banished”, fleeing the church of Athena for refusing to sacrifice war criminals.

The Cleric as Charlatan

“Give me a religion and I give you a profit center” says Kaj the Profit, a well known and highly respected figure among the  desert tribal regions.  Preaching fire and  brimstone  this cleric seeks food, cash and favors in order to make people “right with their gods” while extracting as much money as possible before hitting the road to their next conquest. This archetype has many possibilities as the cleric might find themselves in situations where they are being hunted or recognized by authorities as false preachers.

As can be seen the cleric can be a class as lively as any knightly-knight of decrepit arch-mage, This article is dedicated to the people might be reluctant to play a character or are looking new ideas to breathe life into their existing character

Simple Cryptography With C#

One of the applications I’m working on has the need to store Privately Identifiable Information (PII) in it’s databases. We have chosen to store this information using the RSA Public Key Cryptography provider included in the .NET Core base libraries.

We all use Public Key Cryptography (PKC) every day. If you are reading this you are using PKC as the underpinnings of HTTPS use public key cryptography. Basically PKC is implemented using two individual keys the PUBLIC one and the PRIVATE one. The public key is used to encrypt data and the private key is used to decrypt data. As it name implies you can distribute the PUBLIC key … well… publicly. Meaning you can give it out freely. The PRIVATE key is yours and yours alone and is the only key that can decrypt your data.

For our needs we created a simple library for managing our PKC needs. This library has 3 parts:

1) Function to generate PUBLIC and PRIVATE keys
2) Function to encrypt a string
3) Function to decrypt a string.

Lets take a look at generating keys first. To to this you need to create a class that will be used to return the PUBLIC and PRIVATE key strings. The class looks like this:

public class KeyPair
{
	public string PublicKey { get; set; } = "";
	public string PrivateKey { get; set; } = "";
}

Next you need to create an instance of the RSACryptoServiceProvider class. When creating an instance of this class you need to pass it the size of your keys.

The size of the key specifies the strength of the key you are using. Meaning how difficult will it be to crack. The larger the more difficult it is to crack. Our code stores the size of the key in a CONST called Keysize. After creating an instance of the provider your an call the ToXmlString() of the provide which will return your PUBLIC and PRIVATE keys depending on the boolean you pass to the function. Passing a false value returns the PUBLIC key, passing a true value returns the private key. The following code shows how to create and return a PUBLIC/PRIVATE key pair.

using System;
using System.Security.Cryptography;
using System.Text;

namespace CryptoLibrary
{
	public class CryptoTools
	{
		private const int KeySize = 2048;

		public KeyPair GetKeyPair()
		{
			var provider = new RSACryptoServiceProvider(KeySize);
			var retVal = new KeyPair()
			{
				PublicKey = provider.ToXmlString(false),
				PrivateKey = provider.ToXmlString(true)
			};
			return retVal;
		}

	public class KeyPair
	{
		public string PublicKey { get; set; } = "";
		public string PrivateKey { get; set; } = "";
	}

}

Now you can call the library to generate your keypair.

var tools = new CryptoTools();
var keys = tools.GetKeyPair();

This code returns your keys in an XML string that looks like this:

<RSAKeyValue>
  <Modulus>vx53GKAPG02.......yQgyoVlAwYHZxP7jVTyQ==</Modulus>
  <Exponent>AQAB</Exponent>
</RSAKeyValue>

With these two keys you can write some simple functions to Encrypt and Decrypt strings. The steps to encrypt a string are:

1) Create an instance of the RSACryptoServiceProvider class.
2) Load the PUBLIC key using the FromXmlString() function.
3) Convert your string to a byte[] array
4) Encrypt the string (returned as byte[] array)
5) Turn the byte[] array into a base 64 string.
6) Return the string.

The steps to decrypt a string are:

1) Create an instance of the RSACryptoServiceProvider class.
2) Load the PRIVATE key using the FromXmlString() function.
3) Convert your string to a byte[] array
4) Decrypt the string (returned as byte[] array)
5) Turn the byte[] array into a UTF8 string.
6) Return the string.

The following code represents this functionality:

public string Encrypt(string publicKeyXML, string itemToEncrypt)
{
	var provider = new RSACryptoServiceProvider(KeySize);
	provider.FromXmlString(publicKeyXML);

	byte[] bytes = Encoding.UTF8.GetBytes(itemToEncrypt);
	var encryptedData = provider.Encrypt(bytes,true);
	var retval = Convert.ToBase64String(encryptedData);
	return retval;
}

public string Decrypt(string privateKeyXML, string itemToDecrypt)
{
	var provider = new RSACryptoServiceProvider(KeySize);
	provider.FromXmlString(privateKeyXML);

	byte[] bytes = Convert.FromBase64String(itemToDecrypt);
	var encryptedData = provider.Decrypt(bytes, true);
	var retval = Encoding.UTF8.GetString(encryptedData);
	return retval;
}

The following code shows how to generate keys, encrypt the data and descrypt the data.

var tools = new CryptoTools();
var keys = tools.GetKeyPair();
			
Console.WriteLine(keys.PublicKey);
Console.WriteLine(keys.PublicKey);

var encrypted = tools.Encrypt(keys.PublicKey, "555-12-3456");
var decrypted = tools.Decrypt(keys.PrivateKey, encrypted);

var encrypted2 = tools.Encrypt(keys.PublicKey, "4000-0000-0000-0002");
var decrypted2 = tools.Decrypt(keys.PrivateKey, encrypted);

The complete library for these tools:

using System;
using System.Security.Cryptography;
using System.Text;

namespace CryptoLibrary
{
	public class CryptoTools
	{
		private const int KeySize = 2048;

		public KeyPair GetKeyPair()
		{
			var provider = new RSACryptoServiceProvider(KeySize);

			var retVal = new KeyPair()
			{
				PublicKey = provider.ToXmlString(false),
				PrivateKey = provider.ToXmlString(true)
			};
			
			return retVal;

		}

		public string Encrypt(string publicKeyXML, string itemToEncrypt)
		{
			var provider = new RSACryptoServiceProvider(KeySize);
			provider.FromXmlString(publicKeyXML);
			byte[] bytes = Encoding.UTF8.GetBytes(itemToEncrypt);
			var encryptedData = provider.Encrypt(bytes,true);
			var retval = Convert.ToBase64String(encryptedData);
			return retval;
		}

		public string Decrypt(string privateKeyXML, string itemToDecrypt)
		{
			var provider = new RSACryptoServiceProvider(KeySize);
			provider.FromXmlString(privateKeyXML);
			byte[] bytes = Convert.FromBase64String(itemToDecrypt);
			var encryptedData = provider.Decrypt(bytes, true);
			var retval = Encoding.UTF8.GetString(encryptedData);
			return retval;
		}
	}

	public class KeyPair
	{
		public string PublicKey { get; set; } = "";
		public string PrivateKey { get; set; } = "";
	}
}

I hope that you find these tools useful. In my next post I’ll show how to combine this solution and the hashing library shown in my Building a Simple C# Hashing Utility post to build a bit more holistic solution to handling encrypted data.

Console Apps in Python and C# (.Net Core) Part 4

At this point we have a pair of programs written in Python and C#. These programs are used to run the ccextractor program with extension and path parameters. The next step in our evolution is to run our code on other platforms namely macOS and Linux. This post will demonstrate running code on both of those platforms.

Running on macOS

Before you start working on the code, you’ll need to get your Mac set up to 1) Install the ccextractor application and 2) Python 3 code.

Installing the extractor is simple and is done via the Homebrew infrastructure used by Mac Developers. To install the ccextractor do the following :

Install Homebrew if it’s not already installed. Simply run this script (copied from https://brew.sh/ ) from a terminal window.

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

Once you have installed Homebrew, you can install the ccextractor program by issuing the following command:

brew install ccextractor

After installing the ccextractor, test it by typing ccextractor from the terminal window. You should see a screen full of help information. Now insure Python3 is installed. From a terminal window type: python3

If Python 3 is installed you’ll see the Python’s interactive window. If no,t you may be promoted to install the Command Line tools for OSX. If so, run that installer. If the Command Line Tools installer does not run directions for installing Python 3 can be found here: https://docs.python-guide.org/starting/install3/osx/

Now it’s time to test the code. Clone this repo:

https://github.com/rjpaddock/ExtractorRunner.git

Now from a terminal window change into the folder where you cloned that repo and run the following command:

python3 run_cc.py --extension mpg --directory [[INSERT YOUR DIRECTORY HERE]]

You will be presented with the following error information:

Traceback (most recent call last):
  File "run_cc.py", line 15, in <module>
    subprocess.run([extractor_name,  os.path.join(args.directory, file)])        
  File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/subprocess.py", line 489, in run
    with Popen(*popenargs, **kwargs) as process:
  File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/subprocess.py", line 854, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/subprocess.py", line 1702, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'ccextractorwin'

This error is because the name of the ccextractor application is different in the Windows environment. Check out the last line. What is the fix for this ?

To fix this you need to call a different executable based on operating system. Lucky for us Python has a built in library for just such a thing. To check what platform your code is running import the platform library at the top of your python file

import platform

Next add the following code to your script:

extractorname = ''
if platform.system() == 'Windows':
  extractor_name = 'ccextractorwin'
elif platform.system() == 'Darwin':
  extractor_name = 'ccextractor'
elif platform.system() == "Linux":
  extractor_name = 'ccextractor'

Now run the application. Your script should start processing files with no error.

NOTE: The code in the Repository already has this change applied. You’re welcome 🙂

The next step is to get the C# code up and running on macOS. This process was much easier than I anticipated, as Microsoft has created a macOS version of Visual Studio. The first step is to install Visual Studio Mac from the Microsoft website https://visualstudio.microsoft.com/vs/mac/

When installing the application make sure to install it with the .NET Core option selected:

Once the installer completes, open the ExtractorRunner solution from the folder you pulled code into. Open the options dialog for the project and set the command line parameters you have been using to test:

Run your code now. You will now see an error in the console window of your application:

This is very similar to the Python error and requires the same solution. .NET Core also included a set or libraries to determine your operating system. Add the following snippet to the top of your program:

using System.Runtime.InteropServices;

Now add the following block of code to your C# program:

var extractor_exe_path = "";
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
  {
  extractor_exe_path = "ccextractorwin";  
  }
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
  {
  extractor_exe_path = "ccextractorwin";
  }
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
  {
  extractor_exe_path = "ccextractor";  
  }

Now run your code and you should see proper output in the runner window.

Now you have the same command line functionality for both the Python and C# versions of this program and can run the code on the Mac and Windows. Now lets take a look at the process of running thus under Ubuntu.

Running on Linux (Ubuntu)

Before modifying the our runner programs we need to install the ccextractor application on our Linux server. Directions for installing ccextractor on Linux can be found here: https://github.com/CCExtractor/ccextractor/wiki/Installation

Basically you pull the code from Github, and run the typical process of building applications in the Linux world. I was lucky as the code “just compiled” and ran using the instructions provided. Once I did that I had to make one simple change to the script and was able to execute our runner application. The branch of code to determine the proper program to run looks like this:

if platform.system() == 'Windows':
  extractor_name = 'ccextractorwin' 
elif platform.system() == 'Darwin':
  extractor_name = 'ccextractor'
elif platform.system() == "Linux":
  extractor_name = '/home/azureuser/data/projects/ccextractor/linux/ccextractor'

Now I was able to run the code using the same command line options we used on the Mac.

python3 run_cc.py --extension mpg --directory /home/azureuser/data/sampledata/

Now that the Python code is up and running you can turn your sites onto running the C# code next. To do this you need to first install the .NET Core SDK on your Ubuntu instance. This is done by following the directions from this page: https://docs.microsoft.com/en-us/dotnet/core/install/linux-ubuntu

If you are running a different flavor of Linux you can find directions on this page: https://docs.microsoft.com/en-us/dotnet/core/install/linux

Once you have the SDK installed , change into the folder where you cloned the GitHub repository and run the following command:

dotnet build

This will build an executable file and put it in a sub-folder (off the root of your code) in this location /bin/Debug/netcoreapp3.1 There is one more step though. Before you can run the code you need to change your Program.cs file to use the following executable selection code:

var extractor_exe_path = "";
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
  {
	  extractor_exe_path = "ccextractorwin";
	}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
	{
		extractor_exe_path = "ccextractor";
	}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
	{
		extractor_exe_path = "/home/azureuser/data/projects/ccextractor/linux/ccextractor";
	}

Run the dotnet build command again and change into that folder and run the following command:

./ExtractorRunner --extension mpg --directory /home/azureuser/data/sampledata/

The following screen shows the ccextractor running on Ubuntu via the c# Extractorrunner program.

That’s how you create a totally cross platform application in Python and C#. I was pleasantly surprised how simple it was to build and run the C# code on Mac and Linux which is a testament to the work the Microsoft team has done over the last few years. I hope you enjoyed this series. Next week I hope to return with a recap of this series with a few additional tips. Then it’s on to writing about other programming items.

Console Apps in Python and C# (.Net Core) Part 3

In my last post, I showed you how to add named parameters to a Python script. These parameters –extension and –directory gave us the ability to run our programs with different extensions and located in different directories. This article will add the same functionality to the C# version of this program.

Where Python has an argument parser built into its native libraries, the .NET platform does not. Not to fear, there is a third-party library that you can install to add this needed functionality. This library is called CommandLineParser and can be installed via a Nuget package. You can install this library via the Nuget console by issuing the following command:

Install-Package CommandLineParser -Version 2.8.0

Once you have installed this library you need to build a class that will hold your parsed command line parameters. This class will be augmented with Attributes provided by the command line parser. The first parameter to add is the dynamic extension. To do this add the following class code to your program:

public class Options
{
  [Option(longName:"extension",HelpText = "Extension of files to convert",Default = ".mpg")]
  public string Extension { get; set; } = "";
}

This code has a string property called Extension. When you pass in the –extension parameter it will be stored on this property. The more interesting aspect of this class is the [Option] attribute.

[Option(longName:"extension",HelpText = "Extension of files to convert",Default = ".mpg")]

The longName property tells the CommandLIneParser library to parse an argument with the name –extension onto the Extension parameter. The HelpText and Default properties are self explanatory based solely on their name.

Now that you have created this class you can call the command line parser to populate your arguments onto an instance of the Options class. This code demonstrates how to do this:

var parsed = Parser.Default.ParseArguments<Options>(args);
var options = ((Parsed<Options>) parsed).Value;

This code takes the args collection passed to your program, parses them, and returns a parsed object. After parsing the argument collection you need to cast the .Value property of the parsed object into an instance you can use in your programming code. Your processing logic will now look like this:

var directory_to_import = "D:/Data/clients/RodPaddock/CCExtractor/";
var extractor_exe_path = "D:/Data/clients/RodPaddock/CCExtractor/ccextractorwin";
foreach (var fileName in Directory.GetFiles(options.Directory,$"*{options.Extension}"))
{
  Console.WriteLine(fileName);
  var process = new Process()
  {
    StartInfo = new ProcessStartInfo
	  {
  	    FileName = $"{extractor_exe_path}",
  	    Arguments = $"{fileName}",
	    UseShellExecute = true,
	   }
   };
	process.Start();
 }

Notice that the GetFiles() function now uses the Extension property of your Options class.

The next step is to add the directory to your Options class. To do this, simply add another property to your class with the appropriate name and options.  Your class code will now look like this:

public class Options
{
  [Option(longName:"extension",HelpText = "Extension of files to convert",Default = ".mpg")]
  public string Extension { get; set; } = "";

  [Option(longName: "directory", HelpText = "Directory to process", Default = ".")]
  public string Directory { get; set; } = ".";
}

Notice that the DefaultValue property is a single period (.). This tells the get files routine to simple process the current directory.

Now you can incorporate your new Directory option into your application code. This is what the final version will look like:

using System;
using System.Diagnostics;
using System.IO;
using CommandLine;

namespace ExtractorRunner
{
	class Program
	{
		static void Main(string[] args)
		{
			
			var parsed = Parser.Default.ParseArguments<Options>(args);
			var options = ((Parsed<Options>) parsed).Value;
			
			var extractor_exe_path = "ccextractorwin";
			foreach (var fileName in Directory.GetFiles(options.Directory,$"*{options.Extension}"))
			{
				Console.WriteLine(fileName);
				var process = new Process()
				{
					StartInfo = new ProcessStartInfo
					{
						FileName = $"{extractor_exe_path}",
						Arguments = $"{fileName}",
						UseShellExecute = true,
					}
				};
				process.Start();
			}

		}
		public class Options
		{
			[Option(longName:"extension",HelpText = "Extension of files to convert",Default = ".mpg")]
			public string Extension { get; set; } = "";

			[Option(longName: "directory", HelpText = "Directory to process", Default = ".")]
			public string Directory { get; set; } = ".";
		}
	}
}

One item of note is the path to the EXE is just the name of the application. This is because in the last post we decided to add the Ccexteactorwin.exe file to our system PATH via the System Environment variables screen..

You can now run your code from visual studio. When testing your code you can call your application with arguments by opening your Project Properties Window, selecting the Debug section and passing specifying your parameters in the Arguments section. The following screen shows that:

Your running program will now spawn a new process which looks like this:

At this point we have two programs one in Python and one in C#. They both accept similar named parameters and now call the ccextractor program based on global PATHs. Next step is to get the code running on platforms other than Windows.