Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
    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)
Exemplo n.º 3
0
    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)
Exemplo n.º 4
0
 def on_file_history(self, event):
     """Handler for the event on file history selection in the file menu"""
     file_num = event.GetId() - wx.ID_FILE1
     path = self.GetMenuBar().file_history.GetHistoryFile(file_num)
     log(f"You selected {path}")
     wx.PostEvent(self, ProjectHistoryEvent(path=path))
     self.project_open(path)
Exemplo n.º 5
0
 def on_disc(self, event):
     """Handler for the disc and disc_total field events as they are linked"""
     if not self.check_disc():
         log("Corrected disk total as it can not be smaller than the disk.")
     self.project.disc = self.sc_disc.GetValue()
     self.project.disc_total = self.sc_disk_total.GetValue()
     event.Skip()
Exemplo n.º 6
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()
Exemplo n.º 7
0
 def set_disc_total(self, value):
     """Sets the disc total.
     It will always set it and that will trigger the on_disc handler
     to check if all is well...
     """
     self.target.sc_disk_total.SetValue(int(value))
     if not self.target.check_disc():
         log("Corrected disk total as it can not be smaller than the disk.")
Exemplo n.º 8
0
 def project_open(self, path):
     """Open a saved project"""
     try:
         with open(path, 'rb') as fi:
             self.project = pickle.load(fi)
             self.project.refresh_after_pickle()
             self.project.name = path
             self.reset_metadata(self.project)
     except FileNotFoundError:
         log(f"File: {path} could not be opened.")
Exemplo n.º 9
0
 def set_genre(self, value):
     """Sets the genre.
     It assumes that if the field has already been set either by a previous event
     or manually this event can be ignored.
     In this case we need a 'dirty' flag to do this as the field is a drop down and is never empty
     This event is less important then manual or previous set values.
     """
     if self.target.genre_pristine:
         if value in GENRES:
             self.target.genre_pristine = False
             self.target.cb_genre.SetValue(value)
         else:
             if not self.genre_logged:
                 log(f"Genre {value} from the metadata is not a known genre.")
                 self.genre_logged = True
Exemplo n.º 10
0
 def on_open_project(self, event):
     """Handles the open project event."""
     self.status("Open Project")
     with wx.FileDialog(self,
                        message="Choose a file...",
                        defaultDir=os.getcwd(),
                        defaultFile="",
                        wildcard=ivonet.FILE_WILDCARD_PROJECT,
                        style=wx.FD_OPEN | wx.FD_CHANGE_DIR
                        | wx.FD_FILE_MUST_EXIST
                        | wx.FD_PREVIEW) as open_dlg:
         if open_dlg.ShowModal() == wx.ID_OK:
             path = open_dlg.GetPath()
             log(f"Opening file: {path}")
             self.project_open(path)
             wx.PostEvent(self, ProjectHistoryEvent(path=path))
     event.Skip()
Exemplo n.º 11
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)
Exemplo n.º 12
0
    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)
Exemplo n.º 13
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
Exemplo n.º 14
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)
Exemplo n.º 15
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)
Exemplo n.º 16
0
    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)
Exemplo n.º 17
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()
Exemplo n.º 18
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()
Exemplo n.º 19
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}')
Exemplo n.º 20
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
Exemplo n.º 21
0
 def update_check(self, event):
     response = requests.get(ivonet.UPDATE_URL)
     if response.ok and ivonet.VERSION != response.text:
         log(f"Update {response.text} is available for download.")
         log("For update goto: https://m4baker.ivonet.nl ")
Exemplo n.º 22
0
    def run(self):
        self.running = True
        log(f"Creating: {self.m4b}")

        self.parent.stage = 1
        checksum = ffprobe.checksum(self.project)
        log(f"Checksum for [{self.project}] is [{checksum}]")
        if not checksum:
            wx.PostEvent(
                self.parent,
                ProcessExceptionEvent(msg="No checksum retrieved.",
                                      project=self.project))
            return

        self.parent.stage = 2
        activation_bytes = self.get_activation_bytes(checksum)
        log(f"Activation bytes for [{self.project}] are [{activation_bytes}]")
        if not activation_bytes:
            wx.PostEvent(
                self.parent,
                ProcessExceptionEvent(msg="No activation bites found.",
                                      project=self.project))
            return

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

        self.parent.stage = 3
        metadata = ffprobe.metadata(self.project)

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

        self.parent.stage = 4
        self.convert_2_m4a(activation_bytes)

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

        self.parent.stage = 5
        self.add_metadata(metadata)

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

        self.parent.stage = 7
        self.extract_cover()

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

        self.parent.stage = 8
        self.add_cover_art()

        self.cleanup()

        if self.keep_going:
            self.parent.update(100)
            wx.PostEvent(self.parent, ProcessDoneEvent())
        self.running = False
        self.keep_going = False
        log(f"Created: {self.m4b}")
Exemplo n.º 23
0
    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