Skip to content

Commit

Permalink
Updated classes for matching build patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
Javinator9889 committed May 17, 2020
1 parent 817601d commit 1496c40
Show file tree
Hide file tree
Showing 8 changed files with 87 additions and 89 deletions.
83 changes: 34 additions & 49 deletions YouTubeMDBot/audio/ffmpeg.py
Expand Up @@ -58,7 +58,7 @@ def __init__(self, data: bytes, command: List[str] = None):
:param command: the ffmpeg command.
"""
self._data = data
self.__command = command
self.command = command
self.__out = None
self.__err = None

Expand All @@ -67,40 +67,28 @@ def process(self) -> int:
Runs the ffmpeg command in a separate process and pipes both stdout and stderr.
:return: the return code of the operation ('0' if everything is OK, > 0 if not).
"""
proc = Popen(self.__command, stdout=PIPE, stderr=PIPE, stdin=PIPE)
proc = Popen(self.command, stdout=PIPE, stderr=PIPE, stdin=PIPE)
self.__out, self.__err = proc.communicate(self._data)
return proc.returncode

def get_command(self) -> List[str]:
"""
Get the command for editing.
:return: List[str] with the command - as this is a pointer, all editions done
to the list are directly changing the self object.
"""
return self.__command

def set_command(self, command: List[str]):
"""
Sets the new list, overriding every old implementation.
:param command: the new command.
"""
self.__command = command

def get_output(self) -> bytes:
@property
def output(self) -> bytes:
"""
Gets the stdout of the process.
:return: bytes with the command output.
"""
return self.__out

def get_extra(self) -> bytes:
@property
def extra(self) -> bytes:
"""
Gets the stderr of the process.
:return: bytes with extra information.
"""
return self.__err

def get_volume(self) -> float:
@property
def volume(self) -> float:
"""
Gets the maximum volume of the data input.
:return: the volume.
Expand Down Expand Up @@ -153,15 +141,14 @@ class FFmpegMP3(FFmpegExporter):
Exports audio data to MP3 format.
"""
def convert(self) -> int:
command = super().get_command()
if self._bitrate:
command.append("-b:a")
command.append(self._bitrate)
command.append("-acodec")
command.append("libmp3lame")
command.append("-f")
command.append("mp3")
command.append("-")
self.command.append("-b:a")
self.command.append(self._bitrate)
self.command.append("-acodec")
self.command.append("libmp3lame")
self.command.append("-f")
self.command.append("mp3")
self.command.append("-")
return super().convert()


Expand All @@ -170,15 +157,14 @@ class FFmpegOGG(FFmpegExporter):
Exports audio data to OGG format.
"""
def convert(self) -> int:
command = super().get_command()
if self._bitrate:
command.append("-b:a")
command.append(self._bitrate)
command.append("-c:a")
command.append("libvorbis")
command.append("-f")
command.append("ogg")
command.append("-")
self.command.append("-b:a")
self.command.append(self._bitrate)
self.command.append("-c:a")
self.command.append("libvorbis")
self.command.append("-f")
self.command.append("ogg")
self.command.append("-")
return super().convert()


Expand All @@ -188,18 +174,17 @@ def __init__(self, data: bytes, filename: str, bitrate: str = None):
self.filename = filename

def convert(self) -> int:
command = super().get_command()
vol = self.get_volume() * -1
command.append("-af")
command.append(f"volume={vol}dB")
vol = self.volume * -1
self.command.append("-af")
self.command.append(f"volume={vol}dB")
if self._bitrate:
command.append("-b:a")
command.append(self._bitrate)
command.append("-c:a")
command.append("aac")
command.append("-movflags")
command.append("faststart")
command.append("-f")
command.append("ipod")
command.append(self.filename)
self.command.append("-b:a")
self.command.append(self._bitrate)
self.command.append("-c:a")
self.command.append("aac")
self.command.append("-movflags")
self.command.append("faststart")
self.command.append("-f")
self.command.append("ipod")
self.command.append(self.filename)
return super().convert()
10 changes: 2 additions & 8 deletions YouTubeMDBot/audio/fpcalc.py
Expand Up @@ -52,16 +52,10 @@ def __init__(self, audio: bytes):
duration = re.search(duration_pattern, res)
fingerprint = re.search(fingerprint_pattern, res)

self.__duration: int = int(duration.group(0))
self.duration: int = int(duration.group(0))
self.__fp: str = str(fingerprint.group(0))

def duration(self) -> int:
"""
Obtains the audio duration in seconds.
:return: duration in seconds.
"""
return self.__duration

@property
def fingerprint(self) -> str:
"""
Obtains the audio fingerprint.
Expand Down
3 changes: 1 addition & 2 deletions YouTubeMDBot/constants/app_constants.py
Expand Up @@ -21,8 +21,7 @@
PROGRAM_ARGS = sys.argv
# YouTube DL options
YDL_CLI_OPTIONS = ["youtube-dl", "--format", "bestaudio[ext=m4a]", "--quiet",
"--output",
"-"]
"--output", "-"]

# FPCalc command
FPCALC = ["fpcalc", "-"]
Expand Down
13 changes: 3 additions & 10 deletions YouTubeMDBot/downloader/youtube_downloader.py
Expand Up @@ -35,9 +35,9 @@ def __init__(self, url: str):
the video.
:param url: the video URL.
"""
self.__url: str = url
self.url: str = url
self.__options: list = YDL_CLI_OPTIONS.copy()
self.__options.append(self.__url)
self.__options.append(self.url)

def download(self) -> Tuple[BytesIO, bytes]:
"""
Expand All @@ -57,13 +57,6 @@ def download(self) -> Tuple[BytesIO, bytes]:
raise RuntimeError("youtube-dl downloader exception - more info: " +
str(stderr.decode("utf-8")))

def get_url(self) -> str:
"""
Obtains the video URL.
:return: str with the URL.
"""
return self.__url


class M4AYouTubeDownloader(YouTubeDownloader):
def __init__(self, url: str, bitrate: str = None):
Expand All @@ -81,7 +74,7 @@ def download(self) -> Tuple[BytesIO, bytes]:
if ret != 0:
m4a_file.close()
raise RuntimeError("ffmpeg is unable to convert file - output: "
+ m4a_converter.get_extra().decode("utf-8"))
+ m4a_converter.extra.decode("utf-8"))
with m4a_file:
m4a_data = m4a_file.read()
return BytesIO(m4a_data), m4a_data
Expand Down
39 changes: 33 additions & 6 deletions YouTubeMDBot/metadata/AudioMetadata.py
Expand Up @@ -13,9 +13,10 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from io import BytesIO
from typing import List
from mutagen.mp4 import MP4
from mutagen.mp4 import MP4Cover
from io import BytesIO


class AudioMetadata:
Expand All @@ -24,6 +25,7 @@ class AudioMetadata:
object. By using this class, it is possible to set the required information by
using mutagen without having to remember the metadata keys.
"""

def __init__(self, audio: BytesIO):
"""
Generates a new instance.
Expand All @@ -32,35 +34,60 @@ def __init__(self, audio: BytesIO):
self._audio = MP4(audio)
self._data = audio

def set_title(self, title: str):
@property
def title(self) -> str:
return self._audio[u"\xa9nam"]

@title.setter
def title(self, title):
"""
Sets the audio title.
:param title: the audio title.
"""
self._audio[u"\xa9nam"] = title

def set_artist(self, artist: str):
@property
def artist(self) -> str:
return self._audio[u"\xa9ART"]

@artist.setter
def artist(self, artist: str):
"""
Sets the audio artist.
:param artist: the audio artist.
"""
self._audio[u"\xa9ART"] = artist

def set_album(self, album: str):
@property
def album(self) -> str:
return self._audio[u"\xa9alb"]

@album.setter
def album(self, album: str):
"""
Sets the audio album.
:param album: the audio album
"""
self._audio[u"\xa9alb"] = album

def set_extras(self, extras: list):
@property
def extras(self) -> str:
return self._audio[u"\xa9cmt"]

@extras.setter
def extras(self, extras: list):
"""
Sets the audio extras.
:param extras: a list of extras that will be added to the audio information.
"""
self._audio[u"\xa9cmt"] = '; '.join(map(str, extras))

def set_cover(self, cover: bytes):
@property
def cover(self) -> List[MP4Cover]:
return self._audio[u"covr"]

@cover.setter
def cover(self, cover: bytes):
"""
Sets the audio cover.
:param cover: the audio cover.
Expand Down
8 changes: 4 additions & 4 deletions YouTubeMDBot/metadata/MetadataIdentifier.py
Expand Up @@ -91,10 +91,10 @@ def identify_audio(self) -> bool:
:return: 'True' if the result is valid (the audio was correctly identified),
else 'False'.
"""
fingerprint = FPCalc(self.audio)
fp_calc = FPCalc(self.audio)
data: json = acoustid.lookup(apikey=ACOUSTID_KEY,
fingerprint=fingerprint.fingerprint(),
duration=fingerprint.duration(),
fingerprint=fp_calc.fingerprint,
duration=fp_calc.duration,
meta="recordings releaseids releasegroups")
self.result = data
is_valid = self._is_valid_result(data)
Expand Down Expand Up @@ -162,7 +162,7 @@ def identify_audio(self) -> bool:
if self._downloader:
from urllib.request import urlopen

video_id = youtube_utils.get_yt_video_id(self._downloader.get_url())
video_id = youtube_utils.get_yt_video_id(self._downloader.url)
video_data = YouTubeAPI.video_details(video_id)
self.title = video_data.title
self.artist = video_data.artist
Expand Down
18 changes: 9 additions & 9 deletions YouTubeMDBot/tests/identifier.py
Expand Up @@ -87,30 +87,30 @@ def find_metadata(self, future, downloader) -> Tuple[BytesIO, bytes, dict]:
io, data = future.get()
f_dl_t = time()
print("Downloaded {} - elapsed time: {:.1f}s"
.format(downloader.get_url(), f_dl_t - st_dl_t))
.format(downloader.url, f_dl_t - st_dl_t))
identifier = \
YouTubeMetadataIdentifier(audio=data, downloader=downloader)
valid = identifier.identify_audio()
assert valid
song_info = {downloader.get_url(): {
song_info = {downloader.url: {
"title": identifier.title,
"artist": identifier.artist,
"cover": identifier.cover
}}
if not identifier.youtube_data:
song_info[downloader.get_url()]["score"] = identifier.score
song_info[downloader.get_url()]["record_id"] = \
song_info[downloader.url]["score"] = identifier.score
song_info[downloader.url]["record_id"] = \
"https://musicbrainz.org/recording/{0}".format(
identifier.recording_id)
song_info[downloader.get_url()]["release_id"] = \
song_info[downloader.url]["release_id"] = \
"https://musicbrainz.org/release/{0}".format(
identifier.release_id)
song_info[downloader.get_url()]["album"] = identifier.album
song_info[downloader.url]["album"] = identifier.album
else:
song_info[downloader.get_url()][
song_info[downloader.url][
"duration"] = identifier.duration
song_info[downloader.get_url()]["id"] = identifier.youtube_id
song_info[downloader.get_url()]["youtube_data"] = True
song_info[downloader.url]["id"] = identifier.youtube_id
song_info[downloader.url]["youtube_data"] = True
self.barrier.wait()
return io, data, song_info

Expand Down
2 changes: 1 addition & 1 deletion YouTubeMDBot/tests/tagger.py
Expand Up @@ -15,7 +15,7 @@ def find_metadata(self, future, downloader) -> Tuple[BytesIO, bytes, dict]:
print(f"Running test: find_metadata in {__file__}")
io, data, song_info = super().find_metadata(future, downloader)
tagger = AudioMetadata(io)
url = downloader.get_url()
url = downloader.url
tagger.set_title(song_info[url]["title"])
tagger.set_artist(song_info[url]["artist"])
Expand Down

0 comments on commit 1496c40

Please sign in to comment.