DEV Community

Cover image for This Script is Only Useful to Me (Probably)
CodingVibe
CodingVibe

Posted on

This Script is Only Useful to Me (Probably)

I have a podcast with a friend and we were interested in uploading our episodes in full to YouTube. Unfortunately, he doesn't know how to video edit and I'm busy with my job, streaming, the podcast, and writing articles like this, so it seemed like YouTube as a podcast platform was dead to us. I did a bit of research and it turns out both our podcast platform Buzzsprout and YouTube have APIs available for uploading, which meant if I invested the time, I could bundle up the uploading work in to one script and have it done even faster.

Then the classic dilemma hit me -- do I really want to sink the time in to doing this? There's so many factors that go in to that like how much time it'll save me, how much time it'll take, will anyone else find it useful. Ultimately, I hit upon the piece of logic I often hit on when deciding what personal projects to do -- it sounds fun so why not?

Three Scripts

First off, I decided to write three separate scripts to do the following operations:

  • Upload the podcast to Buzzsprout
  • Create a visualization video for the podcast. Essentially a static image with audio visualization on top with the podcast as the audio
  • Upload a video to YouTube

All three on their own are small, atomic operations that can be stitched together with an overarching script or CI/CD platform to form a complete workflow, but can also be used individually. Since the upload data required for both Buzzsprout and YouTube are similar (time to publish, title, description, and episode number), it makes sense to streamline the input of data in to both upload scripts and call the script just once.

Podcast Workflow Automator

Copying and pasting the code from all three scripts and making one super script does future me a disservice. By smashing them together, but also having them separate, any changes in one code base needs to also be copy/pasted in to the other code base and if I don't remember that, I'll end up fixing bugs or coding enhancements twice. As mentioned earlier, I could have one base script and call these individual scripts on their own via Bash, Powershell, or a CI/CD platform, but since Python also allows you to import code from other .py files, I decided to keep it consistent and go for that option.

Prepping individual scripts for import

Unfortunately, the way in which I usually write standalone scripts isn't compatible with import, so a few changes had to be made. Upon initially writing the scripts, my code looked like this:

def main():
  parser = argparse.ArgumentParser()
  parser.add_argument("--foo")

  # script logic

if __name__ == "__main__":
  try:
    main()
Enter fullscreen mode Exit fullscreen mode

This structure doesn't allow us to use the meat and potatoes of the script without grabbing arguments from argparse. To fix this, we pull all the argparse logic in to its own function, then call the main function from that. The main function is also renamed to something more logical so when we're importing it in other functions, we're not scratching our heads wondering what main does.

def call_with_args():
  parser = argparse.ArgumentParser()
  parser.add_argument("--foo")

  args, _ = parser.parse_known_args()
  upload_podcast(args.foo)

def upload_podcast(foo):
  # upload logic


if __name__ == "__main__":
  try:
    call_with_args()
Enter fullscreen mode Exit fullscreen mode

Now that our relevant functions have been renamed, we can move on to exposing and importing them in to our new script. First off, the base script needed to be moved to a directory with the name I wanted to use for the package. Next an __init__.py file had to be created alongside the script that imported and exposed the primary function as part of being a module. Then a setup.py file detailing the package and its dependencies had to be created in the base directory. All of that is to enable importing in the requirements.txt like this:

git+https://github.com/codingvibe/audio-to-vizualization@main#egg=audio_to_visualization
git+https://github.com/codingvibe/buzzsprout-uploader@main#egg=buzzsprout_uploader
git+https://github.com/codingvibe/youtube-uploader@main#egg=youtube_uploader
Enter fullscreen mode Exit fullscreen mode

Now we can import the relevant functions in to our combined script like any normal package this:

from buzzsprout_uploader import upload_podcast
from audio_to_visualization import create_vizualization
from youtube_uploader import upload_video
Enter fullscreen mode Exit fullscreen mode

Was this a non-trivial amount of work to do for something nobody is likely going to use? Yes. Are there easier, dirtier ways to do this? Absolutely. Since I have the repos locally, I could have just done this:

sys.path.append('../')

from buzzsprout_uploader.buzzsprout_uploader import upload_podcast
from audio_to_visualization.audio_to_visualization import create_vizualization
from youtube_uploader.youtube_uploader import upload_video
Enter fullscreen mode Exit fullscreen mode

Had I done this, I wouldn't have had to bother with the script.py or package directory. I decided to go with the pip-importable way just to get some experience doing it and on the off chance anyone else would use this. Had I not intended to upload it to Github, I definitely would have done it the lazy way (and in fact I did on my Twitch stream and only made it importable when I wrote this article). Doing it the "right" way took quite a while and lead to this fun experience:

Final Thoughts

Is this a project more people can use than just me? Maybe, but honestly it doesn't matter -- it's going to make my life marginally easier and was fun to write. I spent probably 20 hours taking my time to upload an episode to Buzzsprout and YouTube from 20 minutes to 5, which means it'll be worth the time investment once we have 80+ episodes. As the saying goes, "why spend 10 minutes doing a task when I can spend 3 hours automating it?"

The three individual scripts you can find here:

GitHub logo codingvibe / audio-to-vizualization

Given an audio file and background image, make an audio visualization on the image to the audio

Audio to Visualization

The purpose of this small Python script is to transform an audio file in to a video using a background image and an audio visualizer. This tool was written leveraging ffmpeg and requires it be installed and accessible via the ffmpeg command on the command line.

FFMPEG

Download ffmpeg and get access to the documentation at https://www.ffmpeg.org/

Requirements

Install via pip:

pip install -r requirements.txt

Run the script

python audio_to_visualization/audio_to_visualization.py <arguments>

Command line arguments

Argument Description Required Default
--audio path to audio file to visualize true N/A
--background path to image to use for background true N/A
--output path and name of output file. Must end in .mp4 true N/a
--vis-background-to-vid-ratio ratio of visualization background height to input image height (0.0-1.0) false 0.2
--vis-waves-to-vid-ratio ratio of visualization waves height to input image height (0.0-1.0) false 0.15
--vis-color color for visualization waveforms. can be used multiple times false

GitHub logo codingvibe / buzzsprout-uploader

Simple script to upload a podcast to Buzzsprout

buzzsprout-uploader

Simple script to upload a podcast to Buzzsprout.

Requirements

Install via pip:

pip install -r requirements.txt

Run the script

python buzzsprout_uploader/buzzsprout_uploader.py <arguments>

API Key and Podcast ID

A Buzzsprout API key and the ID of your podcast are both required to run this script. You can obtain both by logging in to Buzzsprout and navigating to https://www.buzzsprout.com/my/profile/api

Command line arguments

Argument Description Required Default
--audio path to audio file to upload true N/A
--title title of the podcast true N/A
--description description of the podcast true N/A
--tags space separated list of tags for the podcast false []
--publish-at-date date to publish video (format is YYYY-MM-DD) false
--publish-at-time time to publish video (iso format) false
--episode-number episode number of this upload false If not supplied, most recent episode number + 1
--season-number season number for this upload false If not supplied, most recent episode number
--private whether or not

GitHub logo codingvibe / youtube-uploader

Updated version of the Python YouTube upload example from Google with extra command line arguments

youtube-uploader

Updated version of the Python YouTube upload example from Google with extra command line arguments.

Install Requirements

pip install

Run the script

python youtube_uploader.py <arguments>

Command line arguments

Argument Description Required Default
--file path to video file to upload true N/A
--title title of the uploaded video false Test Title
--description description of the video false Test Description
--category YouTube category of video (https://developers.google.com/youtube/v3/docs/videoCategories/list) false 22
--keywords comma separated list of tags false
--privacy-status privacy status of video false public
--publish-at-date date to publish video (format is YYYY-MM-DD) false
--publish-at-time time to publish video (iso format) false
--client-secrets-file path to client secrets json file false youtube-uploader-client-credentials.json

And the final workflow script is here:

GitHub logo codingvibe / podcast-workflow-automator

A script that uploads a podcast to Buzzsprout, creates an audio visualization of your podcast, then uploads to YouTube

podcast-workflow-automator

A script that uploads a podcast to Buzzsprout, creates an audio visualization of your podcast, then uploads to YouTube

Command line arguments

I know there are a lot. I'm sorry.

Argument Description Required Default
--audio path to audio file to upload true N/A
--title title of the uploaded podcast and video true N/A
--description description of the podcast and video true N/A
--api-key Buzzsprout API key to use to upload true N/A
--podcast-id ID of the podcast to upload to true false
--background-image path to image to use for background in video true N/A
--tags space separated list of tags for the podcast and video false
--episode-number episode number of this upload false If not supplied, most recent episode number + 1
--season-number season number for this upload false If not supplied, most recent episode number
--private whether or not this upload should be private false false
--publish-at-date date

Top comments (0)