aboutsummaryrefslogtreecommitdiffstats
path: root/dialogs/reorganize_dialog.py
blob: 1385c980bdb2350cf8eb6c0bb81f4aa9ca3e1e60 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
from pathlib import Path
from types import SimpleNamespace

from PyQt6.QtWidgets import (
    QAbstractItemView,
    QCheckBox,
    QDialog,
    QHBoxLayout,
    QHeaderView,
    QLabel,
    QPushButton,
    QTableWidget,
    QTableWidgetItem,
    QVBoxLayout,
)

from models import Track, plan_reorganize
from ui_common import MISSING_BRUSH


class ReorganizeDialog(QDialog):
    """Preview of moving every track to root/Artist/Album/. Nothing is touched until the user accepts."""

    def __init__(self, tracks: list[Track], root: Path, parent=None):
        super().__init__(parent)
        self.setWindowTitle("Reorganize Folders")
        self.resize(1000, 600)
        self.tracks, self.root = tracks, root
        self.moves: list[SimpleNamespace] = []

        intro = QLabel(f"Move every FLAC (and its .lrc) in <b>{root}</b> into <b>Artist / Album</b> folders. "
                       "Filenames are kept. Uses the saved tags on disk.")
        intro.setWordWrap(True)
        self.album_artist = QCheckBox("Use Album Artist when set")
        self.album_artist.setChecked(True)
        self.album_artist.setToolTip("Keeps compilations / features in one folder. Untick to always use the track Artist.")
        self.album_artist.toggled.connect(self._refresh)
        self.cleanup = QCheckBox("Remove empty folders afterwards")
        self.cleanup.setChecked(True)
        opts = QHBoxLayout()
        opts.addWidget(self.album_artist)
        opts.addWidget(self.cleanup)
        opts.addStretch()

        self.table = QTableWidget(0, 2)
        self.table.setHorizontalHeaderLabels(["From", "To"])
        self.table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
        self.table.verticalHeader().setVisible(False)
        self.table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
        self.table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
        self.table.setWordWrap(False)

        self.summary = QLabel()
        self.ok_btn = QPushButton()
        self.ok_btn.setDefault(True)
        self.ok_btn.clicked.connect(self.accept)
        cancel = QPushButton("Cancel")
        cancel.clicked.connect(self.reject)
        bottom = QHBoxLayout()
        bottom.addWidget(self.summary, 1)
        bottom.addWidget(self.ok_btn)
        bottom.addWidget(cancel)

        layout = QVBoxLayout(self)
        layout.addWidget(intro)
        layout.addLayout(opts)
        layout.addWidget(self.table, 1)
        layout.addLayout(bottom)
        self._refresh()

    def _rel(self, path: Path) -> str:
        try:
            return str(path.relative_to(self.root))
        except ValueError:
            return str(path)

    def _refresh(self):
        plan = plan_reorganize(self.tracks, self.root, self.album_artist.isChecked())
        self.moves = [m for m in plan if not m.conflict]
        self.table.setRowCount(len(plan))
        for row, move in enumerate(plan):
            src = QTableWidgetItem(self._rel(move.src))
            dst = QTableWidgetItem(self._rel(move.dst) + ("   (conflict: skipped)" if move.conflict else ""))
            src.setToolTip(str(move.src))
            dst.setToolTip(str(move.dst))
            if move.conflict:
                dst.setForeground(MISSING_BRUSH)
            self.table.setItem(row, 0, src)
            self.table.setItem(row, 1, dst)
        conflicts = len(plan) - len(self.moves)
        in_place = len(self.tracks) - len(plan)
        text = f"{len(self.moves)} to move · {in_place} already in place"
        if conflicts:
            text += f" · {conflicts} skipped (a file with the same name is already at the destination)"
        self.summary.setText(text)
        self.ok_btn.setText(f"Move {len(self.moves)} File(s)")
        self.ok_btn.setEnabled(bool(self.moves))
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage