def add_cover_art(self, cover, m4b):
        """add_cover_art(cover_name, audiobook_file) -> audiobook file with cover art

        Saved the CoverArt from the project to disc and adds it to the audiobook.
        """
        img = wx.Image(BytesIO(self.project.cover_art), wx.BITMAP_TYPE_ANY)
        img.SaveFile(cover, wx.BITMAP_TYPE_PNG)
        self.parent.update(10)
        cmd = [ivonet.APP_MP4_ART, "--add", cover, m4b]

        self.subprocess(cmd)

        log(f"Adding Cover Art to: {self.project.title}")
        while self.keep_going:
            try:
                line = self.process.stdout.readline()
            except UnicodeDecodeError:
                #  just skip a line.
                continue
            if not line:
                dbg(f"Finished Adding CoverArt to: {self.project.title}")
                break
            dbg(line)
            if "adding" in line:
                self.parent.update(50)
        self.__check_process(cmd)
        self.parent.update(100)
Beispiel #2
0
 def extract_cover(self):
     dbg("Extracting cover...")
     ret = subprocess.getstatusoutput(
         f'ffmpeg -i "{self.project}" -y -v quiet -an -vcodec copy "{self.cover}"'
     )
     if ret[0] != 0:
         self.keep_going = False
Beispiel #3
0
def checksum(filename) -> str:
    keep_going = True
    cmd = [ivonet.APP_FFPROBE, f"{filename}"]

    process = shell_command(cmd)

    ret = None
    while keep_going:
        try:
            line = process.stdout.readline()
        except UnicodeDecodeError:
            #  just skip a line
            continue
        dbg(line)
        if not line:
            dbg(f"No more input from subprocess")
            keep_going = False
            break
        # print(line)
        if "checksum == " in line:
            ret = line.split("== ")[1]
    process.stdout.close()
    if process.returncode != 0 and keep_going:
        raise IOError("Could not retrieve checksum")
    return ret.strip()
Beispiel #4
0
 def on_error(self, event: ProcessExceptionEvent):
     log("Processing stopped because an error occurred:",
         event.project.title)
     dbg("Processing error", str(event.cmd))
     self.filename.SetForegroundColour(wx.RED)
     self.Refresh()
     self.stop()
    def merge_mp3_files(self, merged):

        if len(self.project.tracks) == 1:
            self.parent.update(25)
            shutil.copyfile(self.project.tracks[0], merged)
            self.parent.update(100)
            return

        cmd = [ivonet.APP_MP3_BINDER, '-out', merged]
        [cmd.append(mp3) for mp3 in self.project.tracks]

        self.subprocess(cmd)

        log(f"Merging mp3 files for: {self.project.title}")
        total = len(self.project.tracks)
        count = 0
        while self.keep_going:
            try:
                line = self.process.stdout.readline()
            except UnicodeDecodeError:
                #  just skip a line
                continue
            dbg(line)
            if not line:
                dbg(f"Finished Merge for: {self.project.title}")
                break
            if "Processing:" in line:
                count += 1
                self.parent.update(int((count * 100) / total))
        self.__check_process(cmd)
Beispiel #6
0
def main():
    wx.SystemOptions.SetOption("mac.window-plain-transition", 1)
    wx.SystemOptions.SetOption("mac.textcontrol-use-spell-checker", 1)
    app = M4Baker()
    frame = MainFrame(None, title="M4Baker", size=(1024, 768))
    frame.Show(True)
    dbg("M4Baker initialized.")
    app.MainLoop()
 def __check_process(self, cmd):
     if self.process and not self.keep_going:
         self.process.terminate()
     self.process.stdout.close()
     self.process.wait()
     if self.process.returncode != 0 and self.keep_going:
         # Only throw an exception if the process terminated wrong
         # but we wanted to keep going
         self.keep_going = False
         dbg("Process exitcode: ", self.process.returncode)
         wx.PostEvent(self.parent, ProcessExceptionEvent(cmd=cmd, project=self.project))
Beispiel #8
0
    def on_queue(self, event):
        """Handles the queue event from either the toolbar or the File menu (shortcut)."""
        if not self.project.verify():
            dbg("You slipped between verifies :-) no no no processing allowed.")
            return
        self.status("Queueing audiobook...")

        for aax in self.project.tracks:
            self.queue_project(aax)

        self.on_clear(event)
        event.Skip()
Beispiel #9
0
    def OnDropFiles(self, x, y, filenames):
        dbg("MP3 Files dropped", filenames)

        for name in filenames:
            if name.lower().endswith(ivonet.FILE_EXTENSION):
                log("Recognized project file. Opening...")
                self.target.project_open(name)
                return True
            if name.lower().endswith(
                    ".aax"
            ) and name not in self.target.lc_audiofiles.GetStrings():
                self.target.append_track(name)
            else:
                log(f"Dropped file '{name}' is not an mp3 file or not unique in the list."
                    )
                return False
        self.genre_logged = False
        self.disc_correction_logged = False
        return True
Beispiel #10
0
    def add_cover_art(self):
        self.parent.update(10)
        cmd = [ivonet.APP_MP4_ART, "--add", self.cover, self.m4b]

        self.subprocess(cmd)

        log(f"Adding Cover Art to: {self.project}")
        while self.keep_going:
            try:
                line = self.process.stdout.readline()
            except UnicodeDecodeError:
                #  just skip a line.
                continue
            if not line:
                dbg(f"Finished Adding CoverArt to: {self.project}")
                break
            dbg(line)
            if "adding" in line:
                self.parent.update(50)
        self.__check_process(cmd)
        self.parent.update(100)
    def create_chapters(self, chapter_file, m4b):
        cmd = [ivonet.APP_MP4_CHAPS, ]
        if self.project.chapter_method == CHAPTER_LIST[0]:
            with open(chapter_file, "w") as fo:
                chapters = self.project.chapter_file()
                dbg(chapters)
                fo.write(chapters)
                self.parent.update(10)
            cmd.append("-i")
        else:
            fixed = int(self.project.chapter_method.split()[1].strip()) * 60
            cmd.append("-e")
            cmd.append(str(fixed))
        cmd.append(m4b)

        self.subprocess(cmd)

        log(f"Adding chapter information to: {self.project.title}")
        while self.keep_going:
            try:
                line = self.process.stdout.readline()
            except UnicodeDecodeError:
                #  just skip a line should not effect the progress
                continue
            if not line:
                dbg(f"Chapter information done: {self.project.title}")
                break
            dbg(line)
            if "QuickTime" in line:
                self.parent.update(50)
        self.__check_process(cmd)
        self.parent.update(100)
    def convert_2_m4a(self, merged, m4a):
        cmd = [ivonet.APP_FFMPEG,
               '-i',
               merged,
               "-stats",
               "-threads", "4",
               "-vn",
               "-y",
               "-acodec", "aac",
               "-strict",
               "-2",
               "-map_metadata", "0",
               "-map_metadata:s:a", "0:s:a",
               "-ac", "1",
               m4a,
               ]
        self.subprocess(cmd)

        log(f"Conversion has started for: {self.project.title}")
        while self.keep_going:
            try:
                line = self.process.stdout.readline()
            except UnicodeDecodeError:
                #  just skip a line
                continue
            if not line:
                dbg(f"Conversion finished for: {self.project.title}")
                break
            dbg(line)
            duration = self.DURATION.match(line)
            if duration:
                self.total_duration = time_seconds(duration.groups())
                continue
            elapsed = self.TIME_ELAPSED.match(line)
            if elapsed:
                self.progress = self.calc_percentage_done(elapsed.groups())
                self.parent.update(self.progress)
        self.__check_process(cmd)
Beispiel #13
0
 def set_cover_art(self, image):
     """handles the 'cover_art.force' and 'track.cover_art' events.
     gets an image file object or file location as input.
     """
     self.project.cover_art = image
     try:
         img = wx.Image(BytesIO(image), wx.BITMAP_TYPE_ANY)
         width = img.GetWidth()
         height = img.GetHeight()
     except AssertionError as e:
         dbg(e)
         log("CoverArt found but unknown format")
         return
     pnl_width, pnl_height = self.cover_art_panel.GetSize()
     if width > height:
         new_width = pnl_width
         new_height = pnl_width * height / width
     else:
         new_height = pnl_height
         new_width = pnl_height * width / height
     self.cover_art.SetBitmap(wx.Bitmap(img.Scale(new_width, new_height)))
     self.cover_art.Center()
     self.cover_art.Refresh()
Beispiel #14
0
    def on_queue(self, event):
        """Handles the queue event from either the toolbar or the File menu (shortcut)."""
        if not self.project.verify():
            dbg("You slipped between verifies :-) no no no processing allowed."
                )
            return
        self.status("Queueing audiobook...")

        base_dir = None
        if self.project.name:
            base_dir, filename = os.path.split(self.project.name)
        elif self.project.tracks:
            base_dir = os.path.split(self.project.tracks[0])[0]

        with wx.FileDialog(self,
                           "Save audiobook...",
                           defaultDir=self.default_save_path,
                           defaultFile=self.project.final_name(),
                           wildcard=ivonet.FILE_WILDCARD_M4B,
                           style=wx.FD_SAVE) as fileDialog:

            if base_dir:
                fileDialog.SetDirectory(base_dir)

            if fileDialog.ShowModal() == wx.ID_CANCEL:
                return

            # save the current contents in the file
            pathname = fileDialog.GetPath()
            if not pathname.endswith(".m4b"):
                pathname += ".m4b"
            self.project.m4b_name = pathname

        self.queue_project(self.project)
        self.on_clear(event)
        event.Skip()
Beispiel #15
0
def save_project(window, project):
    filename = project.title or "Untitled"
    if project.disc_total > 1:
        filename += f".Part {project.disc}"
    filename += ivonet.FILE_EXTENSION

    base_dir = None
    if project.name:
        base_dir, filename = os.path.split(project.name)
    elif project.tracks:
        base_dir = os.path.split(project.tracks[0])[0]

    default_dir = os.environ["HOME"] or os.getcwd()

    with wx.FileDialog(window,
                       message="Save file as ...",
                       defaultDir=default_dir,
                       defaultFile=f"{filename}",
                       wildcard=ivonet.FILE_WILDCARD_PROJECT,
                       style=wx.FD_SAVE
                       ) as save_dlg:

        save_dlg.SetFilterIndex(0)
        if base_dir:
            save_dlg.SetDirectory(base_dir)

        if save_dlg.ShowModal() == wx.ID_OK:
            path = save_dlg.GetPath()
            if not path.endswith(ivonet.FILE_EXTENSION):
                path += ivonet.FILE_EXTENSION
            with open(path, 'wb') as fo:
                dbg("Saving project file.." + str(project))
                project.name = path
                pickle.dump(project, fo)
            wx.PostEvent(window, ProjectHistoryEvent(path=path))
            log(f'Saved to: {path}')
Beispiel #16
0
    def add_metadata(self, metadata):
        """AtomicParsley "${AUDIOBOOK}.m4a" --title "${TITLE}"
        --grouping "${GROUPING}" --sortOrder album "${GROUPING}"
        --album "${ALBUM}" --artist "${AUTHOR}" --genre "${GENRE}"
        --tracknum "${TRACK}" --disk "${TRACK}" --comment "${COMMENT}"
        --year "${YEAR}" --stik Audiobook --overWrite"""
        tags = metadata["format"]["tags"]
        cmd = [
            ivonet.APP_ATOMIC_PARSLEY, self.m4a, "--title", f"{tags['title']}",
            "--album", f"{tags['album']}", "--artist", f"{tags['artist']}",
            "--genre", f"{tags['genre']}", "--comment",
            f"""{tags['comment']}""", "--year", f"{tags['date']}",
            "--encodingTool",
            f"{ivonet.TXT_APP_NAME} ({ivonet.TXT_APP_TINY_URL})", "--stik",
            "Audiobook", "--overWrite"
        ]
        dbg(cmd)
        self.subprocess(cmd)

        log(f"Adding metadata to: {self.project}")
        while self.keep_going:
            try:
                line = self.process.stdout.readline()
            except UnicodeDecodeError:
                #  just skip a line
                continue
            if not line:
                dbg(f"Finished Adding metadata to {self.project}")
                break
            dbg(line)
            if "Progress:" in line:
                ret = line.split("%")
                if len(ret) > 1:
                    try:
                        percentage = int(ret[0].split()[-1])
                        self.parent.update(percentage)
                        dbg(percentage)
                    except (IndexError, ValueError):
                        # Just ignore... probably bad line
                        pass
        self.parent.update(100)
        self.__check_process(cmd)
Beispiel #17
0
    def convert_2_m4a(self, activation_bytes):
        cmd = [
            ivonet.APP_FFMPEG,
            "-activation_bytes",
            activation_bytes,
            '-i',
            self.project,
            "-stats",
            "-y",
            "-vn",
            "-c:a",
            "copy",
            "-acodec",
            "aac",
            "-movflags",
            "use_metadata_tags",
            "-map_metadata",
            "0",
            "-map_metadata:s:a",
            "0:s:a",
            self.m4a,
        ]
        dbg(cmd)
        self.subprocess(cmd)

        log(f"Conversion has started for: {self.project}")
        while self.keep_going:
            try:
                line = self.process.stdout.readline()
            except UnicodeDecodeError:
                #  just skip a line
                continue
            if not line:
                dbg(f"Conversion finished for: {self.project}")
                break
            dbg(line)
            duration = self.DURATION.match(line)
            if duration:
                self.total_duration = time_seconds(duration.groups())
                continue
            elapsed = self.TIME_ELAPSED.match(line)
            if elapsed:
                self.progress = self.calc_percentage_done(elapsed.groups())
                self.parent.update(self.progress)
        self.__check_process(cmd)
    def add_metadata(self, m4a):
        cmd = [
            ivonet.APP_ATOMIC_PARSLEY,
            m4a,
            "--title", f"{self.project.title}",
            "--grouping", f"{self.project.grouping}",
            "--sortOrder", 'album', f"{self.project.grouping}",
            "--album", f"{self.project.title}",
            "--artist", f"{self.project.artist}",
            "--genre", f"{self.project.genre}",
            "--tracknum", f"{self.project.disc}/{self.project.disc_total}",
            "--disk", f"{self.project.disc}/{self.project.disc_total}",
            "--comment", f"""{self.project.get_comment()}""",
            "--year", f"{self.project.year}",
            "--encodingTool", f"{ivonet.TXT_APP_NAME} ({ivonet.TXT_APP_TINY_URL})",
            "--stik", "Audiobook",
            "--overWrite"
        ]
        self.subprocess(cmd)

        log(f"Adding metadata to: {self.project.title}")
        while self.keep_going:
            try:
                line = self.process.stdout.readline()
            except UnicodeDecodeError:
                #  just skip a line
                continue
            if not line:
                dbg(f"Finished Adding metadata to {self.project.title}")
                break
            dbg(line)
            if "Progress:" in line:
                ret = line.split("%")
                if len(ret) > 1:
                    try:
                        percentage = int(ret[0].split()[-1])
                        self.parent.update(percentage)
                        dbg(percentage)
                    except (IndexError, ValueError):
                        # Just ignore... probably bad line
                        pass
        self.parent.update(100)
        self.__check_process(cmd)
Beispiel #19
0
    def OnDropFiles(self, x, y, filenames):
        dbg("MP3 Files dropped", filenames)

        for name in filenames:
            if name.lower().endswith(ivonet.FILE_EXTENSION):
                log("Recognized project file. Opening...")
                self.target.project_open(name)
                return True
            if name.lower().endswith(".mp3") and name not in self.target.lc_mp3.GetStrings():
                self.target.append_track(name)
                tag = TinyTag.get(name, image=True, ignore_errors=True)
                if tag.get_image():
                    self.target.set_cover_art(tag.get_image())
                for item, mapping in methods:
                    dbg("method: ", item)
                    value = getattr(tag, item)
                    if value:
                        dbg("Value:", value)
                        getattr(self, f"set_{mapping}")(value.strip())
            else:
                log(f"Dropped file '{name}' is not an mp3 file or not unique in the list.")
        self.genre_logged = False
        self.disc_correction_logged = False
        return True
Beispiel #20
0
    def activation_bytes(self, rt_file, checksum):
        cmd = [ivonet.APP_RCRACK, rt_file, "-h", checksum]
        dbg(cmd)

        self.subprocess(cmd)

        log(f"RainbowCrack: {self.project}")
        ret = None
        while self.keep_going:
            try:
                line = self.process.stdout.readline()
            except UnicodeDecodeError:
                continue
            dbg(line)
            if not line:
                dbg(f"Finished Merge for: {self.project}")
                break
            if "hex:" in line:
                ret = line.split("hex:")[1]
                if not ret or "notfound" in ret:
                    ret = None
        self.__check_process(cmd)
        self.parent.update(100)
        return ret.strip()
Beispiel #21
0
 def on_save(self, event):
     dbg("on_save event!")
     save_project(self.parent, self.project)
     event.Skip()
Beispiel #22
0
 def remove_from_active_queue(self, book):
     try:
         self.active_queue.remove(book)
     except ValueError as e:
         dbg(e)
    def run(self):
        self.running = True

        with tempfile.TemporaryDirectory() as project_tmpdir:
            dbg("Temp dir:", project_tmpdir)
            log(f"Creating: {self.project.m4b_name}")

            # bind all mp3 into one file (mp3binder)
            self.parent.stage = 0
            merged = os.path.join(project_tmpdir, "merged.mp3")
            self.merge_mp3_files(merged)

            self.process = None
            if not self.keep_going:
                self.running = False
                return

            # Convert ffmpeg
            self.parent.stage = 1
            m4a = os.path.join(project_tmpdir, "converted.m4a")
            self.convert_2_m4a(merged, m4a)

            self.process = None
            if not self.keep_going:
                self.running = False
                return

            # Add metadata tags
            self.parent.stage = 2
            self.add_metadata(m4a)

            self.process = None
            if not self.keep_going:
                self.running = False
                return

            # Add chapters
            self.parent.stage = 3
            m4b = os.path.join(project_tmpdir, "converted.m4b")
            chapter_file = os.path.join(project_tmpdir, "converted.chapters.txt")
            self.create_chapters(chapter_file, m4b)

            self.process = None
            if not self.keep_going:
                self.running = False
                return

            # Add CoverArt
            self.parent.stage = 4
            cover = os.path.join(project_tmpdir, "cover.png")
            self.add_cover_art(cover, m4b)

            self.process = None
            if not self.keep_going:
                self.running = False
                return

            self.parent.stage = 5
            self.parent.update(25)
            log(f"Moving completed audiobook [{self.project.title}] to final destination.")
            try:
                basename, ext = os.path.splitext(unique_name(self.project.m4b_name))
                shutil.move(m4b, unique_name(self.project.m4b_name))
                self.parent.update(50)
                shutil.move(cover, unique_name(basename + ".png"))
                if os.path.isfile(chapter_file):
                    shutil.move(chapter_file, unique_name(basename + ".chapters.txt"))
            except InputError as e:
                log(e.message)
                self.keep_going = False

            if self.keep_going:
                self.parent.update(100)
                wx.PostEvent(self.parent, ProcessDoneEvent())
                log(f"Created: {self.project.m4b_name}")
            else:
                log("Something went wrong and the conversion could not complete successfully.")
                log("Please try again and if the problem persists you can PM me on twitter")
                log("@ivonet with a description of when and where it goes wrong.")
                log("Thanks for the help.")
            self.running = False
            self.keep_going = False
Beispiel #24
0
 def on_tracks_empty(self, event):
     """Handles the double click event on the tracks panel and will empty the list"""
     dbg("on_tracks_empty")
     self.lc_mp3.SetStrings([])
     event.Skip()