absfuyu 5.6.1__py3-none-any.whl → 6.1.2__py3-none-any.whl
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.
Potentially problematic release.
This version of absfuyu might be problematic. Click here for more details.
- absfuyu/__init__.py +5 -3
- absfuyu/__main__.py +2 -2
- absfuyu/cli/__init__.py +13 -2
- absfuyu/cli/audio_group.py +98 -0
- absfuyu/cli/color.py +2 -2
- absfuyu/cli/config_group.py +2 -2
- absfuyu/cli/do_group.py +2 -2
- absfuyu/cli/game_group.py +20 -2
- absfuyu/cli/tool_group.py +68 -4
- absfuyu/config/__init__.py +3 -3
- absfuyu/core/__init__.py +10 -6
- absfuyu/core/baseclass.py +104 -34
- absfuyu/core/baseclass2.py +43 -2
- absfuyu/core/decorator.py +2 -2
- absfuyu/core/docstring.py +4 -2
- absfuyu/core/dummy_cli.py +3 -3
- absfuyu/core/dummy_func.py +2 -2
- absfuyu/dxt/__init__.py +2 -2
- absfuyu/dxt/base_type.py +93 -0
- absfuyu/dxt/dictext.py +188 -6
- absfuyu/dxt/dxt_support.py +2 -2
- absfuyu/dxt/intext.py +72 -4
- absfuyu/dxt/listext.py +495 -23
- absfuyu/dxt/strext.py +2 -2
- absfuyu/extra/__init__.py +2 -2
- absfuyu/extra/audio/__init__.py +8 -0
- absfuyu/extra/audio/_util.py +57 -0
- absfuyu/extra/audio/convert.py +192 -0
- absfuyu/extra/audio/lossless.py +281 -0
- absfuyu/extra/beautiful.py +2 -2
- absfuyu/extra/da/__init__.py +39 -3
- absfuyu/extra/da/dadf.py +436 -29
- absfuyu/extra/da/dadf_base.py +2 -2
- absfuyu/extra/da/df_func.py +89 -5
- absfuyu/extra/da/mplt.py +2 -2
- absfuyu/extra/ggapi/__init__.py +8 -0
- absfuyu/extra/ggapi/gdrive.py +223 -0
- absfuyu/extra/ggapi/glicense.py +148 -0
- absfuyu/extra/ggapi/glicense_df.py +186 -0
- absfuyu/extra/ggapi/gsheet.py +88 -0
- absfuyu/extra/img/__init__.py +30 -0
- absfuyu/extra/img/converter.py +402 -0
- absfuyu/extra/img/dup_check.py +291 -0
- absfuyu/extra/pdf.py +4 -6
- absfuyu/extra/rclone.py +253 -0
- absfuyu/extra/xml.py +90 -0
- absfuyu/fun/__init__.py +2 -20
- absfuyu/fun/rubik.py +2 -2
- absfuyu/fun/tarot.py +2 -2
- absfuyu/game/__init__.py +2 -2
- absfuyu/game/game_stat.py +2 -2
- absfuyu/game/schulte.py +78 -0
- absfuyu/game/sudoku.py +2 -2
- absfuyu/game/tictactoe.py +2 -2
- absfuyu/game/wordle.py +6 -4
- absfuyu/general/__init__.py +2 -2
- absfuyu/general/content.py +2 -2
- absfuyu/general/human.py +2 -2
- absfuyu/general/resrel.py +213 -0
- absfuyu/general/shape.py +3 -8
- absfuyu/general/tax.py +344 -0
- absfuyu/logger.py +806 -59
- absfuyu/numbers/__init__.py +13 -0
- absfuyu/numbers/number_to_word.py +321 -0
- absfuyu/numbers/shorten_number.py +303 -0
- absfuyu/numbers/time_duration.py +217 -0
- absfuyu/pkg_data/__init__.py +2 -2
- absfuyu/pkg_data/deprecated.py +2 -2
- absfuyu/pkg_data/logo.py +1462 -0
- absfuyu/sort.py +4 -4
- absfuyu/tools/__init__.py +2 -2
- absfuyu/tools/checksum.py +119 -4
- absfuyu/tools/converter.py +2 -2
- absfuyu/tools/generator.py +24 -7
- absfuyu/tools/inspector.py +2 -2
- absfuyu/tools/keygen.py +2 -2
- absfuyu/tools/obfuscator.py +2 -2
- absfuyu/tools/passwordlib.py +2 -2
- absfuyu/tools/shutdownizer.py +3 -8
- absfuyu/tools/sw.py +213 -10
- absfuyu/tools/web.py +10 -13
- absfuyu/typings.py +5 -8
- absfuyu/util/__init__.py +31 -2
- absfuyu/util/api.py +7 -4
- absfuyu/util/cli.py +119 -0
- absfuyu/util/gui.py +91 -0
- absfuyu/util/json_method.py +2 -2
- absfuyu/util/lunar.py +2 -2
- absfuyu/util/package.py +124 -0
- absfuyu/util/path.py +313 -4
- absfuyu/util/performance.py +2 -2
- absfuyu/util/shorten_number.py +206 -13
- absfuyu/util/text_table.py +2 -2
- absfuyu/util/zipped.py +2 -2
- absfuyu/version.py +22 -19
- {absfuyu-5.6.1.dist-info → absfuyu-6.1.2.dist-info}/METADATA +37 -8
- absfuyu-6.1.2.dist-info/RECORD +105 -0
- {absfuyu-5.6.1.dist-info → absfuyu-6.1.2.dist-info}/WHEEL +1 -1
- absfuyu/extra/data_analysis.py +0 -21
- absfuyu-5.6.1.dist-info/RECORD +0 -79
- {absfuyu-5.6.1.dist-info → absfuyu-6.1.2.dist-info}/entry_points.txt +0 -0
- {absfuyu-5.6.1.dist-info → absfuyu-6.1.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Absfuyu: Time duration
|
|
3
|
+
----------------------
|
|
4
|
+
Short time
|
|
5
|
+
|
|
6
|
+
Version: 6.1.1
|
|
7
|
+
Date updated: 30/12/2025 (dd/mm/yyyy)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
# Module level
|
|
13
|
+
# ---------------------------------------------------------------------------
|
|
14
|
+
__all__ = [
|
|
15
|
+
"Duration",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Library
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from typing import Protocol, Self
|
|
23
|
+
|
|
24
|
+
from absfuyu.core import versionadded
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Class - Duration
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
# Format preset
|
|
30
|
+
class SupportDurationFormatPreset(Protocol):
|
|
31
|
+
def __call__(self, duration: Duration, /) -> str: ...
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
@versionadded("5.16.0")
|
|
36
|
+
class Duration:
|
|
37
|
+
"""
|
|
38
|
+
Convert duration in seconds to a more readable form. Eg: 3 mins 2 secs
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
total_seconds : int | float
|
|
43
|
+
Seconds to convert to
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
total_seconds: int | float
|
|
47
|
+
|
|
48
|
+
years: int = field(init=False)
|
|
49
|
+
months: int = field(init=False)
|
|
50
|
+
days: int = field(init=False)
|
|
51
|
+
hours: int = field(init=False)
|
|
52
|
+
minutes: int = field(init=False)
|
|
53
|
+
seconds: int = field(init=False)
|
|
54
|
+
|
|
55
|
+
_formats: dict[str, SupportDurationFormatPreset] = field(init=False)
|
|
56
|
+
|
|
57
|
+
# Calculate duration
|
|
58
|
+
def _calculate_duration(self) -> None:
|
|
59
|
+
SEC_PER_MIN = 60
|
|
60
|
+
SEC_PER_HOUR = 3600
|
|
61
|
+
SEC_PER_DAY = 86400
|
|
62
|
+
SEC_PER_MONTH = 30 * SEC_PER_DAY
|
|
63
|
+
SEC_PER_YEAR = 365 * SEC_PER_DAY
|
|
64
|
+
|
|
65
|
+
secs = self.total_seconds
|
|
66
|
+
|
|
67
|
+
self.years, secs = divmod(secs, SEC_PER_YEAR)
|
|
68
|
+
self.months, secs = divmod(secs, SEC_PER_MONTH)
|
|
69
|
+
self.days, secs = divmod(secs, SEC_PER_DAY)
|
|
70
|
+
self.hours, secs = divmod(secs, SEC_PER_HOUR)
|
|
71
|
+
self.minutes, self.seconds = divmod(secs, SEC_PER_MIN)
|
|
72
|
+
|
|
73
|
+
# Format handling
|
|
74
|
+
def _init_format(self) -> None:
|
|
75
|
+
|
|
76
|
+
def duration_compact_preset(duration: Self, /) -> str:
|
|
77
|
+
"""
|
|
78
|
+
Example: "1y 2m 3d 4h 5m 6s"
|
|
79
|
+
(fields = hidden when = 0).
|
|
80
|
+
"""
|
|
81
|
+
parts = []
|
|
82
|
+
if duration.years:
|
|
83
|
+
parts.append(f"{duration.years}y")
|
|
84
|
+
if duration.months:
|
|
85
|
+
parts.append(f"{duration.months}m")
|
|
86
|
+
if duration.days:
|
|
87
|
+
parts.append(f"{duration.days}d")
|
|
88
|
+
if duration.hours:
|
|
89
|
+
parts.append(f"{duration.hours}h")
|
|
90
|
+
if duration.minutes:
|
|
91
|
+
parts.append(f"{duration.minutes}m")
|
|
92
|
+
if duration.seconds:
|
|
93
|
+
parts.append(f"{duration.seconds}s")
|
|
94
|
+
return " ".join(parts) if parts else "0s"
|
|
95
|
+
|
|
96
|
+
def duration_HMS_only_preset(duration: Self, /) -> str:
|
|
97
|
+
"""
|
|
98
|
+
Example: "02:15:09" (HH:MM:SS only).
|
|
99
|
+
"""
|
|
100
|
+
total = duration.total_seconds
|
|
101
|
+
h, m = divmod(total, 3600)
|
|
102
|
+
m, s = divmod(m, 60)
|
|
103
|
+
return f"{h:02d}:{m:02d}:{s:02d}"
|
|
104
|
+
|
|
105
|
+
def duration_digital_preset(duration: Self, /) -> str:
|
|
106
|
+
"""
|
|
107
|
+
Examples:
|
|
108
|
+
- If >= 1 day: "1d 02:03:04"
|
|
109
|
+
- else: "02:03:04"
|
|
110
|
+
"""
|
|
111
|
+
total = duration.total_seconds
|
|
112
|
+
days, sec = divmod(total, 86400)
|
|
113
|
+
h, sec = divmod(sec, 3600)
|
|
114
|
+
m, s = divmod(sec, 60)
|
|
115
|
+
|
|
116
|
+
if days:
|
|
117
|
+
return f"{days}d {h:02d}:{m:02d}:{s:02d}"
|
|
118
|
+
return f"{h:02d}:{m:02d}:{s:02d}"
|
|
119
|
+
|
|
120
|
+
self._formats = {
|
|
121
|
+
"compact": duration_compact_preset,
|
|
122
|
+
"hms": duration_HMS_only_preset,
|
|
123
|
+
"digital": duration_digital_preset,
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@versionadded("5.17.0")
|
|
127
|
+
def add_format(self, name: str, format_func: SupportDurationFormatPreset) -> None:
|
|
128
|
+
"""
|
|
129
|
+
Add format style to Duration
|
|
130
|
+
|
|
131
|
+
Parameters
|
|
132
|
+
----------
|
|
133
|
+
name : str
|
|
134
|
+
Name of the style (name will be lowercased)
|
|
135
|
+
|
|
136
|
+
format_func : SupportDurationFormatPreset
|
|
137
|
+
Format function
|
|
138
|
+
"""
|
|
139
|
+
self._formats[name.lower().strip()] = format_func
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def available_formats(self) -> list[str]:
|
|
143
|
+
"""
|
|
144
|
+
Available style format
|
|
145
|
+
|
|
146
|
+
Returns
|
|
147
|
+
-------
|
|
148
|
+
list[str]
|
|
149
|
+
All available style formats
|
|
150
|
+
"""
|
|
151
|
+
return list(self._formats)
|
|
152
|
+
|
|
153
|
+
def __format__(self, format_spec: str) -> str:
|
|
154
|
+
"""
|
|
155
|
+
Change format of an object.
|
|
156
|
+
|
|
157
|
+
Usage
|
|
158
|
+
-----
|
|
159
|
+
>>> print(f"{<object>:<format_spec>}")
|
|
160
|
+
>>> print(<object>.__format__(<format_spec>))
|
|
161
|
+
>>> print(format(<object>, <format_spec>))
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
func = self._formats.get(format_spec.lower().strip(), None)
|
|
165
|
+
|
|
166
|
+
if func is None:
|
|
167
|
+
return self.__str__()
|
|
168
|
+
else:
|
|
169
|
+
return func(self)
|
|
170
|
+
|
|
171
|
+
# POST INIT
|
|
172
|
+
def __post_init__(self) -> None:
|
|
173
|
+
if not isinstance(self.total_seconds, (int, float)) or self.total_seconds < 0:
|
|
174
|
+
raise ValueError("seconds must be a non-negative number")
|
|
175
|
+
self._calculate_duration()
|
|
176
|
+
self._init_format()
|
|
177
|
+
|
|
178
|
+
def __str__(self) -> str:
|
|
179
|
+
|
|
180
|
+
def _plural(n: int | float, word: str):
|
|
181
|
+
return f"{n} {word}{'s' if n != 1 else ''}"
|
|
182
|
+
|
|
183
|
+
parts = []
|
|
184
|
+
if self.years:
|
|
185
|
+
parts.append(_plural(self.years, "year"))
|
|
186
|
+
if self.months:
|
|
187
|
+
parts.append(_plural(self.months, "month"))
|
|
188
|
+
if self.days:
|
|
189
|
+
parts.append(_plural(self.days, "day"))
|
|
190
|
+
if self.hours:
|
|
191
|
+
parts.append(_plural(self.hours, "hour"))
|
|
192
|
+
if self.minutes:
|
|
193
|
+
parts.append(_plural(self.minutes, "minute"))
|
|
194
|
+
if self.seconds:
|
|
195
|
+
parts.append(_plural(self.seconds, "second"))
|
|
196
|
+
return " ".join(parts) if parts else "0 second"
|
|
197
|
+
|
|
198
|
+
# From other type of duration
|
|
199
|
+
@classmethod
|
|
200
|
+
def from_minute(cls, minutes: int | float) -> Self:
|
|
201
|
+
return cls(minutes * 60)
|
|
202
|
+
|
|
203
|
+
@classmethod
|
|
204
|
+
def from_hour(cls, hours: int | float) -> Self:
|
|
205
|
+
return cls(hours * 3600)
|
|
206
|
+
|
|
207
|
+
@classmethod
|
|
208
|
+
def from_day(cls, days: int | float) -> Self:
|
|
209
|
+
return cls(days * 86400)
|
|
210
|
+
|
|
211
|
+
@classmethod
|
|
212
|
+
def from_month(cls, months: int | float) -> Self:
|
|
213
|
+
return cls(months * 86400 * 30)
|
|
214
|
+
|
|
215
|
+
@classmethod
|
|
216
|
+
def from_year(cls, years: int | float) -> Self:
|
|
217
|
+
return cls(years * 86400 * 365)
|
absfuyu/pkg_data/__init__.py
CHANGED