@officebeats/matrix-iptv-cli 3.3.6 → 4.0.2

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/index.html CHANGED
@@ -1,4 +1,4 @@
1
- <!DOCTYPE html>
1
+ <!doctype html>
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
@@ -25,18 +25,60 @@
25
25
  line-height: 1.1;
26
26
  overflow: hidden;
27
27
  border-bottom: 1px solid #003b00;
28
+ touch-action: none; /* Prevent browser scrolling to allow custom touch gestures */
28
29
  }
29
30
  #video-container {
30
31
  width: 100%;
31
32
  background: #000;
32
33
  display: none;
33
- position: relative;
34
+ position: relative; /* ensure overlay positioning works */
34
35
  aspect-ratio: 16 / 9;
35
36
  border-top: 2px solid #00ff41;
37
+ box-shadow: 0 0 20px rgba(0, 255, 65, 0.4);
36
38
  }
37
39
  video {
38
40
  width: 100%;
39
41
  height: 100%;
42
+ outline: none;
43
+ }
44
+ #video-close {
45
+ position: absolute;
46
+ top: 10px;
47
+ right: 10px;
48
+ background: rgba(13, 2, 8, 0.8);
49
+ color: #00ff41;
50
+ border: 1px solid #00ff41;
51
+ padding: 8px 12px;
52
+ cursor: pointer;
53
+ z-index: 10;
54
+ font-weight: bold;
55
+ font-family: inherit;
56
+ backdrop-filter: blur(4px);
57
+ transition: all 0.2s ease;
58
+ }
59
+ #video-close:hover {
60
+ background: #00ff41;
61
+ color: #0d0208;
62
+ box-shadow: 0 0 10px #00ff41;
63
+ }
64
+ .mobile-controls {
65
+ display: none;
66
+ position: absolute;
67
+ bottom: 20px;
68
+ left: 0;
69
+ width: 100%;
70
+ justify-content: center;
71
+ gap: 10px;
72
+ z-index: 10;
73
+ opacity: 0.7;
74
+ }
75
+ .ctrl-btn {
76
+ background: rgba(13, 2, 8, 0.8);
77
+ border: 1px solid #00ff41;
78
+ color: #00ff41;
79
+ padding: 10px 15px;
80
+ font-weight: bold;
81
+ border-radius: 5px;
40
82
  }
41
83
  @media (min-width: 1024px) {
42
84
  #terminal {
@@ -49,13 +91,33 @@
49
91
  right: 20px;
50
92
  width: 480px;
51
93
  border: 2px solid #00ff41;
94
+ box-shadow: 0 0 15px rgba(0, 255, 65, 0.5);
95
+ }
96
+ }
97
+ @media (max-width: 1023px) {
98
+ .mobile-controls {
99
+ display: flex;
52
100
  }
53
101
  }
54
102
  </style>
55
103
  </head>
56
104
  <body>
57
105
  <div id="terminal">Loading Matrix Interface...</div>
106
+ <div class="mobile-controls">
107
+ <!-- Added explicit back button for easy exit on mobile without gestures -->
108
+ <button
109
+ class="ctrl-btn"
110
+ onclick="
111
+ document.dispatchEvent(
112
+ new KeyboardEvent('keydown', { key: 'Escape' }),
113
+ )
114
+ "
115
+ >
116
+ ESC / BACK
117
+ </button>
118
+ </div>
58
119
  <div id="video-container">
120
+ <button id="video-close" onclick="closeVideo()">[ X ] CLOSE</button>
59
121
  <video id="video" controls playsinline></video>
60
122
  </div>
61
123
 
@@ -82,6 +144,13 @@
82
144
  });
83
145
  }
84
146
  };
147
+
148
+ window.closeVideo = function () {
149
+ const video = document.getElementById("video");
150
+ video.pause();
151
+ video.src = "";
152
+ document.getElementById("video-container").style.display = "none";
153
+ };
85
154
  </script>
86
155
 
87
156
  <script type="module">
@@ -116,6 +185,55 @@
116
185
  }
117
186
  client.handle_key(event.key);
118
187
  });
188
+
189
+ // Touch Input Handling Setup
190
+ let touchStartX = 0;
191
+ let touchStartY = 0;
192
+ let touchStartT = 0;
193
+
194
+ document.addEventListener(
195
+ "touchstart",
196
+ (e) => {
197
+ if (
198
+ e.target.tagName.toLowerCase() === "button" ||
199
+ e.target.tagName.toLowerCase() === "video"
200
+ )
201
+ return;
202
+ touchStartX = e.changedTouches[0].screenX;
203
+ touchStartY = e.changedTouches[0].screenY;
204
+ touchStartT = Date.now();
205
+ },
206
+ { passive: false },
207
+ );
208
+
209
+ document.addEventListener("touchend", (e) => {
210
+ if (
211
+ e.target.tagName.toLowerCase() === "button" ||
212
+ e.target.tagName.toLowerCase() === "video"
213
+ )
214
+ return;
215
+ let touchEndX = e.changedTouches[0].screenX;
216
+ let touchEndY = e.changedTouches[0].screenY;
217
+ let dx = touchEndX - touchStartX;
218
+ let dy = touchEndY - touchStartY;
219
+ let dt = Date.now() - touchStartT;
220
+
221
+ if (dt < 400) {
222
+ // Tap or quick swipe
223
+ if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 30) {
224
+ // Horizontal Swipe
225
+ if (dx > 0) client.handle_key("ArrowRight");
226
+ else client.handle_key("ArrowLeft");
227
+ } else if (Math.abs(dy) > Math.abs(dx) && Math.abs(dy) > 30) {
228
+ // Vertical Swipe
229
+ if (dy > 0) client.handle_key("ArrowDown");
230
+ else client.handle_key("ArrowUp");
231
+ } else if (Math.abs(dx) < 10 && Math.abs(dy) < 10) {
232
+ // Tap
233
+ client.handle_key("Enter");
234
+ }
235
+ }
236
+ });
119
237
  } catch (e) {
120
238
  console.error("Wasm Init Failed:", e);
121
239
  document.getElementById("terminal").textContent =
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@officebeats/matrix-iptv-cli",
3
- "version": "3.3.6",
3
+ "version": "4.0.2",
4
4
  "description": "The premium Terminal IPTV Decoder",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -0,0 +1,49 @@
1
+ import socket
2
+ import requests
3
+
4
+ host = "zfruvync.duperab.xyz"
5
+ print(f"Checking host: {host}")
6
+
7
+ # 1. System DNS
8
+ try:
9
+ ip = socket.gethostbyname(host)
10
+ print(f"[System DNS] Resolved to: {ip}")
11
+ except Exception as e:
12
+ print(f"[System DNS] Failed: {e}")
13
+
14
+ # 2. Google DoH
15
+ try:
16
+ resp = requests.get(f"https://dns.google/resolve?name={host}", timeout=10)
17
+ data = resp.json()
18
+ if data.get("Status") == 0:
19
+ ips = [ans["data"] for ans in data.get("Answer", []) if ans["type"] == 1]
20
+ print(f"[Google DoH] Resolved to: {ips}")
21
+ else:
22
+ print(f"[Google DoH] Failed with status {data.get('Status')}")
23
+ except Exception as e:
24
+ print(f"[Google DoH] Error: {e}")
25
+
26
+ # 3. Cloudflare DoH
27
+ try:
28
+ resp = requests.get(f"https://cloudflare-dns.com/dns-query?name={host}", headers={"Accept": "application/dns-json"}, timeout=10)
29
+ data = resp.json()
30
+ if data.get("Status") == 0:
31
+ ips = [ans["data"] for ans in data.get("Answer", []) if ans["type"] == 1]
32
+ print(f"[Cloudflare DoH] Resolved to: {ips}")
33
+ else:
34
+ print(f"[Cloudflare DoH] Failed with status {data.get('Status')}")
35
+ except Exception as e:
36
+ print(f"[Cloudflare DoH] Error: {e}")
37
+
38
+ # 4. Quad9 DoH
39
+ try:
40
+ # Quad9 has a JSON API at https://dns.quad9.net/resolve?name=
41
+ resp = requests.get(f"https://dns.quad9.net/resolve?name={host}", timeout=10)
42
+ data = resp.json()
43
+ if data.get("Status") == 0:
44
+ ips = [ans["data"] for ans in data.get("Answer", []) if ans["type"] == 1]
45
+ print(f"[Quad9 DoH] Resolved to: {ips}")
46
+ else:
47
+ print(f"[Quad9 DoH] Failed with status {data.get('Status')}")
48
+ except Exception as e:
49
+ print(f"[Quad9 DoH] Error: {e}")
@@ -39,26 +39,35 @@ if (!fs.existsSync(binDir)) {
39
39
 
40
40
  function download(url, dest) {
41
41
  return new Promise((resolve, reject) => {
42
- const file = fs.createWriteStream(dest);
43
42
  https
44
43
  .get(url, (response) => {
45
- if (response.statusCode === 302 || response.statusCode === 301) {
46
- // Handle Redirect
44
+ if (response.statusCode === 302 || response.statusCode === 301 || response.statusCode === 307 || response.statusCode === 308) {
45
+ // Handle Redirect without leaving locks open
47
46
  download(response.headers.location, dest).then(resolve).catch(reject);
48
47
  return;
49
48
  }
49
+
50
50
  if (response.statusCode !== 200) {
51
51
  reject(new Error(`Failed to download: ${response.statusCode}`));
52
52
  return;
53
53
  }
54
+
55
+ // Only open the file stream if the download actually works
56
+ const file = fs.createWriteStream(dest);
54
57
  response.pipe(file);
58
+
55
59
  file.on("finish", () => {
56
60
  file.close();
57
61
  resolve();
58
62
  });
63
+
64
+ file.on("error", (err) => {
65
+ file.close();
66
+ fs.unlink(dest, () => {});
67
+ reject(err);
68
+ });
59
69
  })
60
70
  .on("error", (err) => {
61
- fs.unlink(dest, () => {});
62
71
  reject(err);
63
72
  });
64
73
  });
Binary file
Binary file
@@ -0,0 +1,34 @@
1
+ fn main() {
2
+ let mock_name = "US | MSNBC HEVC";
3
+
4
+ // Stream layout extraction natively replicated to simulate Wave Terminal bugs
5
+ let mut spans = vec![];
6
+ let row_num = format!("{:>4} ", 1);
7
+ spans.push(row_num);
8
+ spans.push(" ".to_string()); // Used to be `│ `
9
+
10
+ let c = format!("{:<30} ", "MSNBC HEVC");
11
+ spans.push(c);
12
+
13
+ println!("DEBUG: Stream constraint outputs - ");
14
+ for span in spans {
15
+ println!("--> '{}' (len {})", span, span.len());
16
+ }
17
+
18
+ // Category Grid constraints
19
+ println!("\nDEBUG: Category 4-column output simulation - ");
20
+ let max_name_len: usize = 20;
21
+ let count: usize = 1205;
22
+ let count_str = format!("{:04}", count); // Used to be `[{:04}]`
23
+
24
+ let pre_pad = " "; // active
25
+ let fav_marker = " ";
26
+ let cat_icon = "🎬 "; // 3 bytes, 2 chars visual? Unicode width handling is likely what breaks Wave
27
+ let name_clean = format!("{}{}{} {}", pre_pad, fav_marker, cat_icon, "MOVIES");
28
+
29
+ let padded_name = format!("{:<max_name_len$}", name_clean, max_name_len=max_name_len);
30
+ println!("--> '{}' (len {} chars, {} bytes)", padded_name, padded_name.chars().count(), padded_name.len());
31
+ println!("--> '{}' (len {})", count_str, count_str.len());
32
+ println!("--> GAP: '{}' (len {})", "", "".len()); // Changed padding space to empty constraint
33
+ }
34
+