-1

I would like to make a simple web application using Amazon Polly.

This is my directory structure.

.
├── data.txt
├── main.py
└── templates
    └── index.html

This is my main.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from flask import Flask
from flask import render_template
from flask import request
from flask import send_file
from boto3 import Session
import os
import csv
from io import StringIO
from contextlib import closing

app = Flask(__name__)

@app.route("/", methods=['POST', 'GET'])
def index():
    if request.method == "POST":
        ## Sessionを作成
        session = Session(aws_access_key_id='???', aws_secret_access_key='???', region_name='us-east-1')
        polly = session.client("polly")

        ## polly.mp3が存在した場合は削除
        if os.path.isfile('/tmp/output.mp3'):
            os.remove ('/tmp/output.mp3')

        ## data.txtをCSVとして読み込む
        f = open('data.txt')
        reader = csv.reader(f)

        ## CSVを1行ずつ読み込む
        for line in reader:

            ## CSVの1つめの項目で男女を切り替え
            if line[0] == "M":
                voice="Matthew"
            elif line[0] == "F":
                voice="Joanna"

            ## 音声データを作成
            response = polly.synthesize_speech(
                        Text = '<speak><amazon:domain name="conversational"><prosody rate="slow">' + line[1].decode("utf-8") + '</prosody></amazon:domain></speak>',
                        OutputFormat = "mp3",
                        VoiceId = voice, Engine='neural', TextType='ssml')

            ## polly.mp3に追加書き込み
            if "AudioStream" in response:
                with closing(response["AudioStream"]) as stream:
                    data = stream.read()
                    fw = open("/tmp/output.mp3", "a+")
                    fw.write(data)
                    fw.close()

        f.close()

        return send_file("/tmp/output.mp3",as_attachment=True)
    else:
        return render_template("index.html")

if __name__ == "__main__":
    app.run()

This is my index.html

<html>
    <body>
        <form action="/" method="POST">
            <input type="submit" value="download">
        </form>
    </body>
</html>

When I tested this code locally (Windows10, Windows Subsystems for Linux, Debian, Python3.7.6), I got this error message.

  File "main.py", line 41, in index    
    Text = '<speak><amazon:domain name="conversational"><prosody rate="slow">' + line[1].decode("utf-8") + '</prosody></amazon:domain></speak>',
AttributeError: 'str' object has no attribute 'decode'

So, I searched the Internet and found this page. 'str' object has no attribute 'decode'. Python 3 error?

I drop the .decode('utf-8') part:

Text = '<speak><amazon:domain name="conversational"><prosody rate="slow">' + line[1] + '</prosody></amazon:domain></speak>',

Then I got a new error message.

  File "main.py", line 50, in index
    fw.write(data)  
TypeError: write() argument must be str, not bytes

I searched "write() argument must be str, not bytes". A lot of websites suggest that I should put the .decode('utf-8') part.

Could you give me any information or suggestion?

Dane Brouwer
  • 2,827
  • 1
  • 22
  • 30
Kazuaki Suzuki
  • 1,025
  • 6
  • 19
  • 33
  • 1
    `.decode()` converts `bytes` to `string` and you have `string` in variable so it doesn't have `.decode()` - it has only `.encode()` to convert `string` to `bytes` but you don't need it. – furas Jan 25 '20 at 05:02
  • 2
    you have to open file in `bytes` mode `open(... , "b")` - `open("/tmp/output.mp3", "ba+")` to write bytes. And don't try to convert bytes to string because it can create incorrect file. – furas Jan 25 '20 at 05:04
  • 1
    BTW: there is no sense to open file in append mode `open(..., "a")` because you can't append one `mp3` to other `mp3` this way. It will create incorrect file. You would need tools like [ffmpeg](https://github.com/kkroening/ffmpeg-python) or [moviepy](https://zulko.github.io/moviepy/) to read all `mp3` to memory, join them and save all as single file – furas Jan 25 '20 at 05:06
  • Thank you very much @furas . Your solution worked perfectly! I use `open("/tmp/output.mp3", "ba+")` . If you could, could you tell me the difference between `open("/tmp/output.mp3", "ba+")` and`open("/tmp/output.mp3", "a")` ? I read your comment, searched the Internet and read websites which explain the difference of a, b and +. However, I can't understand. – Kazuaki Suzuki Jan 25 '20 at 07:28
  • normally it opens in `text` mode and it may convert some chars - ie. end of line from `\n` to `\r\n` or `\n\r` - because Window, Linux and Mac may uses different codes for new line. Using `b` it opens in `byte` mode so it saves all bytes without any changes. And it doesn't try to convert from `UTF-8` or `CP1250` to unicode - (in text files Windows, Linux, Mac can use different encoding). – furas Jan 25 '20 at 07:37

1 Answers1

0

As @furas commented,

open("/tmp/output.mp3", "ba+")

worked perfectly. Thank you @furas!

Kazuaki Suzuki
  • 1,025
  • 6
  • 19
  • 33