Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Github Action for Image Creation on GitHub #53

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

name: Create and publish a Docker image

on:
push:
branches:
- 'master'
- 'main'
- 'dev'

tags:
- 'v*'
- 'v*.*'
- 'v*.*.*'
- '*'
- '*.*'
- '*.*.*'
pull_request:
branches:
- 'main'
- 'dev'


env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v3

# https://github.com/docker/setup-qemu-action
- name: Set up QEMU
uses: docker/setup-qemu-action@v1

# https://github.com/docker/setup-buildx-action
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1

- name: Log in to the Container registry
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}


- name: Build and push Docker image
uses: docker/build-push-action@v2
with:
context: .
push: true
platforms: linux/amd64,linux/arm/v7
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,24 @@ aws.properties*

# python venv
venv/
.venv/

*.log

# Don't check in rendered mp3 files
mp3/
*.mp3
example-00001.txt
example-sentences.txt
example-structure.txt
sentence-15.txt
sentence-17.txt
sentence-20.txt
sentence-22.txt
sentence-25.txt
sentence-28.txt
sentence-30.txt
sentence-35.txt
sentence-40.txt
sentence-45.txt
sentence-50.txt
12 changes: 11 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@ FROM python:3

RUN apt update
RUN apt -y install lame ffmpeg ebook2cw
RUN pip install boto3
RUN pip3 install --upgrade pip

ADD requirements.txt .
RUN pip3 install -r requirements.txt

RUN mkdir -p /opt/morse-code-ninja
WORKDIR /opt/morse-code-ninja
RUN mkdir mp3
RUN mkdir /input
RUN mkdir /output

ADD . .

#ENTRYPOINT ["./entrypoint.sh", "-i", "input.txt"]
8 changes: 8 additions & 0 deletions Morse-Code-Ninja.code-workspace
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"folders": [
{
"path": "."
}
],
"settings": {}
}
45 changes: 11 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,21 @@ The software used to generate Morse Code Ninja practice sets as found on
[Kurt Zoglmann's YouTube Channel](https://www.youtube.com/channel/UCXrTMfMEhkC9rVyQNU5aZlA).

# Required Software
These must be installed and available in your Shell's PATH.
* [ebook2cw](https://fkurz.net/ham/ebook2cw.html)
* [ffmpeg](https://ffmpeg.org)
* [lame](https://lame.sourceforge.io/)
* [Perl 5](https://www.perl.org)
* [Python 3](https://www.python.org)
* [Boto3](https://aws.amazon.com/sdk-for-python/)

# Cloud Setup
1. Set up an AWS Account. Feel free to follow these
[instructions](https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/).

2. Create an [IAM user](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html).
For ease, I recommend using the "console" for this. During creation, give
the IAM user "Programmatic access." When prompted save the **key ID** and **secret access key**.
During the creation, attach the **AmazonPollyFullAccess** policy to the user.

3. Edit the aws.properties file. Set the **key ID** and **secret access key**. As an alternative,
you may define AWS_KEY_ID and AWS_SECRET_ACCESS_KEY as environmental variables.

4. Run this command to make sure that you don't accidentally check in your key! `git update-index --assume-unchanged aws.properties`
# Setup macOS
python3 -m venv .venv
source .venv/bin/activate
pip3 install -r requirements.txt
brew install ebook2cw lame ffmpeg

perl render.pl -i practice-sets/instant-qso-element-courses/Course\ 01\ -\ Lesson\ 022\ -\ RST\ -\ Introduce\ New\ Element.txt

# Usage
##### Run in docker (WIP)
docker build . -t mp3
docker run --rm -it -v $PWD/practice-sets/instant-qso-element-courses/Course\ 01\ -\ Lesson\ 022\ -\ RST\ -\ Introduce\ New\ Element.txt:/input/input.txt:ro -v $PWD/mp3:/output mp3 ./entrypoint.sh -i input.txt


#### EXAMPLE:
perl render.pl -i example.txt

Expand All @@ -40,8 +31,6 @@ you may define AWS_KEY_ID and AWS_SECRET_ACCESS_KEY as environmental variables.
[--norepeat] [--nospoken] [--nocourtesytone] [-e NEURAL | STANDARD]
[--sm] [--ss] [--sv] [-x] [--lang ENGLISH | SWEDISH]

Uses AWS Polly and requires valid credentials in the aws.properties file.<br/><br/>

#### OPTIONS:

##### Required:
Expand Down Expand Up @@ -82,15 +71,3 @@ multiple copies are executing at the same time using the same output directory.
Be aware that the script can create a huge number of temporary files, which is proportional to the input file. Some types of filesystems will deal with this better than others.

This set of scripts works on Linux and macOS.

# Docker Usage
This usage limits the required software to:
- [Docker](https://www.docker.com/get-started/)
- [AWS Cloud Setup](#cloud-setup)

To run
```
./morse-code-ninja.sh -i example.txt
```

The script `morse-code-ninja.sh` wraps the docker compose command passing all arguments to the dockerized `render.pl`
2 changes: 0 additions & 2 deletions aws.properties

This file was deleted.

10 changes: 0 additions & 10 deletions docker-compose.yml

This file was deleted.

5 changes: 5 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash
cp /input/* .
perl render.pl "$@"
mv *.mp3 /output

3 changes: 0 additions & 3 deletions morse-code-ninja.sh

This file was deleted.

2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
gtts
boto3
77 changes: 43 additions & 34 deletions text2speech.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3

import boto3
import sys
Expand All @@ -8,6 +8,8 @@
from os import environ
import shutil
import subprocess
from gtts import gTTS


sentence_filename = sys.argv[1]
engine_type = sys.argv[2].lower() # needs to be: standard | neural
Expand All @@ -27,28 +29,28 @@
separator = "="
aws_properties = {}

if "AWS_ACCESS_KEY_ID" in environ and "AWS_SECRET_ACCESS_KEY" in environ:
aws_properties['aws_access_key_id'] = environ['AWS_ACCESS_KEY_ID']
aws_properties['aws_secret_access_key'] = environ['AWS_SECRET_ACCESS_KEY']
else:
try:
with open('aws.properties') as property_file:

for line in property_file:
if separator in line:
name, value = line.split(separator, 1)
aws_properties[name.strip()] = value.strip()
except IOError as e:
print(f"I/O error reading aws.properties: {e.errno}, {e.strerror}")

print("text2speech script does not have AWS credentials.")
sys.exit(ioError)

if aws_properties['aws_access_key_id'] == 'CHANGE_ME' or aws_properties['aws_secret_access_key'] == 'CHANGE ME':
print("**********************************************************************************")
print("text2speech script does NOT have AWS credentials. Set properties in aws.properties")
print("**********************************************************************************")
sys.exit(ioError)
# if "AWS_ACCESS_KEY_ID" in environ and "AWS_SECRET_ACCESS_KEY" in environ:
# aws_properties['aws_access_key_id'] = environ['AWS_ACCESS_KEY_ID']
# aws_properties['aws_secret_access_key'] = environ['AWS_SECRET_ACCESS_KEY']
# else:
# try:
# with open('aws.properties') as property_file:

# for line in property_file:
# if separator in line:
# name, value = line.split(separator, 1)
# aws_properties[name.strip()] = value.strip()
# except IOError as e:
# print(f"I/O error reading aws.properties: {e.errno}, {e.strerror}")

# print("text2speech script does not have AWS credentials.")
# sys.exit(ioError)

# if aws_properties['aws_access_key_id'] == 'CHANGE_ME' or aws_properties['aws_secret_access_key'] == 'CHANGE ME':
# print("**********************************************************************************")
# print("text2speech script does NOT have AWS credentials. Set properties in aws.properties")
# print("**********************************************************************************")
# sys.exit(ioError)

sha256_hash = hashlib.sha256()

Expand All @@ -62,18 +64,25 @@

def render(cache_filename, voice_id, text_type, text):
if not os.path.exists(cache_filename):
polly_client = boto3.Session(aws_access_key_id=aws_properties['aws_access_key_id'],
aws_secret_access_key=aws_properties['aws_secret_access_key'],
region_name='us-east-1').client('polly')
if text_type is None:
response = polly_client.synthesize_speech(Engine=engine_type, VoiceId=voice_id, OutputFormat='mp3', Text=text)
else:
response = polly_client.synthesize_speech(Engine=engine_type, VoiceId=voice_id, OutputFormat='mp3',
TextType=text_type, Text=text)
# polly_client = boto3.Session(aws_access_key_id=aws_properties['aws_access_key_id'],
# aws_secret_access_key=aws_properties['aws_secret_access_key'],
# region_name='us-east-1').client('polly')
# if text_type is None:
# response = polly_client.synthesize_speech(Engine=engine_type, VoiceId=voice_id, OutputFormat='mp3', Text=text)
# else:
# response = polly_client.synthesize_speech(Engine=engine_type, VoiceId=voice_id, OutputFormat='mp3',
# TextType=text_type, Text=text)

# file = open(temp_filename, 'wb')
# file.write(response['AudioStream'].read())
# file.close()

print ("Create the mp3 file")
language = 'en'
myobj = gTTS(text=text, lang=language, slow=False)
myobj.save(temp_filename)


file = open(temp_filename, 'wb')
file.write(response['AudioStream'].read())
file.close()

subprocess.run(['lame', '--resample', '44.1', '-a', '-b', '256',
temp_filename,
Expand Down
2 changes: 1 addition & 1 deletion upload2s3.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3

import boto3
import os
Expand Down