nowfycore 1.0.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.
- nowfycore-1.0.0/PKG-INFO +45 -0
- nowfycore-1.0.0/README.md +34 -0
- nowfycore-1.0.0/pyproject.toml +22 -0
- nowfycore-1.0.0/setup.cfg +4 -0
- nowfycore-1.0.0/src/nowfycore/__init__.py +87 -0
- nowfycore-1.0.0/src/nowfycore/about.py +165 -0
- nowfycore-1.0.0/src/nowfycore/api.py +268 -0
- nowfycore-1.0.0/src/nowfycore/bridge.py +13 -0
- nowfycore-1.0.0/src/nowfycore/cache.py +21 -0
- nowfycore-1.0.0/src/nowfycore/corekeys.py +39 -0
- nowfycore-1.0.0/src/nowfycore/flags.py +52 -0
- nowfycore-1.0.0/src/nowfycore/injections.py +188 -0
- nowfycore-1.0.0/src/nowfycore/language.py +22 -0
- nowfycore-1.0.0/src/nowfycore/panel.py +79 -0
- nowfycore-1.0.0/src/nowfycore/services.py +27 -0
- nowfycore-1.0.0/src/nowfycore/state.py +43 -0
- nowfycore-1.0.0/src/nowfycore/tab_actions.py +417 -0
- nowfycore-1.0.0/src/nowfycore/themes.py +65 -0
- nowfycore-1.0.0/src/nowfycore/trackhub.py +82 -0
- nowfycore-1.0.0/src/nowfycore/trackhub_ui.py +160 -0
- nowfycore-1.0.0/src/nowfycore/translations.py +7 -0
- nowfycore-1.0.0/src/nowfycore/ui_controls.py +570 -0
- nowfycore-1.0.0/src/nowfycore/welcome.py +47 -0
- nowfycore-1.0.0/src/nowfycore.egg-info/PKG-INFO +45 -0
- nowfycore-1.0.0/src/nowfycore.egg-info/SOURCES.txt +26 -0
- nowfycore-1.0.0/src/nowfycore.egg-info/dependency_links.txt +1 -0
- nowfycore-1.0.0/src/nowfycore.egg-info/requires.txt +3 -0
- nowfycore-1.0.0/src/nowfycore.egg-info/top_level.txt +1 -0
nowfycore-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nowfycore
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Nowfy core runtime package (pure layer)
|
|
5
|
+
Author: AGeekApple
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: requests
|
|
9
|
+
Requires-Dist: yt-dlp
|
|
10
|
+
Requires-Dist: ytmusicapi
|
|
11
|
+
|
|
12
|
+
# nowfycore
|
|
13
|
+
|
|
14
|
+
Pure Python core runtime for Nowfy.
|
|
15
|
+
|
|
16
|
+
## Local build
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
python -m build packages/nowfycore
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Artifacts:
|
|
23
|
+
- `packages/nowfycore/dist/nowfycore-1.0.0-py3-none-any.whl`
|
|
24
|
+
- `packages/nowfycore/dist/nowfycore-1.0.0.tar.gz`
|
|
25
|
+
|
|
26
|
+
## Release helper
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
python packages/nowfycore/scripts/release_nowfycore.py
|
|
30
|
+
python packages/nowfycore/scripts/release_nowfycore.py --upload --repository pypi
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Optional:
|
|
34
|
+
- `--repository testpypi`
|
|
35
|
+
- `--skip-existing`
|
|
36
|
+
|
|
37
|
+
## Runtime usage in Nowfy plugin
|
|
38
|
+
|
|
39
|
+
`nowfy.plugin` uses:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
__requirements__ = ["nowfycore>=1.0.0"]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
So only `nowfy.plugin` needs to be installed by the user; core runtime is resolved through requirements.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# nowfycore
|
|
2
|
+
|
|
3
|
+
Pure Python core runtime for Nowfy.
|
|
4
|
+
|
|
5
|
+
## Local build
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
python -m build packages/nowfycore
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Artifacts:
|
|
12
|
+
- `packages/nowfycore/dist/nowfycore-1.0.0-py3-none-any.whl`
|
|
13
|
+
- `packages/nowfycore/dist/nowfycore-1.0.0.tar.gz`
|
|
14
|
+
|
|
15
|
+
## Release helper
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
python packages/nowfycore/scripts/release_nowfycore.py
|
|
19
|
+
python packages/nowfycore/scripts/release_nowfycore.py --upload --repository pypi
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Optional:
|
|
23
|
+
- `--repository testpypi`
|
|
24
|
+
- `--skip-existing`
|
|
25
|
+
|
|
26
|
+
## Runtime usage in Nowfy plugin
|
|
27
|
+
|
|
28
|
+
`nowfy.plugin` uses:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
__requirements__ = ["nowfycore>=1.0.0"]
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
So only `nowfy.plugin` needs to be installed by the user; core runtime is resolved through requirements.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "nowfycore"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Nowfy core runtime package (pure layer)"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
authors = [{name = "AGeekApple"}]
|
|
12
|
+
dependencies = [
|
|
13
|
+
"requests",
|
|
14
|
+
"yt-dlp",
|
|
15
|
+
"ytmusicapi",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[tool.setuptools]
|
|
19
|
+
package-dir = {"" = "src"}
|
|
20
|
+
|
|
21
|
+
[tool.setuptools.packages.find]
|
|
22
|
+
where = ["src"]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from .api import get_nowfy_core_api, NowfyCoreAPI, NowfyCoreBackendMixin
|
|
2
|
+
from .language import SUPPORTED_LANGS, get_plugin_lang_code, get_plugin_lang_index, normalize_plugin_lang_index
|
|
3
|
+
from .state import CoreState
|
|
4
|
+
from .services import get_bio_feature_services
|
|
5
|
+
from .flags import is_addon_enabled, is_theme_enabled
|
|
6
|
+
from .bridge import invoke_action
|
|
7
|
+
from .corekeys import CORE_SETTINGS_KEYS, export_core_settings_from_plugin, import_core_settings_to_plugin
|
|
8
|
+
from .trackhub import append_track_hub_style_selector, build_track_hub_subfragment_items
|
|
9
|
+
from .panel import (
|
|
10
|
+
on_panel_drawer_toggle,
|
|
11
|
+
on_panel_disable_logs_toggle,
|
|
12
|
+
on_lab_nowplaying_pill_toggle,
|
|
13
|
+
on_lab_send_haptic_toggle,
|
|
14
|
+
)
|
|
15
|
+
from .cache import clear_cache_data
|
|
16
|
+
from .themes import inject_core_theme_multiselect
|
|
17
|
+
from .about import animate_opening_avatar, inject_about_support_rich_header
|
|
18
|
+
from .injections import (
|
|
19
|
+
inject_services_cards_custom,
|
|
20
|
+
inject_home_services_entry_custom,
|
|
21
|
+
inject_statsfm_profile_custom,
|
|
22
|
+
)
|
|
23
|
+
from .ui_controls import (
|
|
24
|
+
get_nowtab_control_style,
|
|
25
|
+
apply_nowtab_control_modern_ui,
|
|
26
|
+
update_nowtab_control_modern_state,
|
|
27
|
+
inject_track_hub_source_slide,
|
|
28
|
+
inject_nowtab_control_style_slide,
|
|
29
|
+
inject_vinify_logo_position_radios,
|
|
30
|
+
inject_vinify_cover_shape_slide,
|
|
31
|
+
inject_nowfy_pulse_style_slide,
|
|
32
|
+
inject_custom_cover_selector,
|
|
33
|
+
on_custom_cover_select,
|
|
34
|
+
on_custom_cover_toggle,
|
|
35
|
+
)
|
|
36
|
+
from .trackhub_ui import render_track_hub_box_cell, clear_track_hub_box_cell
|
|
37
|
+
from .tab_actions import send_tab_track_download, send_tab_music_preview
|
|
38
|
+
from .welcome import maybe_show_nowfy_welcome_sheet
|
|
39
|
+
from .translations import get_nowfy_translations
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
"get_nowfy_core_api",
|
|
43
|
+
"NowfyCoreAPI",
|
|
44
|
+
"NowfyCoreBackendMixin",
|
|
45
|
+
"SUPPORTED_LANGS",
|
|
46
|
+
"get_plugin_lang_code",
|
|
47
|
+
"get_plugin_lang_index",
|
|
48
|
+
"normalize_plugin_lang_index",
|
|
49
|
+
"CoreState",
|
|
50
|
+
"get_bio_feature_services",
|
|
51
|
+
"is_addon_enabled",
|
|
52
|
+
"is_theme_enabled",
|
|
53
|
+
"invoke_action",
|
|
54
|
+
"CORE_SETTINGS_KEYS",
|
|
55
|
+
"export_core_settings_from_plugin",
|
|
56
|
+
"import_core_settings_to_plugin",
|
|
57
|
+
"append_track_hub_style_selector",
|
|
58
|
+
"build_track_hub_subfragment_items",
|
|
59
|
+
"on_panel_drawer_toggle",
|
|
60
|
+
"on_panel_disable_logs_toggle",
|
|
61
|
+
"on_lab_nowplaying_pill_toggle",
|
|
62
|
+
"on_lab_send_haptic_toggle",
|
|
63
|
+
"clear_cache_data",
|
|
64
|
+
"inject_core_theme_multiselect",
|
|
65
|
+
"animate_opening_avatar",
|
|
66
|
+
"inject_about_support_rich_header",
|
|
67
|
+
"inject_services_cards_custom",
|
|
68
|
+
"inject_home_services_entry_custom",
|
|
69
|
+
"inject_statsfm_profile_custom",
|
|
70
|
+
"get_nowtab_control_style",
|
|
71
|
+
"apply_nowtab_control_modern_ui",
|
|
72
|
+
"update_nowtab_control_modern_state",
|
|
73
|
+
"inject_track_hub_source_slide",
|
|
74
|
+
"inject_nowtab_control_style_slide",
|
|
75
|
+
"inject_vinify_logo_position_radios",
|
|
76
|
+
"inject_vinify_cover_shape_slide",
|
|
77
|
+
"inject_nowfy_pulse_style_slide",
|
|
78
|
+
"inject_custom_cover_selector",
|
|
79
|
+
"on_custom_cover_select",
|
|
80
|
+
"on_custom_cover_toggle",
|
|
81
|
+
"render_track_hub_box_cell",
|
|
82
|
+
"clear_track_hub_box_cell",
|
|
83
|
+
"send_tab_track_download",
|
|
84
|
+
"send_tab_music_preview",
|
|
85
|
+
"maybe_show_nowfy_welcome_sheet",
|
|
86
|
+
"get_nowfy_translations",
|
|
87
|
+
]
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
def animate_opening_avatar(plugin, avatar_view, from_y_dp=20, duration_ms=620):
|
|
2
|
+
try:
|
|
3
|
+
if avatar_view is None:
|
|
4
|
+
return False
|
|
5
|
+
AndroidUtilities = None
|
|
6
|
+
try:
|
|
7
|
+
from hook_utils import find_class
|
|
8
|
+
|
|
9
|
+
AndroidUtilities = find_class("org.telegram.messenger.AndroidUtilities")
|
|
10
|
+
except Exception:
|
|
11
|
+
AndroidUtilities = None
|
|
12
|
+
try:
|
|
13
|
+
if AndroidUtilities is not None:
|
|
14
|
+
avatar_view.setCameraDistance(float(AndroidUtilities.dp(800)))
|
|
15
|
+
except Exception:
|
|
16
|
+
pass
|
|
17
|
+
try:
|
|
18
|
+
if AndroidUtilities is not None:
|
|
19
|
+
avatar_view.setTranslationY(float(AndroidUtilities.dp(int(from_y_dp or 20))))
|
|
20
|
+
except Exception:
|
|
21
|
+
pass
|
|
22
|
+
try:
|
|
23
|
+
avatar_view.setAlpha(0.0)
|
|
24
|
+
except Exception:
|
|
25
|
+
pass
|
|
26
|
+
try:
|
|
27
|
+
avatar_view.setRotationY(-120.0)
|
|
28
|
+
except Exception:
|
|
29
|
+
pass
|
|
30
|
+
try:
|
|
31
|
+
avatar_view.animate().cancel()
|
|
32
|
+
except Exception:
|
|
33
|
+
pass
|
|
34
|
+
try:
|
|
35
|
+
avatar_view.animate().translationY(0.0).alpha(1.0).rotationY(0.0).setDuration(int(duration_ms or 620)).start()
|
|
36
|
+
except Exception:
|
|
37
|
+
pass
|
|
38
|
+
return True
|
|
39
|
+
except Exception:
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _build_support_project_rich_item(plugin, description_text=None):
|
|
44
|
+
try:
|
|
45
|
+
from org.telegram.messenger import ApplicationLoader
|
|
46
|
+
from org.telegram.ui.Components import UItem
|
|
47
|
+
from android.widget import LinearLayout, TextView
|
|
48
|
+
from android.view import Gravity
|
|
49
|
+
from android.util import TypedValue
|
|
50
|
+
from android.graphics.drawable import GradientDrawable
|
|
51
|
+
|
|
52
|
+
Theme = None
|
|
53
|
+
AndroidUtilities = None
|
|
54
|
+
get_last_fragment = None
|
|
55
|
+
tr_fn = None
|
|
56
|
+
try:
|
|
57
|
+
import nowfy as nowfy_mod
|
|
58
|
+
|
|
59
|
+
Theme = getattr(nowfy_mod, "Theme", None)
|
|
60
|
+
AndroidUtilities = getattr(nowfy_mod, "AndroidUtilities", None)
|
|
61
|
+
get_last_fragment = getattr(nowfy_mod, "get_last_fragment", None)
|
|
62
|
+
tr_fn = getattr(nowfy_mod, "tr", None)
|
|
63
|
+
except Exception:
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
ctx = None
|
|
67
|
+
try:
|
|
68
|
+
frag = get_last_fragment() if callable(get_last_fragment) else None
|
|
69
|
+
ctx = frag.getParentActivity() if frag and frag.getParentActivity() else ApplicationLoader.applicationContext
|
|
70
|
+
except Exception:
|
|
71
|
+
ctx = ApplicationLoader.applicationContext
|
|
72
|
+
if ctx is None:
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
rich = LinearLayout(ctx)
|
|
76
|
+
rich.setOrientation(LinearLayout.VERTICAL)
|
|
77
|
+
rich.setGravity(Gravity.START)
|
|
78
|
+
try:
|
|
79
|
+
if AndroidUtilities is not None:
|
|
80
|
+
rich.setPadding(AndroidUtilities.dp(14), AndroidUtilities.dp(14), AndroidUtilities.dp(14), AndroidUtilities.dp(14))
|
|
81
|
+
except Exception:
|
|
82
|
+
pass
|
|
83
|
+
try:
|
|
84
|
+
bg = GradientDrawable()
|
|
85
|
+
if AndroidUtilities is not None:
|
|
86
|
+
bg.setCornerRadius(AndroidUtilities.dp(16))
|
|
87
|
+
bg.setColor(0x162B3E5A)
|
|
88
|
+
rich.setBackground(bg)
|
|
89
|
+
except Exception:
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
desc = TextView(ctx)
|
|
93
|
+
txt = str(description_text or "").strip()
|
|
94
|
+
if not txt:
|
|
95
|
+
try:
|
|
96
|
+
txt = str(plugin.tr("about_plugin_description") if hasattr(plugin, "tr") else "")
|
|
97
|
+
except Exception:
|
|
98
|
+
txt = ""
|
|
99
|
+
if not txt:
|
|
100
|
+
try:
|
|
101
|
+
txt = str(tr_fn("about_plugin_description") if callable(tr_fn) else "")
|
|
102
|
+
except Exception:
|
|
103
|
+
txt = ""
|
|
104
|
+
if not txt:
|
|
105
|
+
txt = "Support the project."
|
|
106
|
+
desc.setText(txt)
|
|
107
|
+
desc.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13)
|
|
108
|
+
desc.setGravity(Gravity.START)
|
|
109
|
+
try:
|
|
110
|
+
if Theme is not None:
|
|
111
|
+
desc.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText))
|
|
112
|
+
else:
|
|
113
|
+
desc.setTextColor(0xFFD2DCEB)
|
|
114
|
+
except Exception:
|
|
115
|
+
pass
|
|
116
|
+
rich.addView(desc)
|
|
117
|
+
return UItem.asCustom(rich)
|
|
118
|
+
except Exception:
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def inject_about_support_rich_header(plugin, activity, items):
|
|
123
|
+
try:
|
|
124
|
+
if activity is None or items is None:
|
|
125
|
+
return False
|
|
126
|
+
desc_txt = ""
|
|
127
|
+
try:
|
|
128
|
+
desc_txt = str(plugin.tr("about_plugin_description") if hasattr(plugin, "tr") else "")
|
|
129
|
+
except Exception:
|
|
130
|
+
desc_txt = ""
|
|
131
|
+
|
|
132
|
+
remove_idxs = []
|
|
133
|
+
for i in range(items.size()):
|
|
134
|
+
try:
|
|
135
|
+
it = items.get(i)
|
|
136
|
+
txt = ""
|
|
137
|
+
try:
|
|
138
|
+
if hasattr(plugin, "_get_uitem_text"):
|
|
139
|
+
txt = str(plugin._get_uitem_text(it) or "").strip()
|
|
140
|
+
except Exception:
|
|
141
|
+
txt = ""
|
|
142
|
+
if desc_txt and txt == desc_txt:
|
|
143
|
+
remove_idxs.append(i)
|
|
144
|
+
except Exception:
|
|
145
|
+
pass
|
|
146
|
+
for i in reversed(remove_idxs):
|
|
147
|
+
try:
|
|
148
|
+
items.remove(i)
|
|
149
|
+
except Exception:
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
rich_item = _build_support_project_rich_item(plugin, desc_txt)
|
|
153
|
+
if rich_item is not None:
|
|
154
|
+
try:
|
|
155
|
+
rich_item.object2 = "__about_support_rich__"
|
|
156
|
+
except Exception:
|
|
157
|
+
pass
|
|
158
|
+
try:
|
|
159
|
+
items.add(0, rich_item)
|
|
160
|
+
except Exception:
|
|
161
|
+
items.add(rich_item)
|
|
162
|
+
return True
|
|
163
|
+
return False
|
|
164
|
+
except Exception:
|
|
165
|
+
return False
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
from .language import (
|
|
2
|
+
get_plugin_lang_code as _get_plugin_lang_code,
|
|
3
|
+
get_plugin_lang_index as _get_plugin_lang_index,
|
|
4
|
+
normalize_plugin_lang_index as _normalize_plugin_lang_index,
|
|
5
|
+
)
|
|
6
|
+
from .state import CoreState
|
|
7
|
+
from .services import get_bio_feature_services as _get_bio_feature_services
|
|
8
|
+
from .flags import is_addon_enabled as _is_addon_enabled, is_theme_enabled as _is_theme_enabled
|
|
9
|
+
from .bridge import invoke_action as _invoke_action
|
|
10
|
+
from .corekeys import (
|
|
11
|
+
export_core_settings_from_plugin as _export_core_settings_from_plugin,
|
|
12
|
+
import_core_settings_to_plugin as _import_core_settings_to_plugin,
|
|
13
|
+
)
|
|
14
|
+
from .trackhub import build_track_hub_subfragment_items as _build_track_hub_subfragment_items
|
|
15
|
+
from .themes import inject_core_theme_multiselect as _inject_core_theme_multiselect
|
|
16
|
+
from .panel import (
|
|
17
|
+
on_panel_drawer_toggle as _on_panel_drawer_toggle,
|
|
18
|
+
on_panel_disable_logs_toggle as _on_panel_disable_logs_toggle,
|
|
19
|
+
on_lab_nowplaying_pill_toggle as _on_lab_nowplaying_pill_toggle,
|
|
20
|
+
on_lab_send_haptic_toggle as _on_lab_send_haptic_toggle,
|
|
21
|
+
)
|
|
22
|
+
from .cache import clear_cache_data as _clear_cache_data
|
|
23
|
+
from .about import (
|
|
24
|
+
animate_opening_avatar as _animate_opening_avatar,
|
|
25
|
+
inject_about_support_rich_header as _inject_about_support_rich_header,
|
|
26
|
+
)
|
|
27
|
+
from .injections import (
|
|
28
|
+
inject_services_cards_custom as _inject_services_cards_custom,
|
|
29
|
+
inject_home_services_entry_custom as _inject_home_services_entry_custom,
|
|
30
|
+
inject_statsfm_profile_custom as _inject_statsfm_profile_custom,
|
|
31
|
+
)
|
|
32
|
+
from .ui_controls import (
|
|
33
|
+
get_nowtab_control_style as _get_nowtab_control_style,
|
|
34
|
+
apply_nowtab_control_modern_ui as _apply_nowtab_control_modern_ui,
|
|
35
|
+
update_nowtab_control_modern_state as _update_nowtab_control_modern_state,
|
|
36
|
+
inject_track_hub_source_slide as _inject_track_hub_source_slide,
|
|
37
|
+
inject_nowtab_control_style_slide as _inject_nowtab_control_style_slide,
|
|
38
|
+
inject_vinify_logo_position_radios as _inject_vinify_logo_position_radios,
|
|
39
|
+
inject_vinify_cover_shape_slide as _inject_vinify_cover_shape_slide,
|
|
40
|
+
inject_nowfy_pulse_style_slide as _inject_nowfy_pulse_style_slide,
|
|
41
|
+
inject_custom_cover_selector as _inject_custom_cover_selector,
|
|
42
|
+
on_custom_cover_select as _on_custom_cover_select,
|
|
43
|
+
on_custom_cover_toggle as _on_custom_cover_toggle,
|
|
44
|
+
)
|
|
45
|
+
from .trackhub_ui import (
|
|
46
|
+
render_track_hub_box_cell as _render_track_hub_box_cell,
|
|
47
|
+
clear_track_hub_box_cell as _clear_track_hub_box_cell,
|
|
48
|
+
)
|
|
49
|
+
from .tab_actions import (
|
|
50
|
+
send_tab_track_download as _send_tab_track_download,
|
|
51
|
+
send_tab_music_preview as _send_tab_music_preview,
|
|
52
|
+
)
|
|
53
|
+
from .welcome import maybe_show_nowfy_welcome_sheet as _maybe_show_nowfy_welcome_sheet
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class NowfyCoreBackendMixin:
|
|
57
|
+
"""Compatibility mixin kept for migration safety."""
|
|
58
|
+
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class NowfyCoreAPI:
|
|
63
|
+
"""Core facade consumed by nowfy.plugin."""
|
|
64
|
+
|
|
65
|
+
def __init__(self):
|
|
66
|
+
self._state = CoreState()
|
|
67
|
+
self._runtime_plugin = None
|
|
68
|
+
|
|
69
|
+
def register_runtime_plugin(self, plugin):
|
|
70
|
+
self._runtime_plugin = plugin
|
|
71
|
+
return True
|
|
72
|
+
|
|
73
|
+
def is_core_active(self):
|
|
74
|
+
return True
|
|
75
|
+
|
|
76
|
+
def is_addon_enabled(self, plugin, addon_key, default_value=False):
|
|
77
|
+
return _is_addon_enabled(plugin, addon_key, default_value)
|
|
78
|
+
|
|
79
|
+
def are_translation_resources_ready(self):
|
|
80
|
+
plugin = self._runtime_plugin
|
|
81
|
+
if plugin is None:
|
|
82
|
+
return True
|
|
83
|
+
try:
|
|
84
|
+
tr_fn = getattr(plugin, "tr", None)
|
|
85
|
+
if not callable(tr_fn):
|
|
86
|
+
return True
|
|
87
|
+
probes = ("custom_command", "nowfy_panel_section_desc", "core_language_label")
|
|
88
|
+
hits = 0
|
|
89
|
+
for key in probes:
|
|
90
|
+
try:
|
|
91
|
+
val = str(tr_fn(key) or "").strip()
|
|
92
|
+
if val and val != key:
|
|
93
|
+
hits += 1
|
|
94
|
+
except Exception:
|
|
95
|
+
pass
|
|
96
|
+
return hits >= 2
|
|
97
|
+
except Exception:
|
|
98
|
+
return True
|
|
99
|
+
|
|
100
|
+
def ensure_translation_resources(self, silent=False):
|
|
101
|
+
plugin = self._runtime_plugin
|
|
102
|
+
if plugin is None:
|
|
103
|
+
return True
|
|
104
|
+
try:
|
|
105
|
+
if self.are_translation_resources_ready():
|
|
106
|
+
return True
|
|
107
|
+
# Best-effort hook if host exposes manual sync action.
|
|
108
|
+
sync_fn = getattr(plugin, "_run_sync_translations_action", None)
|
|
109
|
+
if callable(sync_fn):
|
|
110
|
+
try:
|
|
111
|
+
sync_fn()
|
|
112
|
+
except Exception:
|
|
113
|
+
pass
|
|
114
|
+
return self.are_translation_resources_ready()
|
|
115
|
+
except Exception:
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
def maybe_show_nowfy_welcome_sheet(self, plugin):
|
|
119
|
+
return _maybe_show_nowfy_welcome_sheet(plugin)
|
|
120
|
+
|
|
121
|
+
def get_nowtab_control_style(self, plugin):
|
|
122
|
+
return _get_nowtab_control_style(plugin)
|
|
123
|
+
|
|
124
|
+
def apply_nowtab_control_modern_ui(self, plugin, context, controls_row, control_container, *args, **kwargs):
|
|
125
|
+
return _apply_nowtab_control_modern_ui(plugin, context, controls_row, control_container, *args, **kwargs)
|
|
126
|
+
|
|
127
|
+
def update_nowtab_control_modern_state(self, plugin, is_playing):
|
|
128
|
+
return _update_nowtab_control_modern_state(plugin, is_playing)
|
|
129
|
+
|
|
130
|
+
def send_tab_track_download(self, plugin):
|
|
131
|
+
return _send_tab_track_download(plugin)
|
|
132
|
+
|
|
133
|
+
def send_tab_music_preview(self, plugin):
|
|
134
|
+
return _send_tab_music_preview(plugin)
|
|
135
|
+
|
|
136
|
+
def get_track_hub_style(self, plugin):
|
|
137
|
+
try:
|
|
138
|
+
return int(plugin.get_setting("track_hub_style", 0) or 0)
|
|
139
|
+
except Exception:
|
|
140
|
+
return 0
|
|
141
|
+
|
|
142
|
+
def render_track_hub_box_cell(self, plugin, cell, title_text, subtitle_text, cover_url=""):
|
|
143
|
+
return _render_track_hub_box_cell(plugin, cell, title_text, subtitle_text, cover_url)
|
|
144
|
+
|
|
145
|
+
def clear_track_hub_box_cell(self, plugin, cell):
|
|
146
|
+
return _clear_track_hub_box_cell(plugin, cell)
|
|
147
|
+
|
|
148
|
+
def inject_track_hub_source_slide(self, plugin, activity, items):
|
|
149
|
+
return _inject_track_hub_source_slide(plugin, activity, items)
|
|
150
|
+
|
|
151
|
+
def inject_nowtab_control_style_slide(self, plugin, activity, items):
|
|
152
|
+
return _inject_nowtab_control_style_slide(plugin, activity, items)
|
|
153
|
+
|
|
154
|
+
def inject_vinify_logo_position_radios(self, plugin, activity, items):
|
|
155
|
+
return _inject_vinify_logo_position_radios(plugin, activity, items)
|
|
156
|
+
|
|
157
|
+
def inject_vinify_cover_shape_slide(self, plugin, activity, items):
|
|
158
|
+
return _inject_vinify_cover_shape_slide(plugin, activity, items)
|
|
159
|
+
|
|
160
|
+
def inject_nowfy_pulse_style_slide(self, plugin, activity, items):
|
|
161
|
+
return _inject_nowfy_pulse_style_slide(plugin, activity, items)
|
|
162
|
+
|
|
163
|
+
def inject_custom_cover_selector(self, plugin, activity, items):
|
|
164
|
+
return _inject_custom_cover_selector(plugin, activity, items)
|
|
165
|
+
|
|
166
|
+
def on_custom_cover_select(self, plugin, value):
|
|
167
|
+
return _on_custom_cover_select(plugin, value)
|
|
168
|
+
|
|
169
|
+
def on_custom_cover_toggle(self, plugin, enabled_value):
|
|
170
|
+
return _on_custom_cover_toggle(plugin, enabled_value)
|
|
171
|
+
|
|
172
|
+
def get_core_setting(self, key, default=None):
|
|
173
|
+
return self._state.get(str(key), default, plugin=self._runtime_plugin)
|
|
174
|
+
|
|
175
|
+
def set_core_setting(self, key, value):
|
|
176
|
+
return self._state.set(str(key), value, plugin=self._runtime_plugin)
|
|
177
|
+
|
|
178
|
+
def is_theme_enabled(self, plugin, theme_key, default_value=False):
|
|
179
|
+
raw = str(self.get_core_setting("core_enabled_optional_themes", "") or "").strip()
|
|
180
|
+
return _is_theme_enabled(plugin, theme_key, default_value, core_enabled_optional_themes=raw)
|
|
181
|
+
|
|
182
|
+
def inject_core_theme_multiselect(self, plugin, activity, items):
|
|
183
|
+
return _inject_core_theme_multiselect(plugin, activity, items)
|
|
184
|
+
|
|
185
|
+
def inject_statsfm_profile_custom(self, plugin, activity, items):
|
|
186
|
+
return _inject_statsfm_profile_custom(plugin, activity, items)
|
|
187
|
+
|
|
188
|
+
def inject_services_cards_custom(self, plugin, activity, items):
|
|
189
|
+
return _inject_services_cards_custom(plugin, activity, items)
|
|
190
|
+
|
|
191
|
+
def inject_home_services_entry_custom(self, plugin, activity, items):
|
|
192
|
+
return _inject_home_services_entry_custom(plugin, activity, items)
|
|
193
|
+
|
|
194
|
+
def get_plugin_lang_index(self, plugin):
|
|
195
|
+
return _get_plugin_lang_index(plugin, key="plugin_lang", default=0)
|
|
196
|
+
|
|
197
|
+
def normalize_plugin_lang_index(self, idx):
|
|
198
|
+
return _normalize_plugin_lang_index(idx)
|
|
199
|
+
|
|
200
|
+
def get_plugin_lang_code(self, idx):
|
|
201
|
+
return _get_plugin_lang_code(idx)
|
|
202
|
+
|
|
203
|
+
def on_panel_drawer_toggle(self, plugin, enabled):
|
|
204
|
+
return _on_panel_drawer_toggle(plugin, enabled)
|
|
205
|
+
|
|
206
|
+
def on_panel_disable_logs_toggle(self, plugin, enabled, *args, **kwargs):
|
|
207
|
+
a0 = args[0] if len(args) >= 1 else None
|
|
208
|
+
a1 = args[1] if len(args) >= 2 else None
|
|
209
|
+
a2 = args[2] if len(args) >= 3 else None
|
|
210
|
+
a3 = args[3] if len(args) >= 4 else None
|
|
211
|
+
return _on_panel_disable_logs_toggle(plugin, enabled, a0, a1, a2, a3)
|
|
212
|
+
|
|
213
|
+
def on_lab_nowplaying_pill_toggle(self, plugin, enabled):
|
|
214
|
+
return _on_lab_nowplaying_pill_toggle(plugin, enabled)
|
|
215
|
+
|
|
216
|
+
def on_lab_send_haptic_toggle(self, plugin, enabled):
|
|
217
|
+
return _on_lab_send_haptic_toggle(plugin, enabled)
|
|
218
|
+
|
|
219
|
+
def clear_cache_data(self, plugin):
|
|
220
|
+
return _clear_cache_data(plugin)
|
|
221
|
+
|
|
222
|
+
def get_bio_feature_services(self, plugin):
|
|
223
|
+
return _get_bio_feature_services(plugin)
|
|
224
|
+
|
|
225
|
+
def build_track_hub_subfragment_items(self, plugin):
|
|
226
|
+
return _build_track_hub_subfragment_items(plugin)
|
|
227
|
+
|
|
228
|
+
def invoke_core_action(self, action_name):
|
|
229
|
+
plugin = self._runtime_plugin
|
|
230
|
+
if plugin is None:
|
|
231
|
+
return False
|
|
232
|
+
return _invoke_action(plugin, action_name)
|
|
233
|
+
|
|
234
|
+
def animate_opening_avatar(self, plugin, avatar_view, from_y_dp=20, duration_ms=620):
|
|
235
|
+
return _animate_opening_avatar(plugin, avatar_view, from_y_dp, duration_ms)
|
|
236
|
+
|
|
237
|
+
def inject_about_support_rich_header(self, plugin, activity, items):
|
|
238
|
+
return _inject_about_support_rich_header(plugin, activity, items)
|
|
239
|
+
|
|
240
|
+
def export_core_settings(self):
|
|
241
|
+
data = self._state.export()
|
|
242
|
+
plugin = self._runtime_plugin
|
|
243
|
+
if plugin is not None:
|
|
244
|
+
try:
|
|
245
|
+
data.update(_export_core_settings_from_plugin(plugin))
|
|
246
|
+
except Exception:
|
|
247
|
+
pass
|
|
248
|
+
return data
|
|
249
|
+
|
|
250
|
+
def import_core_settings(self, data):
|
|
251
|
+
applied = self._state.import_settings(data, plugin=self._runtime_plugin)
|
|
252
|
+
plugin = self._runtime_plugin
|
|
253
|
+
if plugin is not None:
|
|
254
|
+
try:
|
|
255
|
+
_import_core_settings_to_plugin(plugin, data)
|
|
256
|
+
except Exception:
|
|
257
|
+
pass
|
|
258
|
+
return applied
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
_API_SINGLETON = None
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def get_nowfy_core_api():
|
|
265
|
+
global _API_SINGLETON
|
|
266
|
+
if _API_SINGLETON is None:
|
|
267
|
+
_API_SINGLETON = NowfyCoreAPI()
|
|
268
|
+
return _API_SINGLETON
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
def invoke_action(plugin, action_name):
|
|
2
|
+
"""Invoke a runtime method on host plugin during staged migration."""
|
|
3
|
+
try:
|
|
4
|
+
name = str(action_name or "").strip()
|
|
5
|
+
if not name:
|
|
6
|
+
return False
|
|
7
|
+
fn = getattr(plugin, name, None)
|
|
8
|
+
if not callable(fn):
|
|
9
|
+
return False
|
|
10
|
+
fn()
|
|
11
|
+
return True
|
|
12
|
+
except Exception:
|
|
13
|
+
return False
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
def clear_cache_data(plugin):
|
|
2
|
+
try:
|
|
3
|
+
for name in (
|
|
4
|
+
"_image_cache",
|
|
5
|
+
"_cache_timestamps",
|
|
6
|
+
"_enhanced_image_cache",
|
|
7
|
+
"_base_theme_cache",
|
|
8
|
+
"_apple_cache",
|
|
9
|
+
"_youtube_thumbnail_cache",
|
|
10
|
+
"_soundcloud_thumbnail_cache",
|
|
11
|
+
"_valid_thumbnail_url_cache",
|
|
12
|
+
):
|
|
13
|
+
try:
|
|
14
|
+
obj = getattr(plugin, name, None)
|
|
15
|
+
if isinstance(obj, dict):
|
|
16
|
+
obj.clear()
|
|
17
|
+
except Exception:
|
|
18
|
+
pass
|
|
19
|
+
return True
|
|
20
|
+
except Exception:
|
|
21
|
+
return False
|