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….
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.
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…
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!”, 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!”
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.
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
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.
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:
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
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:
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.
Sometimes time is of essence when building little utility programs. Once the job of your utility is completed it may be useful to make that program more useful and more universal. Or it just might be a good way to learn something new for a blog post or just your own education.
I decided that this would be a cool opportunity to add some features to the batch runner I created for my friend. One item that was immediately needed changing was the ability to choose the extension of the files to process. My initial choice was to process mpg files as the default extension. My friend immediately changed it to mp4. This is a useful extension point and the first thing to parameterize.
This post will focus on the Python version first. We’ll look at the C# version in our next post. One way we could hack this together would be to use Python’s sys.argv[] array which provides positional arguments to Python programs. For instance if we called our program with the following statement:
python copy run_cc.py .mp4
we could access the .mp4 with sys.arg[0] While this works it will cause problems in the long haul if we add or remove parameters. It is also not very intuitive. It would be better to call the with a named parameter. For example:
run_cc.py –extension .mp4
Python has a built-in library to do this exact thing. This library is known as argparse. To implement our first option we need to do the following:
Add an import argparse to the imports section of our program
Create an argument parser object and add an argument to it. Your code will look like this:
parser = argparse.ArgumentParser()
parser.add_argument("--extension", help="Extension of files to convert", default='.mpg')
args = parser.parse_args()
There is a lot going on with just these few lies of code. What this set of code does is 1) Create an argument parser, 2)Adds a parameter called –extension to the command line. This parameter will be added the args array as a property with the name extension. parameter. Finally this code specifies a help description and a default parameter value. Our program code now looks like:
import os
import subprocess
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--extension", help="Extension of files to convert", default='.mpg')
args = parser.parse_args()
directory_to_import = 'D:/Data/clients/RodPaddock/CCExtractor/'
extractor_exe_path = 'D:/Data/clients/RodPaddock/CCExtractor/ccextractorwin'
for file in os.listdir(directory_to_import):
if file.endswith(args.extension):
print(os.path.join(directory_to_import, file))
subprocess.run([extractor_exe_path, os.path.join(directory_to_import, file)])
The next step is to add a parameter to specify the directory you wish to read files from. We’ll call the parameter –directory and will default it to (.) the current working directory. A sample call would be as follows:
import os
import subprocess
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--extension", help="Extension of files to convert", default='.mpg')
parser.add_argument("--directory", help="Directory to process", default='.')
args = parser.parse_args()
extractor_exe_path = 'D:/Data/clients/RodPaddock/CCExtractor/ccextractorwin'
for file in os.listdir(args.directory):
if file.endswith(args.extension):
print(os.path.join(args.directory, file))
subprocess.run([extractor_exe_path, os.path.join(args.directory, file)])
Finally lets get rid of the EXE path. We are going to “cheat” a bit on this one. We are simply going to add that directory to the PATH statement on the machine. This will sync its operation with the behavior on the Mac.
To change your PATH statement in Windows open the Environmental Variables from the Windows Start menu find PATH in the System variables and add the path to wherever you extracted the ccextractor application. The following screen demonstrates how this should look:
Now your final Python program will look like this:
import os
import subprocess
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--extension", help="Extension of files to convert", default='.mpg')
parser.add_argument("--directory", help="Directory to process", default='.')
args = parser.parse_args()
# should be added to the system PATH statement
extractor_name = 'ccextractorwin'
for file in os.listdir(args.directory):
if file.endswith(args.extension):
print(os.path.join(args.directory, file))
subprocess.run([extractor_name, os.path.join(args.directory, file)])
This completes part 2 of this series. In part 3 we will learn how to accomplish the same feature using C# across multiple platforms.
Earlier this week I got an e-mail from a friend. Here’s the gist of the e-mail (names have been excluded to protect the innocent LOL)
I have a command line tool installed through homebrew on my laptop running high sierra. The command is just ccextractor <filepath> and it runs fine in a standard bash terminal. I was hoping to use automator to be able to run it on batches of files, but i'm struggling with the syntax for the Run Shell Script command.
It just keeps saying ccextractor command not found.
Also the command line tool can only do one file at a time so I guess I need some way to loop the request so it does the first file, then runs the command again on the second file etc?
My friend is a fellow movie geek who wants to run CCExtractor on a batch of movie files.
The problem was my friend could not figure out how to use Automator (a Mac tool) to run this command on a directory of files. An attempt was made to use bash as well with no luck. Hence the e-mail.
I replied back that I could probably whip something up in Python if that would work. “Are you sure that’s not too much work?” my friend replied. “Nah it should be pretty simple to whip up.”, I replied.
Here’s the gist what I did.
Traveled to the https://www.ccextractor.org/ site and downloaded the binaries and some 3.x GB sample files to my drive.
Then I opened my trusty text editor (https://www.sublimetext.com/) is my editor of choice and started a new .py (python) prorgam.
After a bit of google-fu I came up with this set of code:
import os
import subprocess
directory_to_import = 'D:/Data/clients/RodPaddock/CCExtractor/'
extractor_exe_path = 'D:/Data/clients/RodPaddock/CCExtractor/ccextractorwin'
for file in os.listdir(directory_to_import):
if file.endswith(".mpg"):
print(os.path.join(directory_to_import, file))
subprocess.run([extractor_exe_path, os.path.join(directory_to_import, file)])
This code was built, debugged and run on my Windows development box. The goal was to get it working as fast as possible on my main development box before moving it onto a Mac.
Getting the code to run on the Mac was simple. Here’s that version:
import os
import subprocess
directory_to_import = '/Users/rodpaddock/ccextractor'
extractor_exe_path = 'ccextractor'
for file in os.listdir(directory_to_import):
if file.endswith(".mpg"):
print(os.path.join(directory_to_import, file))
subprocess.run([extractor_exe_path, os.path.join(directory_to_import, file)])
As you can see the changes were minimal at best. I changed the path to my user directory on the Mac and git rid of the specific path to the executable. I used brew to install the CCExtractor on my mac so it was in the PATH already. After installing Python version 3.x on my old Mac I was able to run the application as-is. No operating specific issues.
After getting it to work I sent it off to my fiend who simply changed the path to the files to decode and BOOM it just worked.
After marveling at how much could be accomplished with so few lines of code, I became curious to see how complex it would be to build the same application in C#. I’m using .NET Core to do this, as I want to run it cross platform as well.
Here’s the same functionality in C#
using System;
using System.Diagnostics;
using System.IO;
namespace ExtractorRunner
{
class Program
{
static void Main(string[] args)
{
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(directory_to_import,"*.mpg"))
{
Console.WriteLine(fileName);
var process = new Process()
{
StartInfo = new ProcessStartInfo
{
FileName = $"{extractor_exe_path}",
Arguments = $"{fileName}",
UseShellExecute = true,
}
};
process.Start();
}
}
}
}
Not too bad .NET core. It was pretty simple to build this application and get it running in a console application.
Now that I have this code, I think it would be fun to explore making it a bit more useful. I’m doing this as an exercise to learn a few more things about building more robust Python and C# console applications. Here’s a set of features I plan on adding:
Accept an extension parameter (I started with .mpg files) my friend had to change the extension to .mp4 files.
Accept the path to decode as a parameter.
Accept the path to the executable as a parameter
Parameters should be named vs positional if possible.
One of the most interesting aspects of Cryptography is the ability to generate unique hash values from strings or files.
Wikipedia Defines Cryptographic as follows:
A cryptographic hash function is a hash function that is suitable for use in cryptography. It is a mathematical algorithm that maps data of arbitrary size to a bit string of a fixed size and is a one-way function, that is, a function which is practically infeasible to invert
The most common use of hashes is to store hashed (vs plain text) values of passwords in databases.
Another use case of hashes is to detect changes to files. A few years back we built a script runner that would only run new or changed files from a given scripts folder. The basic algorithm for this was:
1) Read all file names from a folder.
2) Look for for a file same name with a .HASH extension.
If .HASH file was missing:
a) Run the script file
b) Create a new file with a .HASH extension.
c) Hash the script file and store the result in
the .HASH file
3) If the .HASH file already existed:
a) Generate a hash value of the script file
b) Compare value the hash value stored in the .HASH file.
c) If they were the same we ignore the file.
d) If they were different Run the script,
re-generate the hash and store the results
in the .HASH file.
The core of these processed is a little nugget of code I built up a few years back .I thought it would be good to share my hashing function with the world. The following code generates a SHA512 has for a given string. The code then takes the generated byte array converts it to a string:
public static string GenerateHash(string stringToHash)
{
var crypt = new SHA512Managed();
var hash = new StringBuilder();
var crypto = crypt.ComputeHash(Encoding.UTF8.GetBytes(stringToHash), 0,
Encoding.UTF8.GetByteCount(stringToHash));
foreach (var bit in crypto)
{
hash.Append(bit.ToString("x2"));
}
return hash.ToString();
}
I built a little command line utility that implements this function and can generate a hash for a passed in string or file. You can find this utility on Github: