python-rockbox 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,66 @@
1
+ Metadata-Version: 2.3
2
+ Name: python-rockbox
3
+ Version: 0.1.0
4
+ Summary: Create and manipulate WPS tags from Python
5
+ Author: Stormy
6
+ Author-email: Stormy <oohstormy@gmail.com>
7
+ Requires-Python: >=3.14
8
+ Description-Content-Type: text/markdown
9
+
10
+ # python-rockbox
11
+
12
+ Create and manipulate WPS tags from Python
13
+
14
+ ### Currently supported:
15
+ * All tags from Rockbox 4.0 (via `Tags` class)
16
+ * Tags (`Tag` class)
17
+ * Conditionals (`Match` class)
18
+
19
+ ## Example
20
+
21
+ This conditional that displays an appropriate battery bitmap:
22
+ ```py
23
+ wps = Match(
24
+ Tags.IS_CHARGING,
25
+ [
26
+ Tag(Tags.DISPLAY_IMAGE, "A"),
27
+ Match(
28
+ Tags.BATTERY_PERCENTAGE,
29
+ [
30
+ Tag(Tags.DISPLAY_IMAGE, "B"),
31
+ Tag(Tags.DISPLAY_IMAGE, "C"),
32
+ Tag(Tags.DISPLAY_IMAGE, "D"),
33
+ Tag(Tags.DISPLAY_IMAGE, "E"),
34
+ Tag(Tags.DISPLAY_IMAGE, "F"),
35
+ ],
36
+ ),
37
+ ],
38
+ )
39
+ ```
40
+
41
+ Would `__repr__` to:
42
+ ```c
43
+ %?bp<%xd(A)|%?bl<%xd(B)|%xd(C)|%xd(D)|%xd(E)|%xd(F)>>
44
+ ```
45
+
46
+ ## Install
47
+
48
+ To install locally, make sure you have the following installed:
49
+ * A modern version of Python 3 (I target 3.14)
50
+ * git
51
+ * pip
52
+
53
+ First, clone the repository:
54
+ ```sh
55
+ git clone https://git.0stormy.xyz/stormy/python-rockbox.git
56
+ ```
57
+
58
+ Next, change directories into the folder you just cloned:
59
+ ```sh
60
+ cd python-rockbox
61
+ ```
62
+
63
+ Finally, install it locally with:
64
+ ```sh
65
+ pip install -e .
66
+ ```
@@ -0,0 +1,57 @@
1
+ # python-rockbox
2
+
3
+ Create and manipulate WPS tags from Python
4
+
5
+ ### Currently supported:
6
+ * All tags from Rockbox 4.0 (via `Tags` class)
7
+ * Tags (`Tag` class)
8
+ * Conditionals (`Match` class)
9
+
10
+ ## Example
11
+
12
+ This conditional that displays an appropriate battery bitmap:
13
+ ```py
14
+ wps = Match(
15
+ Tags.IS_CHARGING,
16
+ [
17
+ Tag(Tags.DISPLAY_IMAGE, "A"),
18
+ Match(
19
+ Tags.BATTERY_PERCENTAGE,
20
+ [
21
+ Tag(Tags.DISPLAY_IMAGE, "B"),
22
+ Tag(Tags.DISPLAY_IMAGE, "C"),
23
+ Tag(Tags.DISPLAY_IMAGE, "D"),
24
+ Tag(Tags.DISPLAY_IMAGE, "E"),
25
+ Tag(Tags.DISPLAY_IMAGE, "F"),
26
+ ],
27
+ ),
28
+ ],
29
+ )
30
+ ```
31
+
32
+ Would `__repr__` to:
33
+ ```c
34
+ %?bp<%xd(A)|%?bl<%xd(B)|%xd(C)|%xd(D)|%xd(E)|%xd(F)>>
35
+ ```
36
+
37
+ ## Install
38
+
39
+ To install locally, make sure you have the following installed:
40
+ * A modern version of Python 3 (I target 3.14)
41
+ * git
42
+ * pip
43
+
44
+ First, clone the repository:
45
+ ```sh
46
+ git clone https://git.0stormy.xyz/stormy/python-rockbox.git
47
+ ```
48
+
49
+ Next, change directories into the folder you just cloned:
50
+ ```sh
51
+ cd python-rockbox
52
+ ```
53
+
54
+ Finally, install it locally with:
55
+ ```sh
56
+ pip install -e .
57
+ ```
@@ -0,0 +1,14 @@
1
+ [project]
2
+ name = "python-rockbox"
3
+ version = "0.1.0"
4
+ description = "Create and manipulate WPS tags from Python"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Stormy", email = "oohstormy@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.14"
10
+ dependencies = []
11
+
12
+ [build-system]
13
+ requires = ["uv_build>=0.11.25,<0.12.0"]
14
+ build-backend = "uv_build"
@@ -0,0 +1,4 @@
1
+ from .wps import Tag, Match, Color
2
+ from .tags import Tags
3
+
4
+ __all__ = ["Tag", "Tags", "Match", "Color"]
File without changes
@@ -0,0 +1,226 @@
1
+ from enum import Enum
2
+
3
+
4
+ class Tags(Enum):
5
+ """
6
+ All WPS tags from Rockbox 4.0 represented as enums.
7
+
8
+ Tag enums are ordered and sectioned by position in manual.
9
+ """
10
+
11
+ # Status Bar (D.1)
12
+ DISPLAY_STATUSBAR = "we"
13
+ HIDE_STATUSBAR = "wd"
14
+ INBUILT_STATUSBAR = "wi"
15
+
16
+ # Hardware (D.2)
17
+ HAS_CLOCK = "cc"
18
+ HAS_RADIO = "tp"
19
+ HAS_TOUCHSCREEN = "Tp"
20
+
21
+ # Metadata (D.3)
22
+ ARTIST_NAME = "ia"
23
+ COMPOSER_NAME = "ic"
24
+ ALBUM_ARTIST = "iA"
25
+ ALBUM_NAME = "id"
26
+ GROUPING = "iG"
27
+ TRACK_NUMBER = "in"
28
+ TRACK_TITLE = "it"
29
+ SONG_COMMENT = "iC"
30
+ ID3_VERSION = "iv"
31
+ SONG_YEAR = "iy"
32
+ DISC_NUMBER = "ik"
33
+
34
+ # Viewports (D.4)
35
+ VIEWPORT = "V"
36
+ VIEWPORT_FG = "Vf"
37
+ VIEWPORT_BG = "Vb"
38
+ VIEWPORT_STYLE = "Vs"
39
+ PRELOAD_VIEWPORT = "Vl"
40
+ DISPLAY_VIEWPORT = "Vd"
41
+ UI_VIEWPORT = "Vi"
42
+ INFO_VIEWPORT = "VI"
43
+ VIEWPORT_ON_BACKDROP = "VB"
44
+
45
+ # Fonts (D.5)
46
+ DEFINE_FONT = "Fl"
47
+
48
+ # Coloring Tags (D.6)
49
+ DRAW_RECTANGLE = "dr"
50
+
51
+ # Power (D.7)
52
+ BATTERY_PERCENTAGE = "bl"
53
+ BATTERY_VOLTS = "bv"
54
+ BATTERY_TIME = "bt"
55
+ IS_CHARGING = "bp"
56
+ CHARGING_BATTERY = "bc"
57
+ SLEEP_TIMER = "bs"
58
+
59
+ # File Information (D.8)
60
+ FILE_BITRATE = "fb"
61
+ FILE_CODEC = "fc"
62
+ FILE_FREQUENCY_HZ = "ff"
63
+ FILE_FREQUENCY_KHZ = "fk"
64
+ FILE_NAME_EXTENSION = "fm"
65
+ FILE_NAME = "fn"
66
+ FILE_PATH = "fp"
67
+ FILE_SIZE = "fs"
68
+ AVERAGE_BITRATE = "fv"
69
+ SEGMENT_FROM_END = "d"
70
+
71
+ # Playlist/Song (D.9)
72
+ PROGRESS_BAR = "pb"
73
+ SONG_PERCENTAGE = "px"
74
+ SONG_TIME = "pc"
75
+ PLAYLIST_ENTRIES = "pe"
76
+ PEAK_METER = "pm"
77
+ PEAK_LEFT_CHANNEL = "pL"
78
+ PEAK_RIGHT_CHANNEL = "pR"
79
+ PLAYLIST_NAME = "pn"
80
+ PLAYLIST_POSITION = "pp"
81
+ PLAYLIST_PERCENTAGE = "pP"
82
+ SONG_TIME_REMAINING = "pr"
83
+ IS_SHUFFLE = "ps"
84
+ TOTAL_TRACK_TIME = "pt"
85
+ VOLUME = "pv"
86
+ TRACK_STARTING = "pS"
87
+ TRACK_ENDING = "pE"
88
+
89
+ # Playlist Viewer (D.10)
90
+ PLAYLIST_VIEWPORT = "Vp"
91
+
92
+ # Runtime Database (D.11)
93
+ SONG_PLAYCOUNT = "rp"
94
+ SONG_RATING = "rr"
95
+ SONG_AUTOSCORE = "ra"
96
+
97
+ # Sound/DSP Settings (D.12)
98
+ PLAYBACK_PITCH = "Sp"
99
+ CROSSFADE_SETTING = "xf"
100
+ REPLAYGAIN_VALUE = "rg"
101
+
102
+ # Hold (D.13)
103
+ IS_HOLD_SWITCH = "mh"
104
+
105
+ # Virtual LED (D.14)
106
+ IS_HARDDISK = "lh"
107
+
108
+ # Repeat Mode (D.15)
109
+ REPEAT_MODE = "mm"
110
+
111
+ # Playback Mode (D.16)
112
+ PLAYBACK_STATUS = "mp"
113
+
114
+ # Current Screen (D.17)
115
+ CURRENT_SCREEN = "cs"
116
+
117
+ # List Title (D.18)
118
+ TITLE_TEXT = "Lt"
119
+ TITLE_ICON = "Li"
120
+
121
+ # List Skinning (D.19)
122
+ CURRENT_ITEM_TEXT = "LT"
123
+ CURRENT_ITEM_ICON = "LI"
124
+ CURRENT_ITEM_INDEX = "LN"
125
+ ITEM_ROW_NUMBER = "LR"
126
+ ITEM_COLUMN_NUMBER = "LC"
127
+ ITEM_IS_SELECTED = "Lc"
128
+ SCROLL_BAR_TAG = "LB"
129
+ VIEWPORT_ITEM_DRAW = "Lb"
130
+
131
+ # Changing Volume (D.20)
132
+ IS_VOLUME_CHANGING = "mv"
133
+
134
+ # Settings (D.21)
135
+ SETTING_VALUE = "St"
136
+ QUICKSCREEN_TOP_NAME = "QT"
137
+ QUICKSCREEN_TOP_VALUE = "Qt"
138
+ QUICKSCREEN_RIGHT_NAME = "QR"
139
+ QUICKSCREEN_RIGHT_VALUE = "Qr"
140
+ QUICKSCREEN_BOTTOM_NAME = "QB"
141
+ QUICKSCREEN_BOTTOM_VALUE = "Qb"
142
+ QUICKSCREEN_LEFT_NAME = "QL"
143
+ QUICKSCREEN_LEFT_VALUE = "Ql"
144
+
145
+ # Images (D.22):
146
+ BACKDROP_IMAGE = "X"
147
+ IMAGE = "x"
148
+ PRELOAD_IMAGE = "xl"
149
+ DISPLAY_IMAGE = "xd"
150
+ NINE_PATCH_IMAGE = "x9"
151
+ ALBUM_ART_SETTINGS = "Cl"
152
+ DISPLAY_ALBUM_ART = "Cd"
153
+ IS_ALBUM_ART = "C"
154
+
155
+ # FM Radio (D.23)
156
+ TUNER_TUNED = "tt"
157
+ FM_SCAN_OR_PRESET = "tm"
158
+ IS_FM_STEREO = "ts"
159
+ MINIMUM_FREQUENCY = "ta"
160
+ MAXIMUM_FREQUENCY = "tb"
161
+ CURRENT_FREQUENCY_MHZ = "tf"
162
+ CURRENT_PRESET_ID = "Ti"
163
+ CURRENT_PRESET_FREQUENCY = "Tf"
164
+ PRESET_COUNTT = "Tc"
165
+ IS_RDS = "tx"
166
+ RDS_NAME = "ty"
167
+ RDS_TEXT = "tz"
168
+ FM_SIGNAL_STRENGTH = "tr"
169
+
170
+ # Alignment (D.24)
171
+ ALIGN_LEFT = "al"
172
+ ALIGN_START = "aL"
173
+ ALIGN_CENTER = "ac"
174
+ ALIGN_RIGHT = "ar"
175
+ ALIGN_END = "aR"
176
+ NEXT_TAG_DIRECTION = "ax"
177
+ DEFINE_ALIGN_OPTIONS = "Sr"
178
+
179
+ # Conditional Tags (D.25)
180
+ IF = "if"
181
+ AND = "and"
182
+ OR = "or"
183
+
184
+ # Sublime Tags (D.26)
185
+ SUBLIME_DISPLAY_CYCLE_TIME = "t"
186
+
187
+ # Time and Date (D.27)
188
+ DAY_OF_MONTH = "cd"
189
+ ZERO_PADDED_MONTH_DAY = "ce"
190
+ TIME_FORMAT = "cf"
191
+ ZERO_PADDED_24_HOUR = "cH"
192
+ NORMAL_24_HOUR = "ck"
193
+ ZERO_PADDED_12_HOUR = "cI"
194
+ NORMAL_12_HOUR = "cl"
195
+ MONTH_OF_YEAR = "cm"
196
+ MINUTES = "cM"
197
+ SECONDS = "cS"
198
+ YEAR_2_DIGIT = "cy"
199
+ YEAR_4_DIGIT = "cY"
200
+ CAPITAL_AM_PM = "cP"
201
+ AM_PM = "cp"
202
+ WEEKDAY = "ca"
203
+ MONTH = "cb"
204
+ DAY_OF_WEEK_1_INDEX = "cu"
205
+ DAY_OF_WEEK_0_INDEX = "cw"
206
+
207
+ # Text Translation (D.28)
208
+ TRANSLATION = "Sx"
209
+
210
+ # Bar Tags (D.29)
211
+ DRAW_AS_BAR = "XX"
212
+
213
+ # Misc. Tags (D.30)
214
+ TAG_SUBSTRING = "ss"
215
+ READ_LINE_FILE = "ft"
216
+ LEFT_PARENTHESIS = "("
217
+ RIGHT_PARENTHESIS = ")"
218
+ COMMA = ","
219
+ PERCENT_SIGN = "%"
220
+ LEFT_THAN = "<"
221
+ PIPE = "|"
222
+ GREATER_THAN = ">"
223
+ SEMICOLON = ";"
224
+ HASHTAG = "#"
225
+ POUND = "#"
226
+ LINE_SHOULD_SCROLL = "s"
@@ -0,0 +1,77 @@
1
+ from .tags import Tags
2
+
3
+
4
+ class Color:
5
+ def __init__(self, color: str) -> None:
6
+ """Rockbox formatted RGB Hex color code"""
7
+ self.color = self.normalize_color(color)
8
+
9
+ @staticmethod
10
+ def normalize_color(color: str) -> str:
11
+ """Normalizes hex color to Rockbox format"""
12
+ color = color.upper().removeprefix("#")
13
+
14
+ # Convert to 6 character hex code if needed
15
+ if len(color) == 3:
16
+ color = "".join(c * 2 for c in color)
17
+ return color
18
+
19
+ def __str__(self) -> str:
20
+ return self.color
21
+
22
+ def __repr__(self) -> str:
23
+ return self.color
24
+
25
+
26
+ class Tag:
27
+ def __init__(self, tag: Tag | Tags | str, *args) -> None:
28
+ """
29
+ Class representation of a Rockbox WPS tag.
30
+
31
+ Ex: `Tag(Tags.DISPLAY_IMAGE, "X")` = `%xd(X)`
32
+ """
33
+ if isinstance(tag, Tag):
34
+ self.tag: str = tag.tag
35
+ self.arguments: tuple = tag.arguments
36
+ else:
37
+ if isinstance(tag, Tags):
38
+ self.tag: str = tag.value
39
+ else:
40
+ self.tag: str = tag
41
+ self.arguments: tuple = args
42
+
43
+ def render_simple_tag(self) -> str:
44
+ """Renders a tag with no arguments"""
45
+ return f"%{self.tag}"
46
+
47
+ def render_argument_tag(self) -> str:
48
+ """Renders a tag that has arguments"""
49
+ rendered_args = ", ".join(map(str, self.arguments))
50
+ return f"%{self.tag}({rendered_args})"
51
+
52
+ def __repr__(self) -> str:
53
+ if self.arguments:
54
+ return self.render_argument_tag()
55
+ else:
56
+ return self.render_simple_tag()
57
+
58
+
59
+ class Match:
60
+ def __init__(
61
+ self, tag: Tag | Tags | str, cases: list[Tag | Match]
62
+ ) -> None:
63
+ """Class representation of Rockbox WPS conditional"""
64
+ if isinstance(tag, Tags):
65
+ self.tag: Tag = Tag(tag.value)
66
+ else:
67
+ self.tag: Tag = Tag(tag)
68
+ self.cases: list[Tag | Match] = cases
69
+
70
+ def _render_match(self) -> str:
71
+ rendered_cases: list = []
72
+ for case in self.cases:
73
+ rendered_cases.append(case.__repr__())
74
+ return f"%?{self.tag.tag}<{"|".join(rendered_cases)}>"
75
+
76
+ def __repr__(self) -> str:
77
+ return self._render_match()