Example #1
0
    def __init__(self, master=None, *args, **kwargs):
        # super().__init__(master, *args, **kwargs)
        self._FileList = FileList()
        self.master = master
        master.title("Music Production Project Manager")

        self.frame = ttk.Frame(self.master)
        self.frame.pack(expand=True, fill="both")
        self.path = tk.StringVar()
        self.null_threshold = tk.DoubleVar()
        self.null_threshold.set(-100)
        self.empty_threshold = tk.DoubleVar()
        self.empty_threshold.set(-100)
        self.keepBackup = tk.BooleanVar()
        self.keepBackup.set(True)
        self.keepMonoize = tk.BooleanVar()
        self.keepMonoize.set(True)
        self.keepRemove = tk.BooleanVar()
        self.keepRemove.set(True)
        self.keepJoin = tk.BooleanVar()
        self.keepJoin.set(True)
        self.backupPath = tk.StringVar()
        self.backupPath.set(self._FileList.options["backup_folder"])

        self.current_stage = tk.IntVar()
        self.analyzed = tk.BooleanVar()
        self.setup_style(self.master)

        top = ttk.Frame(self.frame, style="outline.TFrame")
        self.setup_header(top)
        self.setup_folder_selection(top)
        self.setup_input(top)
        self.fr_header.pack(anchor="n", expand=True, fill="x")
        self.fr_folder_selection.pack(anchor="n",
                                      expand=False,
                                      fill="x",
                                      padx=16)
        self.fr_input.pack(anchor="n", expand=False, fill="x", padx=16)
        top.pack(anchor="n", fill="x", ipady=8)

        mid = ttk.Frame(self.frame, style="outline.TFrame")
        self.display_file_list(mid)
        self.fr_file_list.pack(anchor="n", expand=True, fill="both", padx=16)
        mid.pack(anchor="n", expand=True, fill="both")

        bottom = ttk.Frame(self.frame, style="outline.TFrame")
        self.display_actions(bottom)
        self.fr_actions.pack(side="bottom",
                             expand=False,
                             fill="x",
                             pady=[0, 16],
                             padx=16)
        bottom.pack(anchor="n", fill="x")

        self.stage()
Example #2
0
 def test_proceed(self, mocker):
     with FileList(get_audio_path()) as obj:
         obj.backup = mocker.Mock()
         obj.update_options({"read_only": True})
         assert "read_only" in obj.options
         obj.proceed()
         obj.backup.assert_called()
Example #3
0
 def test__init__(self, options, result):
     with FileList(folder=get_audio_path(), options=options) as obj:
         for item in result:
             if isinstance(result[item], list):
                 L1 = getattr(obj, item)
                 L2 = result[item]
                 assert len(L1) == len(L2) and sorted(L1) == sorted(L2)
             else:
                 assert getattr(obj, item) == result[item]
Example #4
0
 def test_update_options(self):
     options = {"a": "aa", "b": "bb"}
     with FileList(options=options) as obj:
         obj.update_options({"c": "cc"})
         assert obj.options == {"a": "aa", "b": "bb", "c": "cc"}
         obj.update_options({"a": "ab"})
         assert obj.options == {"a": "ab", "b": "bb", "c": "cc"}
         assert obj.options.pop("a") == "ab"
         assert obj.options == {"a": "ab", "b": "bb", "c": "cc"}
Example #5
0
 def test_folderpath(self):
     with FileList() as obj:
         assert len(obj) == 0
         obj.folderpath = get_audio_path()
         assert obj.folderpath == get_audio_path()
         assert len(obj) == len(audio_files)
         assert set(x.filepath for x in obj) == set(
             get_audio_path(x + ".wav") for x in audio_files
         )
         assert len(obj[1:3]) == 2
Example #6
0
    def test__search_for_join(self, tmp_path, files, result):
        filepath = get_audio_path("sin-m.wav")
        ext = ".wav"
        files = [os.path.join(tmp_path, x + ext) for x in files]
        for f in files:
            shutil.copyfile(filepath, f)

        with FileList(tmp_path) as obj:
            assert {
                k: [x.filepath for x in v] for k, v in obj._search_for_join().items()
            } == {
                k: [os.path.join(tmp_path, x + ext) for x in v]
                for k, v in result.items()
            }
Example #7
0
 def test_set_default_action(self):
     with FileList(get_audio_path()) as fl:
         fl.set_default_action()
         assert fl.actions == [
             "Remove",
             "Remove",
             "Remove",
             "None",
             "Monoize",
             "None",
             "Monoize",
             "Monoize",
             "Monoize",
             "Join",
             "Remove",
         ]
Example #8
0
 def test__get_join_options(self, tmp_path, mocker, lists, filename, result):
     ext = ".wav"
     lists = {
         k: [AudioFile(os.path.join(tmp_path, x + ext)) for x in v]
         for k, v in lists.items()
     }
     filepath = os.path.join(tmp_path, filename + ext)
     if "others" in result:
         result.update(
             {
                 "others": [
                     AudioFile(os.path.join(tmp_path, x + ext))
                     for x in result["others"]
                 ]
             }
         )
     f = AudioFile(filepath, analyze=False)
     with FileList() as fl:
         fl.joinlists = mocker.Mock(return_value=lists).return_value
         o = fl._get_join_options(f)
         assert o.pop("others", []) == result.pop("others", [])
         assert o == result
Example #9
0
    def test_backup(self, tmp_path, params):
        def ignore_py(folder, content):
            return [f for f in content if os.path.splitext(f)[1] == ".py"]

        path = get_audio_path()
        shutil.copytree(
            path, tmp_path, dirs_exist_ok=True, ignore=ignore_py,
        )

        bakpath = os.path.join(tmp_path, "bak")

        if params.pop("folderExists", False):
            os.makedirs(bakpath)
            bakpath += "1"

        if params.pop("fileExists", False):
            shutil.copytree(
                tmp_path, bakpath, dirs_exist_ok=True,
            )

        with FileList(tmp_path) as obj:
            f = obj.backup(**params)
            assert os.path.exists(bakpath)
            assert set(os.listdir(bakpath)) == set(f + ".wav" for f in audio_files)
Example #10
0
class FolderBrowser:
    def __init__(self, master=None, *args, **kwargs):
        # super().__init__(master, *args, **kwargs)
        self._FileList = FileList()
        self.master = master
        master.title("Music Production Project Manager")

        self.frame = ttk.Frame(self.master)
        self.frame.pack(expand=True, fill="both")
        self.path = tk.StringVar()
        self.null_threshold = tk.DoubleVar()
        self.null_threshold.set(-100)
        self.empty_threshold = tk.DoubleVar()
        self.empty_threshold.set(-100)
        self.keepBackup = tk.BooleanVar()
        self.keepBackup.set(True)
        self.keepMonoize = tk.BooleanVar()
        self.keepMonoize.set(True)
        self.keepRemove = tk.BooleanVar()
        self.keepRemove.set(True)
        self.keepJoin = tk.BooleanVar()
        self.keepJoin.set(True)
        self.backupPath = tk.StringVar()
        self.backupPath.set(self._FileList.options["backup_folder"])

        self.current_stage = tk.IntVar()
        self.analyzed = tk.BooleanVar()
        self.setup_style(self.master)

        top = ttk.Frame(self.frame, style="outline.TFrame")
        self.setup_header(top)
        self.setup_folder_selection(top)
        self.setup_input(top)
        self.fr_header.pack(anchor="n", expand=True, fill="x")
        self.fr_folder_selection.pack(anchor="n",
                                      expand=False,
                                      fill="x",
                                      padx=16)
        self.fr_input.pack(anchor="n", expand=False, fill="x", padx=16)
        top.pack(anchor="n", fill="x", ipady=8)

        mid = ttk.Frame(self.frame, style="outline.TFrame")
        self.display_file_list(mid)
        self.fr_file_list.pack(anchor="n", expand=True, fill="both", padx=16)
        mid.pack(anchor="n", expand=True, fill="both")

        bottom = ttk.Frame(self.frame, style="outline.TFrame")
        self.display_actions(bottom)
        self.fr_actions.pack(side="bottom",
                             expand=False,
                             fill="x",
                             pady=[0, 16],
                             padx=16)
        bottom.pack(anchor="n", fill="x")

        self.stage()
        # elif self.current_stage.get() > 1:

    def setup_style(self, master):
        s = ttk.Style()
        s.theme_create("Mineral", parent="clam", settings=DefaultSetting()())
        s.theme_use("Mineral")

    def stage(self, value=None):
        if value is not None:
            self.current_stage.set(value)
        stage = self.current_stage.get()
        if stage == 0:
            self.bt_browse.state(["!disabled", "focus"])
            self.bt_analyze.state(["disabled", "!focus"])
            self.bt_proceed.state(["disabled", "!focus"])
        elif stage == 1:
            self.bt_browse.state(["!disabled", "!focus"])
            self.bt_analyze.state(["!disabled", "focus"])
            self.bt_proceed.state(["disabled", "!focus"])
        elif stage == 2:
            self.bt_browse.state(["!disabled", "!focus"])
            self.bt_analyze.state(["readonly", "!focus"])
            self.bt_proceed.state(["!disabled", "focus"])
        elif stage == -1:
            self.bt_browse.state(["disabled", "!focus"])
            self.bt_analyze.state(["disabled", "!focus"])
            self.bt_proceed.state(["disabled", "!focus"])

    def setup_header(self, master):
        frame = ttk.Frame(master)
        title = ttk.Label(frame,
                          text="Project Audio Files",
                          style="Header.TLabel")
        title.pack(expand=True, fill="x")
        self.fr_header = frame

    def setup_folder_selection(self, master):
        frame = ttk.Frame(master)
        address_label = ttk.Label(frame,
                                  text="Select Folder:",
                                  style="Panel.TLabel")
        address_label.pack(side="left")
        addressCmd = (master.register(self.address_validation), "%P")
        self.address = ttk.Entry(frame,
                                 validate="key",
                                 validatecommand=addressCmd)
        self.address.pack(side="left", expand=True, fill="x", padx=8)
        self.bt_browse = ttk.Button(frame,
                                    text="Browse",
                                    command=self.browse_command)
        self.bt_browse.pack(side="left", expand=False)

        self.fr_folder_selection = frame

    def browse_command(self):
        path = tk.filedialog.askdirectory(initialdir=self.path.get())
        if path and os.path.exists(path):
            self.path.set(path)
            self.address.delete(0, tk.END)
            self.address.insert(0, path)
            self.stage(1)

    def address_validation(self, path):
        if path == self.path.get():
            if self.analyzed.get():
                self.stage(2)
            else:
                self.stage(1)
            return True
        else:
            if os.path.exists(path):
                self.path.set(path)
                self.stage(1)
            else:
                self.stage(0)
            return True

    def setup_input(self, master):
        frame = ttk.Frame(master)
        top = ttk.Frame(frame)
        mid = ttk.Frame(frame)
        bottom = ttk.Frame(frame)

        lb_blocksize = ttk.Label(None, text="Blocksize", padding=[8, 0] * 2)
        blocksize = ttk.LabelFrame(
            top,
            labelwidget=lb_blocksize,
            text="Blocksize",
            labelanchor="n",
            borderwidth=1,
            padding=[8, 8, 8, 16],
            relief="sunken",
        )
        cb_blocksize = ttk.Combobox(
            blocksize,
            justify="center",
            values=[
                "None", 24000, 44100, 48000, 88200, 96000, 176400, 192000,
                384000
            ],
        )
        cb_blocksize.set("None")
        cb_blocksize.pack(side="bottom")
        self.blocksize = cb_blocksize

        lb_null_threshold = ttk.Label(None,
                                      text="Null threshold",
                                      padding=[8, 0] * 2)
        null_threshold = ttk.LabelFrame(
            top,
            labelwidget=lb_null_threshold,
            text="Null threshold",
            labelanchor="n",
            borderwidth=1,
            padding=[8, 8, 8, 16],
            relief="sunken",
        )
        en_null_threshold = ttk.Entry(
            null_threshold,
            justify="center",
            validate="key",
            validatecommand=(master.register(self.null_validation), "%P"),
        )
        en_null_threshold.insert(
            0, "{:.20f}".format(self.null_threshold.get()).rstrip("0"))
        en_null_threshold.pack(side="bottom")

        lb_empty_threshold = ttk.Label(None,
                                       text="Empty threshold",
                                       padding=[8, 0] * 2)
        empty_threshold = ttk.LabelFrame(
            top,
            labelwidget=lb_empty_threshold,
            text="Empty threshold",
            labelanchor="n",
            borderwidth=1,
            padding=[8, 8, 8, 16],
            relief="sunken",
        )
        en_empty_threshold = ttk.Entry(
            empty_threshold,
            justify="center",
            validate="key",
            validatecommand=(master.register(self.empty_validation), "%P"),
        )
        en_empty_threshold.insert(
            0, "{:.20f}".format(self.empty_threshold.get()).rstrip("0"))
        en_empty_threshold.pack(side="bottom")

        lb_actions = ttk.Label(None, text="Actions", padding=[8, 0] * 2)
        actions = ttk.LabelFrame(
            mid,
            labelwidget=lb_actions,
            text="Actions",
            labelanchor="n",
            borderwidth=1,
            padding=[8, 8, 8, 16],
            relief="sunken",
        )
        keepMonoize_button = ttk.Checkbutton(
            actions,
            text="Monoize",
            variable=self.keepMonoize,
            command=self.actions_command,
        )
        keepRemove_button = ttk.Checkbutton(
            actions,
            text="Remove",
            variable=self.keepRemove,
            command=self.actions_command,
        )
        keepJoin_button = ttk.Checkbutton(actions,
                                          text="Join",
                                          variable=self.keepJoin,
                                          command=self.actions_command)

        keepMonoize_button.pack(side="left", expand=True)
        keepRemove_button.pack(side="left", expand=True)
        keepJoin_button.pack(side="left", expand=True)

        self.bt_analyze = ttk.Button(bottom,
                                     text="Analyze",
                                     command=self.analyze_command)

        self.bt_analyze.pack(side="left", expand=True, fill="x")
        blocksize.pack(side="left", expand=True, fill="x", padx=[0, 4])
        null_threshold.pack(side="left", expand=True, fill="x", padx=[4, 4])
        empty_threshold.pack(side="left", expand=True, fill="x", padx=[4, 0])
        actions.pack(side="left", expand=True, fill="x")
        top.pack(expand=True, fill="x")
        mid.pack(expand=True, fill="x", pady=8)
        bottom.pack(expand=True, fill="x", pady=8)

        self.fr_input = frame

    def null_validation(self, P):
        try:
            self.null_threshold.set(float(-300 if not P or P == "-" else P))
            return True
        except ValueError:
            return False

    def empty_validation(self, P):
        try:
            self.empty_threshold.set(float(-300 if not P or P == "-" else P))
            return True
        except ValueError:
            return False

    def update_filelist(self):
        options = {
            "null_threshold": self.null_threshold.get(),
            "empty_threshold": self.empty_threshold.get(),
            "blocksize": self.blocksize.get(),
            "monoize": self.keepMonoize.get(),
            "remove": self.keepRemove.get(),
            "join": self.keepJoin.get(),
        }
        self._FileList.folderpath = self.path.get()
        self._FileList.update_options(options)
        self._FileList.update_files()
        self._FileList.set_default_action()

    def actions_command(self):
        if self.current_stage.get() > 1:
            self.analyze_command()

    def analyze_command(self):
        def check(v):
            return "x" if v else ""

        def select(f):
            return f.action

        if not os.path.exists(self.path.get()):
            self.stage(0)
            return

        self.stage(-1)
        self.update_filelist()
        self.file_tree.delete(*self.file_tree.get_children())
        for file in self._FileList:
            self.file_tree.insert(
                "",
                "end",
                text=file.basename,
                values=[
                    file.channels,
                    check(file.isEmpty),
                    check(file.isMono),
                    check(file.isFakeStereo),
                    check(file.isStereo),
                    check(file.isMultichannel),
                    select(file),
                ],
            )
        self.analyzed.set(True)
        self.stage(2)

    def display_file_list(self, master):
        x_width = 60
        frame = ttk.Frame(master)
        tree = ttk.Treeview(
            frame,
            selectmode="browse",
            columns=("Channels", "Empty", "Mono", "Fake", "Stereo", "Multi",
                     "Action"),
        )
        scroll = ttk.Scrollbar(frame, orient="vertical", command=tree.yview)
        tree.configure(yscroll=scroll, yscrollcommand=scroll.set)
        tree.heading("#0", text="Filename")
        tree.column("#0", width=256, stretch=True)
        tree.heading("#1", text="Channels")
        tree.column("#1", width=96, stretch=False, anchor="n")
        tree.heading("Empty", text="Empty")
        tree.column("Empty", width=x_width, stretch=False, anchor="n")
        tree.heading("Mono", text="Mono")
        tree.column("Mono", width=x_width, stretch=False, anchor="n")
        tree.heading("Fake", text="Fake")
        tree.column("Fake", width=x_width, stretch=False, anchor="n")
        tree.heading("Stereo", text="Stereo")
        tree.column("Stereo", width=x_width, stretch=False, anchor="n")
        tree.heading("Multi", text="Multi")
        tree.column("Multi", width=x_width, stretch=False, anchor="n")
        tree.heading("Action", text="Action")
        tree.column("Action", width=128, stretch=False, anchor="n")

        tree.pack(side="left", expand=True, fill="both")
        scroll.pack(side="right", fill="y")

        self.file_tree = tree
        self.fr_file_list = frame

    def display_actions(self, master):
        frame = ttk.Frame(master)
        top = ttk.Frame(frame)
        bottom = ttk.Frame(frame)
        self.bt_proceed = ttk.Button(bottom,
                                     text="Proceed",
                                     command=self.proceed_command)
        self.bt_proceed.pack(expand=True, fill="both")
        backup = ttk.Frame(top)
        lb_backup = ttk.Checkbutton(
            backup,
            text="Back up to sub-folder: ",
            variable=self.keepBackup,
        )
        address = ttk.Entry(
            backup,
            width=16,
            textvariable=self.backupPath,
            validate="key",
            validatecommand=(master.register(self.path_validation), "%S"),
        )

        lb_backup.pack(side="left", expand=False)
        address.pack(side="left", expand=True, fill="x", padx=8)
        backup.pack(side="left", expand=True)

        top.pack(expand=False, fill="x", pady=16)
        bottom.pack(expand=True, fill="both")

        self.fr_actions = frame

    def path_validation(self, S):
        return S in _valid_path_chars

    def proceed_command(self):
        options = {
            "backup": self.keepBackup.get(),
        }
        self._FileList.update_options(options)
        self._FileList.proceed()
        self.analyze_command()
        self.stage(3)
Example #11
0
 def test__init__late_start(self):
     with FileList() as fl:
         assert len(fl) == 0
         fl.folderpath = get_audio_path()
         assert len(fl) == len(os.listdir(get_audio_path())) - 1