@involvex/youtube-music-cli 0.0.0 → 0.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.
@@ -1,10 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { usePlayer } from "../../hooks/usePlayer.js";
3
3
  import NowPlaying from "../player/NowPlaying.js";
4
- import ProgressBar from "../player/ProgressBar.js";
5
4
  import QueueList from "../player/QueueList.js";
6
5
  import { Box } from 'ink';
7
6
  export default function PlayerLayout() {
8
7
  const { state: playerState } = usePlayer();
9
- return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(NowPlaying, {}), _jsx(ProgressBar, {}), playerState.queue.length > 0 && _jsx(QueueList, {})] }));
8
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(NowPlaying, {}), playerState.queue.length > 0 && _jsx(QueueList, {})] }));
10
9
  }
@@ -10,12 +10,15 @@ export default function NowPlaying() {
10
10
  const { state: playerState } = usePlayer();
11
11
  const { columns } = useTerminalSize();
12
12
  if (!playerState.currentTrack) {
13
- return (_jsx(Box, { borderStyle: "round", borderColor: theme.colors.dim, padding: 1, marginY: 1, children: _jsx(Text, { color: theme.colors.dim, children: "No track playing" }) }));
13
+ return (_jsx(Box, { borderStyle: "round", borderColor: theme.colors.dim, paddingX: 1, children: _jsx(Text, { color: theme.colors.dim, children: "No track playing" }) }));
14
14
  }
15
15
  const track = playerState.currentTrack;
16
16
  const artists = track.artists?.map(a => a.name).join(', ') || 'Unknown Artist';
17
- return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.colors.primary, padding: 1, marginY: 1, children: [_jsx(Text, { bold: true, color: theme.colors.primary, children: track.title }), _jsx(Text, { color: theme.colors.secondary, children: artists }), track.album && _jsx(Text, { color: theme.colors.dim, children: track.album.name }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: theme.colors.text, children: formatTime(playerState.progress) }), _jsx(Text, { children: " " }), _jsxs(Text, { color: theme.colors.dim, children: ["[", Math.round((playerState.progress / (playerState.duration || 1)) * 100), "%]"] }), _jsx(Text, { children: " " }), _jsx(Text, { color: theme.colors.text, children: formatTime(playerState.duration) })] }), playerState.duration > 0 && (_jsxs(Box, { children: [_jsx(Text, { color: theme.colors.primary, children: '■'.repeat(Math.floor((playerState.progress / playerState.duration) * (columns - 10))) }), _jsx(Text, { color: theme.colors.dim, children: '-'.repeat(Math.max(0, columns -
18
- 10 -
19
- Math.floor((playerState.progress / playerState.duration) *
20
- (columns - 10)))) })] })), playerState.isLoading && (_jsx(Text, { color: theme.colors.accent, children: "Loading..." })), playerState.error && (_jsx(Text, { color: theme.colors.error, children: playerState.error }))] }));
17
+ // Clamp progress to valid range
18
+ const progress = Math.max(0, Math.min(playerState.progress, playerState.duration || 0));
19
+ const duration = playerState.duration || 0;
20
+ const percentage = duration > 0 ? Math.min(100, Math.floor((progress / duration) * 100)) : 0;
21
+ const barWidth = Math.max(10, columns - 8);
22
+ const filledWidth = duration > 0 ? Math.floor((progress / duration) * barWidth) : 0;
23
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.colors.primary, paddingX: 1, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, color: theme.colors.primary, children: track.title }), _jsx(Text, { color: theme.colors.dim, children: " \u2022 " }), _jsx(Text, { color: theme.colors.secondary, children: artists })] }), track.album && _jsx(Text, { color: theme.colors.dim, children: track.album.name }), _jsxs(Box, { children: [_jsx(Text, { color: theme.colors.primary, children: '█'.repeat(Math.min(filledWidth, barWidth)) }), _jsx(Text, { color: theme.colors.dim, children: '░'.repeat(Math.max(0, barWidth - filledWidth)) })] }), _jsxs(Box, { children: [_jsx(Text, { color: theme.colors.text, children: formatTime(progress) }), _jsxs(Text, { color: theme.colors.dim, children: [" / ", formatTime(duration), " "] }), _jsxs(Text, { color: theme.colors.dim, children: ["[", percentage, "%]"] }), playerState.isLoading && (_jsx(Text, { color: theme.colors.accent, children: " Loading..." })), !playerState.isPlaying && progress > 0 && (_jsx(Text, { color: theme.colors.dim, children: " \u23F8" }))] }), playerState.error && (_jsx(Text, { color: theme.colors.error, children: playerState.error }))] }));
21
24
  }
@@ -1,4 +1,4 @@
1
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // Progress bar component
3
3
  import { Box, Text } from 'ink';
4
4
  import { useTheme } from "../../hooks/useTheme.js";
@@ -10,9 +10,10 @@ export default function ProgressBar() {
10
10
  if (!playerState.currentTrack || !playerState.duration) {
11
11
  return null;
12
12
  }
13
- const progress = playerState.progress;
13
+ // Clamp values to valid range
14
+ const progress = Math.max(0, Math.min(playerState.progress, playerState.duration));
14
15
  const duration = playerState.duration;
15
- const percentage = duration > 0 ? Math.floor((progress / duration) * 100) : 0;
16
- const barWidth = Math.max(0, Math.min(20, Math.floor(percentage / 5))); // 20 chars max, bounds checked
17
- return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: theme.colors.text, children: [formatTime(progress), " / ", formatTime(duration)] }), _jsxs(Text, { color: theme.colors.dim, children: [" ", percentage, "%"] })] }), _jsxs(Box, { children: [_jsx(Text, { color: theme.colors.primary, children: ''.repeat(barWidth) }), _jsx(Text, { color: theme.colors.dim, children: '-'.repeat(20 - barWidth) })] })] }));
16
+ const percentage = duration > 0 ? Math.min(100, Math.floor((progress / duration) * 100)) : 0;
17
+ const barWidth = Math.min(20, Math.floor(percentage / 5));
18
+ return (_jsxs(Box, { children: [_jsx(Text, { color: theme.colors.text, children: formatTime(progress) }), _jsx(Text, { color: theme.colors.dim, children: "/" }), _jsx(Text, { color: theme.colors.text, children: formatTime(duration) }), _jsx(Text, { children: " " }), _jsx(Text, { color: theme.colors.primary, children: ''.repeat(barWidth) }), _jsx(Text, { color: theme.colors.dim, children: ''.repeat(20 - barWidth) }), _jsxs(Text, { color: theme.colors.dim, children: [" ", percentage, "%"] })] }));
18
19
  }
@@ -1,4 +1,4 @@
1
1
  import React from 'react';
2
- declare function QueueList(): import("react/jsx-runtime").JSX.Element;
2
+ declare function QueueList(): import("react/jsx-runtime").JSX.Element | null;
3
3
  declare const _default: React.MemoExoticComponent<typeof QueueList>;
4
4
  export default _default;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  // Queue management component
3
3
  import { useState } from 'react';
4
4
  import React from 'react';
@@ -18,13 +18,19 @@ function QueueList() {
18
18
  return Math.max(20, Math.floor(baseLength * scale));
19
19
  };
20
20
  if (playerState.queue.length === 0) {
21
- return (_jsx(Box, { paddingX: 1, children: _jsx(Text, { color: theme.colors.dim, children: "Queue is empty" }) }));
21
+ return null;
22
22
  }
23
- return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(Box, { borderStyle: "double", borderColor: theme.colors.secondary, paddingX: 1, marginBottom: 1, children: _jsxs(Text, { bold: true, color: theme.colors.primary, children: ["Queue (", playerState.queue.length, " tracks)"] }) }), playerState.queue.map((track, index) => {
23
+ // Show only next 5 tracks
24
+ const visibleQueue = playerState.queue.slice(playerState.queuePosition + 1, playerState.queuePosition + 6);
25
+ if (visibleQueue.length === 0) {
26
+ return null;
27
+ }
28
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.dim, children: ["Up next (", playerState.queue.length - playerState.queuePosition - 1, ' ', "tracks)"] }), visibleQueue.map((track, idx) => {
29
+ const index = playerState.queuePosition + 1 + idx;
24
30
  const isSelected = index === selectedIndex;
25
31
  const artists = track.artists?.map(a => a.name).join(', ') || 'Unknown';
26
- const title = truncate(track.title, getTruncateLength(50));
27
- return (_jsxs(Box, { paddingX: 1, borderStyle: isSelected ? 'double' : undefined, borderColor: isSelected ? theme.colors.primary : undefined, children: [_jsxs(Text, { color: theme.colors.dim, children: [index + 1, "."] }), _jsx(Text, { color: isSelected ? theme.colors.primary : theme.colors.text, bold: isSelected, children: title }), _jsxs(Text, { color: theme.colors.dim, children: [' - ', artists] })] }, track.videoId));
32
+ const title = truncate(track.title, getTruncateLength(40));
33
+ return (_jsxs(Box, { children: [_jsxs(Text, { color: theme.colors.dim, children: [index + 1, ". "] }), _jsx(Text, { color: isSelected ? theme.colors.primary : theme.colors.text, children: title }), _jsxs(Text, { color: theme.colors.dim, children: [" \u2022 ", artists] })] }, track.videoId));
28
34
  })] }));
29
35
  }
30
36
  export default React.memo(QueueList);
@@ -111,7 +111,8 @@ class PluginInstallerService {
111
111
  windowsHide: true,
112
112
  });
113
113
  }
114
- const pluginSourceDir = join(tempDir, 'plugins', pluginName);
114
+ // Plugin is at root of repo (e.g., adblock/, lyrics/, etc.)
115
+ const pluginSourceDir = join(tempDir, pluginName);
115
116
  if (!existsSync(pluginSourceDir)) {
116
117
  rmSync(tempDir, { recursive: true, force: true });
117
118
  return {
@@ -141,12 +141,19 @@ function playerReducer(state, action) {
141
141
  }
142
142
  return state;
143
143
  case 'UPDATE_PROGRESS':
144
- return { ...state, progress: action.progress };
144
+ // Clamp progress to valid range
145
+ const clampedProgress = Math.max(0, Math.min(action.progress, state.duration || action.progress));
146
+ return { ...state, progress: clampedProgress };
145
147
  case 'SET_DURATION':
146
148
  return { ...state, duration: action.duration };
147
149
  case 'TICK':
148
- if (state.isPlaying) {
149
- return { ...state, progress: state.progress + 1 };
150
+ if (state.isPlaying && state.duration > 0) {
151
+ const newProgress = state.progress + 1;
152
+ // Don't exceed duration
153
+ if (newProgress >= state.duration) {
154
+ return { ...state, progress: state.duration, isPlaying: false };
155
+ }
156
+ return { ...state, progress: newProgress };
150
157
  }
151
158
  return state;
152
159
  case 'SET_LOADING':
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@involvex/youtube-music-cli",
3
- "version": "0.0.0",
3
+ "version": "0.0.2",
4
4
  "description": "- A Commandline music player for youtube-music",
5
5
  "repository": {
6
6
  "type": "git",
File without changes