Skip to content
Merged
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
30 changes: 9 additions & 21 deletions discord/sinks/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import sys
import threading
import time
import io
from ..types import snowflake

from .errors import SinkException
Expand Down Expand Up @@ -125,9 +126,7 @@ class AudioData:
"""

def __init__(self, file):
self.file = open(file, "ab")
self.dir_path = os.path.split(file)[0]

self.file = file
self.finished = False

def write(self, data):
Expand All @@ -141,16 +140,12 @@ def write(self, data):
def cleanup(self):
if self.finished:
raise SinkException("The AudioData is already finished writing.")
self.file.close()
self.file = os.path.join(self.dir_path, self.file.name)
self.file.seek(0)
self.finished = True

def on_format(self, encoding):
if not self.finished:
raise SinkException("The AudioData is still writing.")
name = os.path.split(self.file)[1]
name = name.split(".")[0] + f".{encoding}"
self.file = os.path.join(self.dir_path, name)


class Sink(Filters):
Expand All @@ -169,13 +164,8 @@ class Sink(Filters):
finished_callback,
ctx.channel,
)

.. versionadded:: 2.1

Parameters
----------
output_path: :class:`string`
A path to where the audio files should be output.
.. versionadded:: 2.1

Raises
------
Expand All @@ -184,12 +174,11 @@ class Sink(Filters):
Audio may only be formatted after recording is finished.
"""

def __init__(self, *, output_path="", filters=None):
def __init__(self, *, filters=None):
if filters is None:
filters = default_filters
self.filters = filters
Filters.__init__(self, **self.filters)
self.file_path = output_path
self.vc = None
self.audio_data = {}

Expand All @@ -200,8 +189,7 @@ def init(self, vc): # called under listen
@Filters.container
def write(self, data, user):
if user not in self.audio_data:
ssrc = self.vc.get_ssrc(user)
file = os.path.join(self.file_path, f"{ssrc}.pcm")
file = io.BytesIO()
self.audio_data.update({user: AudioData(file)})

file = self.audio_data[user]
Expand All @@ -215,8 +203,8 @@ def cleanup(self):

def get_all_audio(self):
"""Gets all audio files."""
return [os.path.realpath(x.file) for x in self.audio_data.values()]
return [x.file for x in self.audio_data.values()]

def get_user_audio(self, user: snowflake.Snowflake):
"""Gets the audio file(s) of one specific user."""
return os.path.realpath(self.audio_data.pop(user))
return os.path.realpath(self.audio_data.pop(user))
4 changes: 3 additions & 1 deletion discord/sinks/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,16 @@ class WaveSinkError(SinkException):
.. versionadded:: 2.1
"""


class M4ASinkError(SinkException):
"""Exception thrown when a exception occurs with :class:`M4ASink`

.. versionadded:: 2.1
"""


class MKASinkError(SinkException):
"""Exception thrown when a exception occurs with :class:`MKAsSink`

.. versionadded:: 2.1
"""
"""
30 changes: 16 additions & 14 deletions discord/sinks/m4a.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""
import io
import os
import subprocess
import time

from .core import CREATE_NO_WINDOW, Filters, Sink, default_filters
from .errors import M4ASinkError
Expand All @@ -35,26 +37,20 @@ class M4ASink(Sink):

.. versionadded:: 2.1

Parameters
----------
output_path: :class:`string`
A path to where the audio files should be output.

Raises
------
ClientException
An invalid encoding type was specified.
Audio may only be formatted after recording is finished.
"""

def __init__(self, *, output_path="", filters=None):
def __init__(self, *, filters=None):
if filters is None:
filters = default_filters
self.filters = filters
Filters.__init__(self, **self.filters)

self.encoding = "m4a"
self.file_path = output_path
self.vc = None
self.audio_data = {}

Expand All @@ -63,7 +59,7 @@ def format_audio(self, audio):
raise M4ASinkError(
"Audio may only be formatted after recording is finished."
)
m4a_file = audio.file.split(".")[0] + ".m4a"
m4a_file = f"{time.time()}.tmp"
args = [
"ffmpeg",
"-f",
Expand All @@ -73,24 +69,30 @@ def format_audio(self, audio):
"-ac",
"2",
"-i",
audio.file,
"-",
"-f",
"ipod",
m4a_file,
]
process = None
if os.path.exists(m4a_file):
os.remove(
m4a_file
) # process will get stuck asking whether or not to overwrite, if file already exists.
try:
process = subprocess.Popen(args, creationflags=CREATE_NO_WINDOW)
process = subprocess.Popen(args, creationflags=CREATE_NO_WINDOW,
stdin=subprocess.PIPE)
except FileNotFoundError:
raise M4ASinkError("ffmpeg was not found.") from None
except subprocess.SubprocessError as exc:
raise M4ASinkError(
"Popen failed: {0.__class__.__name__}: {0}".format(exc)
) from exc

process.wait()
process.communicate(audio.file.read())

with open(m4a_file, "rb") as f:
audio.file = io.BytesIO(f.read())
audio.file.seek(0)
os.remove(m4a_file)

os.remove(audio.file)
audio.on_format(self.encoding)
audio.on_format(self.encoding)
33 changes: 13 additions & 20 deletions discord/sinks/mka.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""
import io
import os
import subprocess

Expand All @@ -35,26 +36,20 @@ class MKASink(Sink):

.. versionadded:: 2.1

Parameters
----------
output_path: :class:`string`
A path to where the audio files should be output.

Raises
------
ClientException
An invalid encoding type was specified.
Audio may only be formatted after recording is finished.
"""

def __init__(self, *, output_path="", filters=None):
def __init__(self, *, filters=None):
if filters is None:
filters = default_filters
self.filters = filters
Filters.__init__(self, **self.filters)

self.encoding = "mka"
self.file_path = output_path
self.vc = None
self.audio_data = {}

Expand All @@ -63,7 +58,6 @@ def format_audio(self, audio):
raise MKASinkError(
"Audio may only be formatted after recording is finished."
)
mka_file = audio.file.split(".")[0] + ".mka"
args = [
"ffmpeg",
"-f",
Expand All @@ -73,24 +67,23 @@ def format_audio(self, audio):
"-ac",
"2",
"-i",
audio.file,
mka_file,
"-",
"-f",
"matroska",
"pipe:1"
]
process = None
if os.path.exists(mka_file):
os.remove(
mka_file
) # process will get stuck asking whether or not to overwrite, if file already exists.
try:
process = subprocess.Popen(args, creationflags=CREATE_NO_WINDOW)
process = subprocess.Popen(args, creationflags=CREATE_NO_WINDOW,
stdout=subprocess.PIPE, stdin=subprocess.PIPE)
except FileNotFoundError:
raise MKASinkError("ffmpeg was not found.") from None
except subprocess.SubprocessError as exc:
raise MKASinkError(
"Popen failed: {0.__class__.__name__}: {0}".format(exc)
) from exc

process.wait()

os.remove(audio.file)
audio.on_format(self.encoding)
out = process.communicate(audio.file.read())[0]
out = io.BytesIO(out)
out.seek(0)
audio.file = out
audio.on_format(self.encoding)
33 changes: 13 additions & 20 deletions discord/sinks/mkv.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
"""
import io
import os
import subprocess

Expand All @@ -35,26 +36,20 @@ class MKVSink(Sink):

.. versionadded:: 2.1

Parameters
----------
output_path: :class:`string`
A path to where the audio files should be output.

Raises
------
ClientException
An invalid encoding type was specified.
Audio may only be formatted after recording is finished.
"""

def __init__(self, *, output_path="", filters=None):
def __init__(self, *, filters=None):
if filters is None:
filters = default_filters
self.filters = filters
Filters.__init__(self, **self.filters)

self.encoding = "mkv"
self.file_path = output_path
self.vc = None
self.audio_data = {}

Expand All @@ -63,7 +58,6 @@ def format_audio(self, audio):
raise MKVSinkError(
"Audio may only be formatted after recording is finished."
)
mkv_file = audio.file.split(".")[0] + ".mkv"
args = [
"ffmpeg",
"-f",
Expand All @@ -73,24 +67,23 @@ def format_audio(self, audio):
"-ac",
"2",
"-i",
audio.file,
mkv_file,
"-",
"-f",
"matroska",
"pipe:1"
]
process = None
if os.path.exists(mkv_file):
os.remove(
mkv_file
) # process will get stuck asking whether or not to overwrite, if file already exists.
try:
process = subprocess.Popen(args, creationflags=CREATE_NO_WINDOW)
process = subprocess.Popen(args, #creationflags=CREATE_NO_WINDOW,
stdout=subprocess.PIPE, stdin=subprocess.PIPE)
except FileNotFoundError:
raise MKVSinkError("ffmpeg was not found.") from None
except subprocess.SubprocessError as exc:
raise MKVSinkError(
"Popen failed: {0.__class__.__name__}: {0}".format(exc)
) from exc

process.wait()

os.remove(audio.file)
audio.on_format(self.encoding)
out = process.communicate(audio.file.read())[0]
out = io.BytesIO(out)
out.seek(0)
audio.file = out
audio.on_format(self.encoding)
Loading