<p><a href="https://gitlab.com/norrist/solocast">https://gitlab.com/norrist/solocast</a></p>
<h3 id="sample-scripttxt">Sample script.txt</h3>
<pre class="txt"><code>This is a sample script for solocast.
Separate the segments with a blank line

Bulleted lists are OK, but keep the items together by not skipping a line
- Item 1
- Item 2

### Markdown Formatting is OK
But the Formatting gets lost in the script
so you can write show notes in loosely formatted markdown

Don&#39;t have more than 1 blank line separating segments
</code></pre>
<h3 id="solocastpy">solocast.py</h3>
<pre class="python"><code>#! /usr/bin/env python3

import click
import os
from shutil import which

script_file = &quot;script.txt&quot;
recording_directory_name = &quot;Recordings&quot;
recording_format = &quot;wav&quot;
script_segments = {}


def test_sox_exists():
    try:
        assert which(&quot;sox&quot;)
    except AssertionError:
        print(&quot;Cant find sox.  Install sox somewhere in your path.&quot;)
        exit(1)


def get_recording_file_name(slug):
    return f&quot;{recording_directory_name}/{slug}.{recording_format}&quot;


def project_prep():
    if not os.path.exists(recording_directory_name):
        os.makedirs(recording_directory_name)
    if not os.path.exists(f&quot;{recording_directory_name}/Archive&quot;):
        os.makedirs(f&quot;{recording_directory_name}/Archive&quot;)


def wait_for_input():
    click.echo(&quot;*&quot; * 40)
    _ = input(&quot;Press ENTER to Continue&quot;)


def add_slug_text(slug, text):
    script_segments[slug] = text


def recording_exists(slug):
    if os.path.isfile(get_recording_file_name(slug)):
        return True
    return False


def noise_profile_missing():
    if os.path.isfile(f&quot;{recording_directory_name}/noise.prof&quot;):
        return False
    return True


def truncate_audio(slug):
    recording = get_recording_file_name(slug)
    new_recording = f&quot;{recording_directory_name}/{slug}-truncated.{recording_format}&quot;
    click.echo(f&quot;truncating {recording}&quot;)

    SOX_CMD = (
        f&quot;sox -V2 {recording}  {new_recording}   silence -l 1 0.1 .1% -1 1.0 .1%   stat&quot;
    )
    click.echo(SOX_CMD)
    os.system(SOX_CMD)
    os.system(
        f&quot; mv -v {recording} {recording_directory_name}/Archive/{slug}.{recording_format}&quot;
    )
    os.rename(new_recording, recording)
    review_audio(slug)


def play_audio(slug):
    recording = get_recording_file_name(slug)
    click.echo(f&quot;Playing {recording}&quot;)
    os.system(f&quot;play {recording}&quot;)
    review_audio(slug)


def delete_audio(slug):
    recording = get_recording_file_name(slug)
    os.remove(recording)


def review_audio(slug):
    review_menu = [&quot;(p)lay&quot;, &quot;(a)ccept&quot;, &quot;(r)eccord again&quot;, &quot;(t)runcate&quot;]
    click.echo(slug)
    for i in review_menu:
        click.echo(i)
    menu_action = input(&quot;&gt;&gt; &quot;)
    if menu_action == &quot;p&quot;:
        play_audio(slug)
    elif menu_action == &quot;a&quot;:
        exit()
    elif menu_action == &quot;r&quot;:
        delete_audio(slug)
        find_and_record_next()
    elif menu_action == &quot;t&quot;:
        truncate_audio(slug)
    else:
        review_audio(slug)


def record_audio(slug):
    new_recording = get_recording_file_name(slug)
    click.echo(f&quot;Creating {new_recording}&quot;)
    click.echo(&quot;press Enter to start then CRTL-C to quit&quot;)
    wait_for_input()
    os.system(f&quot;rec {new_recording}&quot;)


def record_silent_audio():
    silent_recording = f&quot;{recording_directory_name}/silence.{recording_format}&quot;
    click.echo(&quot;RECORD 5 SECONDS OF SILENCE \n&quot; * 5)
    click.echo(&quot;press Enter to start&quot;)
    wait_for_input()
    os.system(f&quot;rec {silent_recording} trim 0 5&quot;)
    os.system(
        f&quot;sox {silent_recording} -n noiseprof {recording_directory_name}/noise.prof&quot;
    )


def load_script():
    linetext = &quot;&quot;
    with open(script_file) as script_file_reader:
        for line in script_file_reader.readlines():

            if not line.strip():

                slug = linetext[:40].title()
                segment_name = &quot;&quot;.join(filter(str.isalnum, slug))
                add_slug_text(segment_name, linetext)
                linetext = &quot;&quot;

            else:
                linetext += f&quot;{line}  \n&quot;


def combine_recordings_for_export():
    recording_list = []
    combined_recording = f&quot;{recording_directory_name}/combined.{recording_format}&quot;
    for slug, text in script_segments.items():
        recording = get_recording_file_name(slug)
        recording_list.append(recording)
    recording_list_string = &quot; &quot;.join(recording_list)
    print(recording_list_string)
    SOX_CMD = f&quot;sox -V3 {recording_list_string}  {combined_recording} noisered {recording_directory_name}/noise.prof 0.21 norm -10&quot;
    click.echo(SOX_CMD)
    os.system(SOX_CMD)


def find_and_record_next():
    for slug, text in script_segments.items():
        if recording_exists(slug):
            continue
        click.clear()
        click.echo(slug)
        click.echo(&quot;*&quot; * 40)
        click.echo(text)
        click.echo(&quot;*&quot; * 40)
        record_audio(slug)
        review_audio(slug)


@click.group()
def cli():
    test_sox_exists()
    pass


@cli.command()
def combine():
    &quot;Combine Segments into single audio file&quot;
    combine_recordings_for_export()


@cli.command()
def record():
    &quot;Record next unrecorded segment&quot;
    if noise_profile_missing():
        record_silent_audio()
    find_and_record_next()


@cli.command()
def silence():
    &quot;Generate noise profile&quot;
    record_silent_audio()



@cli.command()
def review():
    &quot;Print segments&quot;

    for slug, text in script_segments.items():
        click.clear()
        click.echo(slug)
        click.echo(&quot;*&quot; * 40)
        click.echo()
        click.echo(text)
        wait_for_input()


if __name__ == &quot;__main__&quot;:
    project_prep()
    load_script()
    cli()
</code></pre>
