osu-play 1.3.1 → 1.3.3

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/dist/cli.js CHANGED
@@ -29,8 +29,33 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
29
29
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
30
30
 
31
31
  // src/core/utils/mod.ts
32
+ var exports_mod = {};
33
+ __export(exports_mod, {
34
+ revealFile: () => revealFile,
35
+ hashedFilePath: () => hashedFilePath,
36
+ getRealmDBPath: () => getRealmDBPath,
37
+ getDefaultOsuDataDir: () => getDefaultOsuDataDir,
38
+ getDefaultConfigPath: () => getDefaultConfigPath,
39
+ getDataDir: () => getDataDir,
40
+ getConfigDir: () => getConfigDir
41
+ });
32
42
  import path from "node:path";
33
43
  import { existsSync } from "node:fs";
44
+ import { spawn } from "node:child_process";
45
+ function revealFile(filePath) {
46
+ const command = process.platform === "darwin" ? { args: ["-R", filePath], executable: "open" } : process.platform === "win32" ? { args: ["/select,", filePath], executable: "explorer.exe" } : { args: [path.dirname(filePath)], executable: "xdg-open" };
47
+ return new Promise((resolve, reject) => {
48
+ const child = spawn(command.executable, command.args, {
49
+ detached: true,
50
+ stdio: "ignore"
51
+ });
52
+ child.once("error", reject);
53
+ child.once("spawn", () => {
54
+ child.unref();
55
+ resolve();
56
+ });
57
+ });
58
+ }
34
59
  function getDataDir() {
35
60
  switch (process.platform) {
36
61
  case "linux":
@@ -400,8 +425,8 @@ var init_mod2 = __esm(() => {
400
425
  });
401
426
 
402
427
  // src/core/lazer/mod.ts
403
- var exports_mod = {};
404
- __export(exports_mod, {
428
+ var exports_mod2 = {};
429
+ __export(exports_mod2, {
405
430
  inspectLazerSchemaEntries: () => inspectLazerSchemaEntries,
406
431
  inspectLazerSchema: () => inspectLazerSchema,
407
432
  hashedFilePath: () => hashedFilePath,
@@ -540,7 +565,7 @@ function buildPlaylist(beatmapSets, osuDataDir) {
540
565
  }
541
566
 
542
567
  // src/core/player/mpv.ts
543
- import { spawn } from "node:child_process";
568
+ import { spawn as spawn2 } from "node:child_process";
544
569
  import { existsSync as existsSync2, rmSync } from "node:fs";
545
570
  import net from "node:net";
546
571
  import os from "node:os";
@@ -695,7 +720,7 @@ class MpvPlayerBackend {
695
720
  this.cleanupSocketPath();
696
721
  this.disposing = false;
697
722
  this.stderrBuffer = "";
698
- const processHandle = spawn("mpv", [
723
+ const processHandle = spawn2("mpv", [
699
724
  "--no-config",
700
725
  "--no-terminal",
701
726
  "--idle=yes",
@@ -974,6 +999,13 @@ function findTrackIndexByQuery(playlist, query) {
974
999
  }
975
1000
  return playlist.findIndex((track) => normalizeSearchText(track.title).includes(normalizedQuery));
976
1001
  }
1002
+ function findTrackIndicesByQuery(playlist, query) {
1003
+ const normalizedQuery = normalizeSearchText(query);
1004
+ if (!normalizedQuery) {
1005
+ return playlist.map((_, index) => index);
1006
+ }
1007
+ return playlist.flatMap((track, index) => normalizeSearchText(track.title).includes(normalizedQuery) ? [index] : []);
1008
+ }
977
1009
 
978
1010
  class PlaylistPlayerSession {
979
1011
  backend;
@@ -984,6 +1016,7 @@ class PlaylistPlayerSession {
984
1016
  deleteTrack;
985
1017
  playlist;
986
1018
  reloadPlaylist;
1019
+ revealTrack;
987
1020
  searchQuery = "";
988
1021
  selectedIndex = 0;
989
1022
  operationQueue = Promise.resolve();
@@ -994,6 +1027,7 @@ class PlaylistPlayerSession {
994
1027
  this.deleteTrack = options.deleteTrack;
995
1028
  this.loop = options.loop ?? false;
996
1029
  this.reloadPlaylist = options.reloadPlaylist;
1030
+ this.revealTrack = options.revealTrack;
997
1031
  this.unsubscribeBackend = backend.subscribe((event) => {
998
1032
  this.handleBackendEvent(event);
999
1033
  });
@@ -1034,24 +1068,44 @@ class PlaylistPlayerSession {
1034
1068
  if (this.playlist.length === 0) {
1035
1069
  return;
1036
1070
  }
1071
+ if (this.searchQuery) {
1072
+ this.moveSearchSelection(delta);
1073
+ return;
1074
+ }
1037
1075
  this.selectedIndex = wrapIndex(this.selectedIndex + delta, this.playlist.length);
1038
1076
  this.emit();
1039
1077
  }
1040
1078
  moveSelectionPage(delta, pageSize) {
1079
+ if (this.searchQuery) {
1080
+ this.moveSearchSelection(delta * Math.max(1, pageSize));
1081
+ return;
1082
+ }
1041
1083
  this.moveSelection(delta * Math.max(1, pageSize));
1042
1084
  }
1085
+ moveSearchSelection(delta) {
1086
+ const matches = findTrackIndicesByQuery(this.playlist, this.searchQuery);
1087
+ if (matches.length === 0) {
1088
+ return;
1089
+ }
1090
+ const currentMatchIndex = matches.indexOf(this.selectedIndex);
1091
+ const nextMatchIndex = wrapIndex(currentMatchIndex + delta, matches.length);
1092
+ this.selectedIndex = matches[nextMatchIndex] ?? this.selectedIndex;
1093
+ this.emit();
1094
+ }
1043
1095
  selectHome() {
1044
1096
  if (this.playlist.length === 0) {
1045
1097
  return;
1046
1098
  }
1047
- this.selectedIndex = 0;
1099
+ const matches = findTrackIndicesByQuery(this.playlist, this.searchQuery);
1100
+ this.selectedIndex = matches[0] ?? 0;
1048
1101
  this.emit();
1049
1102
  }
1050
1103
  selectEnd() {
1051
1104
  if (this.playlist.length === 0) {
1052
1105
  return;
1053
1106
  }
1054
- this.selectedIndex = this.playlist.length - 1;
1107
+ const matches = findTrackIndicesByQuery(this.playlist, this.searchQuery);
1108
+ this.selectedIndex = matches.at(-1) ?? this.playlist.length - 1;
1055
1109
  this.emit();
1056
1110
  }
1057
1111
  setSelectionIndex(index) {
@@ -1157,6 +1211,22 @@ class PlaylistPlayerSession {
1157
1211
  }
1158
1212
  });
1159
1213
  }
1214
+ async revealSelectedTrack() {
1215
+ const track = this.playlist[this.selectedIndex];
1216
+ if (!track) {
1217
+ return;
1218
+ }
1219
+ if (!this.revealTrack) {
1220
+ this.reportError(new Error("Opening the containing folder is unavailable."));
1221
+ return;
1222
+ }
1223
+ try {
1224
+ this.clearError();
1225
+ await this.revealTrack(track);
1226
+ } catch (error) {
1227
+ this.reportError(error);
1228
+ }
1229
+ }
1160
1230
  async deleteSelectedTrack() {
1161
1231
  const selectedIndex = this.selectedIndex;
1162
1232
  await this.enqueueOperation(async () => {
@@ -1463,6 +1533,10 @@ class PlaylistPlayerScreen {
1463
1533
  this.session.toggleLoop();
1464
1534
  return;
1465
1535
  }
1536
+ if (matchesKey(data, "o")) {
1537
+ this.session.revealSelectedTrack();
1538
+ return;
1539
+ }
1466
1540
  if (matchesKey(data, Key.backspace)) {
1467
1541
  this.session.deleteSearchCharacter();
1468
1542
  return;
@@ -1474,9 +1548,11 @@ class PlaylistPlayerScreen {
1474
1548
  }
1475
1549
  render(width) {
1476
1550
  const { playlist } = this.snapshot;
1551
+ const visibleIndices = this.snapshot.searchQuery ? findTrackIndicesByQuery(playlist, this.snapshot.searchQuery) : playlist.map((_, index) => index);
1552
+ const selectedVisibleIndex = Math.max(0, visibleIndices.indexOf(this.snapshot.selectedIndex));
1477
1553
  const viewportHeight = Math.max(this.getViewportHeight(), RESERVED_ROWS);
1478
1554
  const listHeight = this.getListHeight(viewportHeight);
1479
- const { start, end } = getVisibleTrackRange(this.snapshot.selectedIndex, playlist.length, listHeight);
1555
+ const { start, end } = getVisibleTrackRange(selectedVisibleIndex, visibleIndices.length, listHeight);
1480
1556
  const position = formatSeconds(this.snapshot.timePositionSeconds);
1481
1557
  const duration = formatSeconds(this.snapshot.durationSeconds);
1482
1558
  const glyph = STATUS_GLYPH[this.snapshot.status] ?? "·";
@@ -1498,8 +1574,14 @@ class PlaylistPlayerScreen {
1498
1574
  }
1499
1575
  if (playlist.length === 0) {
1500
1576
  lines.push(truncateToWidth("No tracks were found in your osu!lazer library.", width));
1577
+ } else if (visibleIndices.length === 0) {
1578
+ lines.push(truncateToWidth("No tracks match the current search.", width));
1501
1579
  } else {
1502
- for (let index = start;index < end; index += 1) {
1580
+ for (let visibleIndex = start;visibleIndex < end; visibleIndex += 1) {
1581
+ const index = visibleIndices[visibleIndex];
1582
+ if (index === undefined) {
1583
+ continue;
1584
+ }
1503
1585
  const track = playlist[index];
1504
1586
  if (!track) {
1505
1587
  continue;
@@ -1514,7 +1596,7 @@ class PlaylistPlayerScreen {
1514
1596
  while (lines.length < viewportHeight - 1) {
1515
1597
  lines.push("");
1516
1598
  }
1517
- lines.push(truncateToWidth(style(this.searchMode ? "type to jump · backspace edit · enter keep · esc leave" : "j/k move · ⏎ play · space pause · n/p track · h/l seek · r loop · d delete · / search · q quit", DIM), width));
1599
+ lines.push(truncateToWidth(style(this.searchMode ? "type to filter · ↑/↓ results · enter play · backspace edit · esc leave" : "j/k move · ⏎ play · space pause · n/p track · h/l seek · o reveal · r loop · d delete · / search · q quit", DIM), width));
1518
1600
  return lines;
1519
1601
  }
1520
1602
  armPendingGoToTop() {
@@ -1556,6 +1638,7 @@ class PlaylistPlayerScreen {
1556
1638
  }
1557
1639
  if (matchesKey(data, Key.enter)) {
1558
1640
  this.searchMode = false;
1641
+ this.session.playSelected();
1559
1642
  return;
1560
1643
  }
1561
1644
  if (matchesKey(data, Key.escape)) {
@@ -1570,8 +1653,16 @@ class PlaylistPlayerScreen {
1570
1653
  this.session.clearSearch();
1571
1654
  return;
1572
1655
  }
1656
+ if (matchesKey(data, Key.up) || matchesKey(data, Key.ctrl("p"))) {
1657
+ this.session.moveSearchSelection(-1);
1658
+ return;
1659
+ }
1660
+ if (matchesKey(data, Key.down) || matchesKey(data, Key.ctrl("n"))) {
1661
+ this.session.moveSearchSelection(1);
1662
+ return;
1663
+ }
1573
1664
  const printable = decodePrintableText(data);
1574
- if (!printable || printable === " ") {
1665
+ if (!printable) {
1575
1666
  return;
1576
1667
  }
1577
1668
  this.session.appendSearchQuery(printable);
@@ -1614,7 +1705,7 @@ async function loadRealmDependencies() {
1614
1705
  try {
1615
1706
  const [{ default: Realm8 }, lazerModule] = await Promise.all([
1616
1707
  import("realm"),
1617
- Promise.resolve().then(() => (init_mod3(), exports_mod))
1708
+ Promise.resolve().then(() => (init_mod3(), exports_mod2))
1618
1709
  ]);
1619
1710
  return {
1620
1711
  Realm: Realm8,
@@ -1679,11 +1770,13 @@ async function deleteTrackFromCollection(track, osuDataDir) {
1679
1770
  await deleteBeatmapSet2(track, osuDataDir);
1680
1771
  }
1681
1772
  async function runTuiPlayer(playlist, osuDataDir, loop) {
1773
+ const { revealFile: revealFile2 } = await Promise.resolve().then(() => (init_mod(), exports_mod));
1682
1774
  const backend = new MpvPlayerBackend;
1683
1775
  const session = new PlaylistPlayerSession(playlist, backend, {
1684
1776
  deleteTrack: (track) => deleteTrackFromCollection(track, osuDataDir),
1685
1777
  loop,
1686
- reloadPlaylist: () => createPlaylist(osuDataDir)
1778
+ reloadPlaylist: () => createPlaylist(osuDataDir),
1779
+ revealTrack: (track) => revealFile2(track.path)
1687
1780
  });
1688
1781
  const terminal = new ProcessTerminal;
1689
1782
  const tui = new TUI(terminal);
@@ -1742,4 +1835,4 @@ main().then(() => {
1742
1835
  process.exit(1);
1743
1836
  });
1744
1837
 
1745
- //# debugId=2535B39351F13C9664756E2164756E21
1838
+ //# debugId=E1B92021231DEE2064756E2164756E21