Forum Archive

Is it possible to play a mp3 file?

laihaiguang

I searched the forum and found some old code snippets. However, they all do not work. I just want to know if it is possible to play a mp3 file with Pythonista3.
Thanks!

cvp

@laihaiguang that is working

import sound
path = 'Johnny Rivers - John Lee Hooker - Live At The Whiskey A Go Go.mp3'
mp3 = sound.Player(path)
mp3.play()
laihaiguang

@cvp Thanks! It works. Here’s my code:

import sound
import os

path = os.path.expanduser('~/Documents/hello.mp3')
mp3 = sound.Player(path)
mp3.play()

Drizzel

I am trying the very same, but for some reason my code does not work. I tried with .m4a and .mp3 files. Any ideas why?

import os
import sound
path = os.listdir()[0]
mp3 = sound.Player(path)
mp3.play()

os.listdir() returns [‘testmpthree.mp3’, ‘Play.py’]
The error lies in line 4, the error is string object not callable

cvp

@Drizzel I have the same IF I do that:

mp3 = sound.Player(path())

Traceback (most recent call last):
File "/private/var/mobile/Containers/Shared/AppGroup/668A7D98-7216-47ED-917D-AA0B6173167E/Pythonista3/Documents/test5.py", line 4, in
mp3 = sound.Player(path())
TypeError: 'str' object is not callable

Drizzel

@cvp No idea why, but I put the brackets, ran it once, got the error, removed the brackets, restarted pythonista, ran it, and it worked. Honestly, I can’t explain it. Nevertheless, thanks a lot.
However, it won’t run m4a files (it doesn’t give me an error, but I just don’t hear anything). Do you know how to fix that? Or do I have to convert it to mp3 first?

cvp

@Drizzel this works

JonB

@Drizzel have you tried looking at it in the file browser? Does the quicklook play it?

Also, does youtubedl write the file, or just give you bytes? If you write the file yourself be sure to open it in binary write mode.

I read somewhere that youtubedl might sometimes have nonstandard headers in m4a files that need to get fixed. iOS does support m4a.

Drizzel

I can play the audio if I manually quickplay it in the file browser.

This here is the code to download and play a song from YouTube:

from __future__ import unicode_literals
import youtube_dl
import os
from sys import argv



# Download data and config
download_options = {
    'format': 'm4a',
    'outtmpl': '%(title)s.%(ext)s',
    'nocheckcertificate': True,
    'preferredcodec': 'best',
}

# Download Songs
with youtube_dl.YoutubeDL(download_options) as dl:
    with open('songs.txt', 'r') as f:
        for song_url in f:
            dl.download([song_url])

import sound
path = os.listdir()[0]
audio = sound.Player(path)
audio.play()
print(audio.playing)

songs.txt just contains some links to music on YouTube.

Every time I run this, I get this message:
WARNING: aAiVsqfbn5g: writing DASH m4a. Only some players support this container. Install ffmpeg or avconv to fix this automatically.
The last line of code always prints that the Audio is not playing when trying it with m4a fields downloaded from YouTube. The file @cvp used works flawlessly, just as some mp3 file I used for testing.
Thanks a lot for trying to solve this, really

JonB

is your goal just to play it? or play it in a scene or something?

You could try opening it in a webview, (pass the os.path.abspath(path) to webview.load_url)

Drizzel

My goal is just play it offline, I’m trying to code something of a Spotify alternative for myself. Although I will at some point implement it in a ui

cvp

@Drizzel you can use Webview with a file

import ui
import os
v = ui.WebView()
v.present('sheet')
v.load_url(os.path.abspath('sample.m4a'))
Drizzel

@cvp great, that actually works :)
Is there any way to stop, skip, and see information like how long the audio is and for how long it has been playing for? I have been searching for these things, but I can’t really find them. There is a stop() function, but for some reason it doesn’t change anything

cvp

@Drizzel my post was only a short example of the last answer of @JonB

cvp

I suppose you have set the frame bigger, so you see all buttons of a player

JonB

You ought to be able to write a JavaScript or HTML 5 player, which you could then control via python using be eval_js. I suspect you can find someone's jukebox JavaScript code that you could just use.

cvp

A good begin to find here

Drizzel

Thanks a lot to both of you, that helped me loads! My html skills are rather limited and JavaScript is absolutely unfamiliar to me, but I will manage somehow, now that I have a good base I can start from :)

cvp

@Drizzel very quick and dirty sample, that's the first time I use js 😢
```
import ui
import os
import time
import threading

class my_thread(threading.Thread):
def init(self,my_view):
threading.Thread.init(self)
self.my_view = my_view
def run(self):
while not self.stop:
time.sleep(1)
try:
d = webview.eval_js('document.getElementById("myaudio").duration;')
if d == 'NaN':
continue
c = webview.eval_js('document.getElementById("myaudio").currentTime;')
c = float(c)
d = float(d)
self.my_view['currentTime'].value = c/d
self.my_view['timers'].text = str(int(c)) + '/' + str(int(d)) + ' sec.'
except Exception as e:
pass

path = 'Johnny Rivers - John Lee Hooker - Live At The Whiskey A Go Go.mp3'
absfilepath=os.path.abspath(path)

TEMPLATE='''

'''

webview = ui.WebView(name='webview')

bplay = ui.ButtonItem()
bplay.image = ui.Image.named('iob:play_32')
def bplay_action(sender):
webview.eval_js('document.getElementById("myaudio").play();')
bplay.action = bplay_action

bpause = ui.ButtonItem()
bpause.image = ui.Image.named('iob:ios7_pause_32')
def bpause_action(sender):
webview.eval_js('document.getElementById("myaudio").pause();')
bpause.action = bpause_action

bstop = ui.ButtonItem()
bstop.image = ui.Image.named('iob:stop_32')
def bstop_action(sender):
# audio player of html5 does not have a stop function
webview.eval_js('document.getElementById("myaudio").pause();')
webview.eval_js('document.getElementById("myaudio").currentTime = 0;')
bstop.action = bstop_action

webview.right_button_items = [bplay,bpause,bstop]

currentTime = ui.Slider(name='currentTime')
currentTime.frame = (10,10,200,32)
def currentTime_action(sender):
d = webview.eval_js('document.getElementById("myaudio").duration;')
c = currentTime.value * float(d)
webview.eval_js('document.getElementById("myaudio").currentTime = ' + str(c) + ';')
currentTime.action = currentTime_action
webview.add_subview(currentTime)

timers = ui.Label(name='timers')
timers.frame = (220,10,370,32)
webview.add_subview(timers)

html = TEMPLATE.replace('{{FPATH}}', absfilepath)
webview.load_html(html)
webview.name = path
webview.frame = (0,0,600,100)

webview.present('sheet')
t = my_thread(webview)
t.stop = False
t.start()
webview.wait_modal()

force stop player if not done

webview.eval_js('document.getElementById("myaudio").pause();')
webview.eval_js('document.getElementById("myaudio").currentTime = 0;')
t.stop = True # force end of thread```

Drizzel

@cvp impressive, I wouldn’t have managed that this quickly :)

This question may be a bit off topic, but do you have any idea why YouTube-dl downloads files with twice the length of the original YouTube video? The first half contains the audio, and the second half simply contains no sound.

from __future__ import unicode_literals
import youtube_dl
import os
from sys import argv



# Download data and config
download_options = {
    'format': 'm4a',
    'outtmpl': '%(title)s.%(ext)s',
    'nocheckcertificate': True,
    'preferredcodec': 'best',
}

# Download Songs
with youtube_dl.YoutubeDL(download_options) as dl:
    with open('songs.txt', 'r') as f:
        for song_url in f:
            dl.download([song_url])
cvp

@Drizzel No idea but a Google search on "youtube m4a double size" shows you aren't alone with this problem...

Drizzel

@cvp

```
import ui
import os
import time
import threading

class my_thread(threading.Thread):
def init(self,my_view):
threading.Thread.init(self)
self.my_view = my_view
def run(self):
while not self.stop:
time.sleep(1)
try:
d = webview.eval_js('document.getElementById("myaudio").duration;')
if d == 'NaN':
continue
c = webview.eval_js('document.getElementById("myaudio").currentTime;')
c = float(c)
d = float(d)
self.my_view['currentTime'].value = c/d
self.my_view['timers'].text = str(int(c)) + '/' + str(int(d)) + ' sec.'
except Exception as e:
pass

path = 'Johnny Rivers - John Lee Hooker - Live At The Whiskey A Go Go.mp3'
absfilepath=os.path.abspath(path)

TEMPLATE='''

'''

webview = ui.WebView(name='webview')

bplay = ui.ButtonItem()
bplay.image = ui.Image.named('iob:play_32')
def bplay_action(sender):
webview.eval_js('document.getElementById("myaudio").play();')
bplay.action = bplay_action

bpause = ui.ButtonItem()
bpause.image = ui.Image.named('iob:ios7_pause_32')
def bpause_action(sender):
webview.eval_js('document.getElementById("myaudio").pause();')
bpause.action = bpause_action

bstop = ui.ButtonItem()
bstop.image = ui.Image.named('iob:stop_32')
def bstop_action(sender):
# audio player of html5 does not have a stop function
webview.eval_js('document.getElementById("myaudio").pause();')
webview.eval_js('document.getElementById("myaudio").currentTime = 0;')
bstop.action = bstop_action

webview.right_button_items = [bplay,bpause,bstop]

currentTime = ui.Slider(name='currentTime')
currentTime.frame = (10,10,200,32)
def currentTime_action(sender):
d = webview.eval_js('document.getElementById("myaudio").duration;')
c = currentTime.value * float(d)
webview.eval_js('document.getElementById("myaudio").currentTime = ' + str(c) + ';')
currentTime.action = currentTime_action
webview.add_subview(currentTime)

timers = ui.Label(name='timers')
timers.frame = (220,10,370,32)
webview.add_subview(timers)

html = TEMPLATE.replace('{{FPATH}}', absfilepath)
webview.load_html(html)
webview.name = path
webview.frame = (0,0,600,100)

webview.present('sheet')
t = my_thread(webview)
t.stop = False
t.start()
webview.wait_modal()

force stop player if not done

webview.eval_js('document.getElementById("myaudio").pause();')
webview.eval_js('document.getElementById("myaudio").currentTime = 0;')
t.stop = True # force end of thread```

Apologies if this seems like a stupid question, but how do I another song? Like, if a ui button is pressed, another song starts playing. I’m relatively new to coding in general, as I started learning python 2 years ago

cvp

@Drizzel try this

import ui
import os
import time
import threading

class my_thread(threading.Thread):
    def __init__(self,my_view):
        threading.Thread.__init__(self)
        self.my_view = my_view
    def run(self):
        while not self.stop:
            time.sleep(1)
            try:
                d = webview.eval_js('document.getElementById("myaudio").duration;')
                if d == 'NaN':
                    continue
                c = webview.eval_js('document.getElementById("myaudio").currentTime;')
                c = float(c)
                d = float(d)
                self.my_view['currentTime'].value = c/d
                self.my_view['timers'].text = str(int(c)) + '/' + str(int(d)) + ' sec.'
            except Exception as e:
                pass

paths = os.listdir()
musics = []
for path in paths:
    if path[-4:].lower() in ('.mp3','.m4a'):
        musics.append(path)
#print(musics)
i_music = 0

TEMPLATE='''
<audio id="myaudio" autoplay preload="auto">
    <source src="file://{{FPATH}}" type="audio/mp3">
</audio>
'''

webview = ui.WebView(name='webview')

def set_music():
    path = musics[i_music]
    absfilepath=os.path.abspath(path)
    html = TEMPLATE.replace('{{FPATH}}', absfilepath)
    webview.load_html(html)
    webview.name = path

bplay = ui.ButtonItem()
bplay.image = ui.Image.named('iob:play_32')
def bplay_action(sender):
    webview.eval_js('document.getElementById("myaudio").play();')
bplay.action = bplay_action

bpause = ui.ButtonItem()
bpause.image = ui.Image.named('iob:ios7_pause_32')
def bpause_action(sender):
    webview.eval_js('document.getElementById("myaudio").pause();')
bpause.action = bpause_action

bstop = ui.ButtonItem()
bstop.image = ui.Image.named('iob:stop_32')
def bstop_action(sender):
    # audio player of html5 does not have a stop function
    webview.eval_js('document.getElementById("myaudio").pause();')
    webview.eval_js('document.getElementById("myaudio").currentTime = 0;')
bstop.action = bstop_action

bnext = ui.ButtonItem()
bnext.image = ui.Image.named('iob:ios7_skipforward_32')
def bnext_action(sender):
    global i_music
    bstop_action('simulate')
    i_music = i_music + 1
    if i_music >= len(musics):
        i_music = 0
    set_music()
bnext.action = bnext_action

webview.right_button_items = [bplay,bpause,bstop,bnext]

currentTime = ui.Slider(name='currentTime')
currentTime.frame = (10,10,200,32)
def currentTime_action(sender):
    d = webview.eval_js('document.getElementById("myaudio").duration;')
    c = currentTime.value * float(d)
    webview.eval_js('document.getElementById("myaudio").currentTime = ' + str(c) + ';') 
currentTime.action = currentTime_action
webview.add_subview(currentTime)

timers = ui.Label(name='timers')
timers.frame = (220,10,370,32)
webview.add_subview(timers)

webview.frame = (0,0,600,100)

set_music()

webview.present('sheet')
#time.sleep(0.5)
t = my_thread(webview)
t.stop = False
t.start()
webview.wait_modal()
# force stop player if not done
webview.eval_js('document.getElementById("myaudio").pause();')
webview.eval_js('document.getElementById("myaudio").currentTime = 0;')
t.stop = True   # force end of thread
Meloka

You can try and download the mp3 file again with y2mate

Bella Gorden

Any music downloaded from streaming services is not saved in the format of MP3 files. But you could use a streaming music downloader to get music from them to MP3. For example, with the help of Spotify MP3 Converter, you could download music from Spotify to MP3. Then you could play an MP3 file.