levix-bot 3.2.0-beta → 3.2.1-beta

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.
package/README.md CHANGED
@@ -67,6 +67,18 @@ docker compose up -d
67
67
  docker compose logs levix
68
68
  ```
69
69
 
70
+ ### Android phone (No server required)
71
+
72
+ Run Levix directly on an ARM64 Android device (Android 10+) as a self-contained background companion:
73
+
74
+ 1. Download **`levix-android-arm64.apk`** from [Releases](https://github.com/Abdodiab2005/levix/releases).
75
+ 2. Install the APK and tap **Start Host**.
76
+ 3. Tap **Open Panel** to set your master password and link WhatsApp via 8-digit Pairing Code.
77
+
78
+ See [ANDROID.md](ANDROID.md) for full instructions, OEM battery optimizations (Xiaomi, Samsung, OnePlus), and offline handling.
79
+
80
+ ---
81
+
70
82
  Standalone Linux, macOS ARM64, and Windows binaries are available from the
71
83
  [latest GitHub release](https://github.com/Abdodiab2005/levix/releases/latest).
72
84
 
@@ -92,7 +104,7 @@ proxies, headless mode, backups, and troubleshooting.
92
104
  | Scheduling | One-off, daily, and weekly messages with durable jobs, delivery status, and manual retry |
93
105
  | Control panel | Live connection state, command settings, roles, permissions, keys, memory, schedules, and logs |
94
106
  | Media | Text, images, video, audio, QR codes, text-to-speech, and speech-to-text |
95
- | Deployment | npm, Docker, systemd installer, standalone binaries, headless mode, and safe domain setup |
107
+ | Deployment | Android APK, npm, Docker, systemd installer, standalone binaries, headless mode, and safe domain setup |
96
108
  | Storage | One SQLite database and one data directory for settings, sessions, memory, and logs |
97
109
 
98
110
  Every command can be enabled, disabled, renamed, and assigned permissions from
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "levix-bot",
3
- "version": "3.2.0-beta",
3
+ "version": "3.2.1-beta",
4
4
  "description": "A self-hosted personal WhatsApp bot with an AI agent, group moderation and a web control panel.",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -47,46 +47,62 @@ async function synthesizeMp3(text, mp3Path) {
47
47
  await fsp.writeFile(mp3Path, Buffer.concat(buffers));
48
48
  }
49
49
 
50
+ async function transcodeWithEncoder(bin, encoder, mp3Path, oggPath) {
51
+ return execFileAsync(
52
+ bin,
53
+ [
54
+ "-hide_banner",
55
+ "-loglevel",
56
+ "error",
57
+ "-y",
58
+ "-i",
59
+ mp3Path,
60
+ "-vn",
61
+ "-c:a",
62
+ encoder,
63
+ "-ac",
64
+ "1",
65
+ "-ar",
66
+ "48000",
67
+ "-b:a",
68
+ "48k",
69
+ "-application",
70
+ "voip",
71
+ "-f",
72
+ "ogg",
73
+ oggPath,
74
+ ],
75
+ { windowsHide: true, timeout: 30_000 }
76
+ );
77
+ }
78
+
50
79
  async function transcodeToOpus(mp3Path) {
51
80
  const bin = ffmpegPath();
52
81
  if (!bin) return null;
53
82
 
54
83
  const oggPath = tmpPath("ogg");
55
- try {
56
- await execFileAsync(
57
- bin,
58
- [
59
- "-hide_banner",
60
- "-loglevel",
61
- "error",
62
- "-y",
63
- "-i",
64
- mp3Path,
65
- "-vn",
66
- "-c:a",
67
- "libopus",
68
- "-ac",
69
- "1",
70
- "-ar",
71
- "48000",
72
- "-b:a",
73
- "48k",
74
- "-application",
75
- "voip",
76
- "-f",
77
- "ogg",
78
- oggPath,
79
- ],
80
- { windowsHide: true, timeout: 30_000 },
81
- );
82
- return oggPath;
83
- } catch (err) {
84
- logger.warn(
85
- { err: err?.message, bin },
86
- "[TTS] ffmpeg transcode failed — will fall back to raw MP3"
87
- );
88
- return null;
84
+ const encoders = ["libopus", "opus"];
85
+ let lastError = null;
86
+
87
+ for (const encoder of encoders) {
88
+ try {
89
+ await transcodeWithEncoder(bin, encoder, mp3Path, oggPath);
90
+ return oggPath;
91
+ } catch (err) {
92
+ lastError = err;
93
+ const msg = `${err?.message || ""} ${err?.stderr || ""}`;
94
+ if (msg.includes("Unknown encoder")) {
95
+ continue;
96
+ }
97
+ break;
98
+ }
89
99
  }
100
+
101
+ logger.warn(
102
+ { err: lastError?.message, bin },
103
+ "[TTS] ffmpeg transcode failed — will fall back to raw MP3"
104
+ );
105
+ return null;
90
106
  }
91
107
 
92
108
  module.exports = {