goblin-malin 0.1.0 → 0.1.1

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
@@ -8,23 +8,35 @@ A keyboard-driven terminal UI for downloading and tagging music tracks with meta
8
8
 
9
9
  ## Installation
10
10
 
11
- No easy installation yet. You can only run the project as developer:
11
+ ### Running the project as a developer
12
+
13
+ You can run the project as a developer:
12
14
 
13
15
  1. Clone the repository and open a terminal in the project directory
14
16
  2. Install [Node.js](https://nodejs.org/en/download) and [yarn](https://classic.yarnpkg.com/lang/en/docs/install)
15
17
  3. Install dependencies in the project directory: `yarn install`
16
18
  4. Run the application: `yarn run dev`
17
19
 
20
+ ### Launching the app in js code
21
+
22
+ > Not customizable yet
23
+
24
+ ```js
25
+ import GoblinMalin from "goblin-malin";
26
+
27
+ GoblinMalin.start();
28
+ ```
29
+
18
30
  ## Steps
19
31
 
20
32
  - Import track URLs from compatible streaming platforms with `Ctrl+V`:
21
- - Spotify
22
- - YouTube
33
+ - `Spotify`
34
+ - `YouTube`
23
35
  - System fetches primary metadata from the corresponding URL platform
24
36
  - System discovers the same track on other platforms (cross-referencing via ISRC or track/artist name)
25
37
  - Filters/orders metadata sources by relevance or leaves the default ranking chosen by the system (use `TAB` key to switch the focused window).
26
38
  - System downloads matching tracks from available download providers:
27
- - yt-dlp
39
+ - `yt-dlp`
28
40
  - User selects the best download source and previews the audio
29
41
  - User saves the file to the desired folder with embedded tags
30
42
 
@@ -521,7 +521,7 @@ var FlowSettings = class {
521
521
  };
522
522
 
523
523
  // src/flows/musicDownloadFlow/utils/downloadTask.ts
524
- import fs2 from "fs/promises";
524
+ import fs3 from "fs/promises";
525
525
 
526
526
  // src/base/task/task-status.ts
527
527
  var StatusType = /* @__PURE__ */ (function(StatusType2) {
@@ -995,6 +995,9 @@ function computeOutputPath(compiled, outputDir) {
995
995
  }
996
996
  __name(computeOutputPath, "computeOutputPath");
997
997
 
998
+ // src/flows/musicDownloadFlow/saveSettings.ts
999
+ import fs2 from "fs";
1000
+
998
1001
  // src/flows/musicDownloadFlow/settings.ts
999
1002
  import * as os from "os";
1000
1003
  import * as path2 from "path";
@@ -1012,6 +1015,7 @@ function extractProviderDefaults(registry) {
1012
1015
  return result;
1013
1016
  }
1014
1017
  __name(extractProviderDefaults, "extractProviderDefaults");
1018
+ var MEDIA_OUTPUT_DIR = path2.join(os.homedir(), "Music", "GoblinMalin");
1015
1019
  var BASE_DEFAULT_MUSIC_DOWNLOAD_FLOW_SETTINGS = {
1016
1020
  metadata: {
1017
1021
  autoFetchOnImport: false,
@@ -1023,7 +1027,8 @@ var BASE_DEFAULT_MUSIC_DOWNLOAD_FLOW_SETTINGS = {
1023
1027
  autoSaveToOutputDir: true,
1024
1028
  autoDeleteTempAfter24h: false,
1025
1029
  autoRelocateMissingFiles: false,
1026
- outputDir: path2.join(os.homedir(), "Music"),
1030
+ outputDir: MEDIA_OUTPUT_DIR,
1031
+ outputTemporaryDir: path2.join(MEDIA_OUTPUT_DIR, "tmp"),
1027
1032
  providers: {}
1028
1033
  }
1029
1034
  };
@@ -1033,10 +1038,20 @@ function getFlowSettings() {
1033
1038
  return SettingsStore.getInstance().getFlowSettings("music-downloader", BASE_DEFAULT_MUSIC_DOWNLOAD_FLOW_SETTINGS);
1034
1039
  }
1035
1040
  __name(getFlowSettings, "getFlowSettings");
1036
- function getDownloadDir() {
1037
- return getFlowSettings().download.outputDir;
1041
+ function getTempDownloadDir() {
1042
+ return getFlowSettings().download.outputTemporaryDir;
1038
1043
  }
1039
- __name(getDownloadDir, "getDownloadDir");
1044
+ __name(getTempDownloadDir, "getTempDownloadDir");
1045
+ function clearTempDownloads() {
1046
+ const dir = getTempDownloadDir();
1047
+ if (fs2.existsSync(dir)) {
1048
+ fs2.rmSync(dir, {
1049
+ recursive: true,
1050
+ force: true
1051
+ });
1052
+ }
1053
+ }
1054
+ __name(clearTempDownloads, "clearTempDownloads");
1040
1055
  function getSaveSettings() {
1041
1056
  const s = getFlowSettings();
1042
1057
  return {
@@ -1280,7 +1295,7 @@ var DownloadTask = class extends Task {
1280
1295
  for (const src of sources) {
1281
1296
  if (src !== selectedSource && src.savedFile) {
1282
1297
  try {
1283
- await fs2.unlink(src.savedFile.path);
1298
+ await fs3.unlink(src.savedFile.path);
1284
1299
  } catch (e) {
1285
1300
  if (e.code !== "ENOENT") throw e;
1286
1301
  }
@@ -1293,7 +1308,7 @@ var DownloadTask = class extends Task {
1293
1308
  }
1294
1309
  outputCreated = true;
1295
1310
  } else {
1296
- await fs2.copyFile(selectedSource.localFile.path, outputPath);
1311
+ await fs3.copyFile(selectedSource.localFile.path, outputPath);
1297
1312
  outputCreated = true;
1298
1313
  }
1299
1314
  await cleanAndTagFlac(outputPath, tags);
@@ -1315,7 +1330,7 @@ var DownloadTask = class extends Task {
1315
1330
  });
1316
1331
  } catch (err) {
1317
1332
  if (outputCreated) {
1318
- await fs2.unlink(outputPath).catch(() => {
1333
+ await fs3.unlink(outputPath).catch(() => {
1319
1334
  });
1320
1335
  }
1321
1336
  this.status.set({
@@ -1377,7 +1392,7 @@ __name(taskIdFromUrl, "taskIdFromUrl");
1377
1392
 
1378
1393
  // src/utils/cache.ts
1379
1394
  import { create } from "flat-cache";
1380
- import * as fs3 from "fs";
1395
+ import * as fs4 from "fs";
1381
1396
  var CACHE_ID = "api-cache";
1382
1397
  var cacheDir = getCacheDir();
1383
1398
  var cache = create({
@@ -1393,7 +1408,7 @@ var cache = create({
1393
1408
  function clearCache() {
1394
1409
  cache.clear();
1395
1410
  try {
1396
- fs3.rmSync(cacheDir, {
1411
+ fs4.rmSync(cacheDir, {
1397
1412
  recursive: true,
1398
1413
  force: true
1399
1414
  });
@@ -1766,7 +1781,7 @@ var EnvironmentError = class extends Error {
1766
1781
  };
1767
1782
 
1768
1783
  // src/base/env.ts
1769
- import * as fs4 from "fs/promises";
1784
+ import * as fs5 from "fs/promises";
1770
1785
  import * as path3 from "path";
1771
1786
  var Env = class {
1772
1787
  static {
@@ -1806,7 +1821,7 @@ var Env = class {
1806
1821
  async saveToEnvFile(key, value) {
1807
1822
  const envPath = path3.resolve(process.cwd(), ".env");
1808
1823
  try {
1809
- const envContent = await fs4.readFile(envPath, {
1824
+ const envContent = await fs5.readFile(envPath, {
1810
1825
  encoding: "utf-8",
1811
1826
  flag: "w+"
1812
1827
  });
@@ -1822,7 +1837,7 @@ var Env = class {
1822
1837
  ${newEntry}
1823
1838
  ` : `${newEntry}
1824
1839
  `;
1825
- await fs4.writeFile(envPath, updatedContent, "utf-8");
1840
+ await fs5.writeFile(envPath, updatedContent, "utf-8");
1826
1841
  this.logger.info(`Successfully added ${key} to .env file.`);
1827
1842
  } catch (error) {
1828
1843
  this.logger.error(`Failed to save ${key} to .env file:`, {
@@ -2793,7 +2808,7 @@ _ts_decorate2([
2793
2808
  ], YoutubeService.prototype, "getTrackMetadata", null);
2794
2809
 
2795
2810
  // src/flows/musicDownloadFlow/services/download-providers/ytdlp/YtDlpService.ts
2796
- import fs7 from "fs";
2811
+ import fs8 from "fs";
2797
2812
  import path7 from "path";
2798
2813
  import { YtDlp } from "ytdlp-nodejs";
2799
2814
 
@@ -2811,7 +2826,7 @@ var DownloadService = class extends ServiceBase {
2811
2826
  };
2812
2827
 
2813
2828
  // src/utils/ytdlp-setup.ts
2814
- import * as fs5 from "fs/promises";
2829
+ import * as fs6 from "fs/promises";
2815
2830
  import { createWriteStream } from "fs";
2816
2831
  import * as path4 from "path";
2817
2832
  import * as https from "https";
@@ -2830,13 +2845,13 @@ async function ensureYtDlpSetup() {
2830
2845
  const binaryName = `yt-dlp_${latestVersion}.exe`;
2831
2846
  const binaryPath = path4.join(getBinDir(), binaryName);
2832
2847
  try {
2833
- await fs5.access(binaryPath);
2848
+ await fs6.access(binaryPath);
2834
2849
  globalLogger.info(`yt-dlp ${latestVersion} already installed at ${binaryPath}`);
2835
2850
  return binaryPath;
2836
2851
  } catch {
2837
2852
  globalLogger.info(`yt-dlp ${latestVersion} not found, downloading\u2026`);
2838
2853
  }
2839
- await fs5.mkdir(getBinDir(), {
2854
+ await fs6.mkdir(getBinDir(), {
2840
2855
  recursive: true
2841
2856
  });
2842
2857
  await cleanupOldVersions("yt-dlp_", binaryName);
@@ -2856,14 +2871,14 @@ async function ensureYtDlpSetup() {
2856
2871
  __name(ensureYtDlpSetup, "ensureYtDlpSetup");
2857
2872
  async function findExistingBinary(prefix, suffix) {
2858
2873
  try {
2859
- const files = await fs5.readdir(getBinDir());
2874
+ const files = await fs6.readdir(getBinDir());
2860
2875
  const binaries = files.filter((file) => file.startsWith(prefix) && file.endsWith(suffix));
2861
2876
  if (binaries.length === 0) {
2862
2877
  return null;
2863
2878
  }
2864
2879
  binaries.sort().reverse();
2865
2880
  const binaryPath = path4.join(getBinDir(), binaries[0]);
2866
- await fs5.access(binaryPath);
2881
+ await fs6.access(binaryPath);
2867
2882
  return binaryPath;
2868
2883
  } catch (error) {
2869
2884
  globalLogger.warn(`Failed to find existing binary: ${error}`);
@@ -2898,10 +2913,10 @@ async function getLatestYtDlpVersion() {
2898
2913
  __name(getLatestYtDlpVersion, "getLatestYtDlpVersion");
2899
2914
  async function cleanupOldVersions(prefix, currentVersion) {
2900
2915
  try {
2901
- const files = await fs5.readdir(getBinDir());
2916
+ const files = await fs6.readdir(getBinDir());
2902
2917
  const oldVersions = files.filter((file) => file.startsWith(prefix) && file.endsWith(".exe") && file !== currentVersion);
2903
2918
  for (const file of oldVersions) {
2904
- await fs5.unlink(path4.join(getBinDir(), file));
2919
+ await fs6.unlink(path4.join(getBinDir(), file));
2905
2920
  globalLogger.info(`Cleaned up old version: ${file}`);
2906
2921
  }
2907
2922
  } catch (error) {
@@ -2929,7 +2944,7 @@ async function downloadFile(url, destination) {
2929
2944
  resolve2();
2930
2945
  });
2931
2946
  fileStream.on("error", (error) => {
2932
- fs5.unlink(destination).catch(() => {
2947
+ fs6.unlink(destination).catch(() => {
2933
2948
  });
2934
2949
  reject(error);
2935
2950
  });
@@ -2941,7 +2956,7 @@ async function downloadFile(url, destination) {
2941
2956
  __name(downloadFile, "downloadFile");
2942
2957
 
2943
2958
  // src/utils/ffmpeg-setup.ts
2944
- import * as fs6 from "fs/promises";
2959
+ import * as fs7 from "fs/promises";
2945
2960
  import { createWriteStream as createWriteStream2 } from "fs";
2946
2961
  import * as path5 from "path";
2947
2962
  import * as https2 from "https";
@@ -2962,13 +2977,13 @@ async function ensureFfmpeg() {
2962
2977
  const binaryName = `ffmpeg_${versionDate}.exe`;
2963
2978
  const binaryPath = path5.join(getBinDir(), binaryName);
2964
2979
  try {
2965
- await fs6.access(binaryPath);
2980
+ await fs7.access(binaryPath);
2966
2981
  globalLogger.info(`ffmpeg ${versionDate} already installed at ${binaryPath}`);
2967
2982
  return binaryPath;
2968
2983
  } catch {
2969
2984
  globalLogger.info(`ffmpeg ${versionDate} not found, downloading\u2026`);
2970
2985
  }
2971
- await fs6.mkdir(getBinDir(), {
2986
+ await fs7.mkdir(getBinDir(), {
2972
2987
  recursive: true
2973
2988
  });
2974
2989
  await cleanupOldVersions2("ffmpeg_", binaryName);
@@ -2985,8 +3000,8 @@ async function ensureFfmpeg() {
2985
3000
  }
2986
3001
  zip.extractEntryTo(ffmpegEntry, getBinDir(), false, true);
2987
3002
  const extractedPath = path5.join(getBinDir(), "ffmpeg.exe");
2988
- await fs6.rename(extractedPath, binaryPath);
2989
- await fs6.unlink(zipPath);
3003
+ await fs7.rename(extractedPath, binaryPath);
3004
+ await fs7.unlink(zipPath);
2990
3005
  globalLogger.info(`Successfully downloaded ffmpeg ${versionDate} to ${binaryPath}`);
2991
3006
  return binaryPath;
2992
3007
  } catch (error) {
@@ -3001,14 +3016,14 @@ async function ensureFfmpeg() {
3001
3016
  __name(ensureFfmpeg, "ensureFfmpeg");
3002
3017
  async function findExistingBinary2(prefix, suffix) {
3003
3018
  try {
3004
- const files = await fs6.readdir(getBinDir());
3019
+ const files = await fs7.readdir(getBinDir());
3005
3020
  const binaries = files.filter((file) => file.startsWith(prefix) && file.endsWith(suffix));
3006
3021
  if (binaries.length === 0) {
3007
3022
  return null;
3008
3023
  }
3009
3024
  binaries.sort().reverse();
3010
3025
  const binaryPath = path5.join(getBinDir(), binaries[0]);
3011
- await fs6.access(binaryPath);
3026
+ await fs7.access(binaryPath);
3012
3027
  return binaryPath;
3013
3028
  } catch (error) {
3014
3029
  globalLogger.warn(`Failed to find existing binary: ${error}`);
@@ -3043,10 +3058,10 @@ async function getLatestFfmpegRelease() {
3043
3058
  __name(getLatestFfmpegRelease, "getLatestFfmpegRelease");
3044
3059
  async function cleanupOldVersions2(prefix, currentVersion) {
3045
3060
  try {
3046
- const files = await fs6.readdir(getBinDir());
3061
+ const files = await fs7.readdir(getBinDir());
3047
3062
  const oldVersions = files.filter((file) => file.startsWith(prefix) && file.endsWith(".exe") && file !== currentVersion);
3048
3063
  for (const file of oldVersions) {
3049
- await fs6.unlink(path5.join(getBinDir(), file));
3064
+ await fs7.unlink(path5.join(getBinDir(), file));
3050
3065
  globalLogger.info(`Cleaned up old version: ${file}`);
3051
3066
  }
3052
3067
  } catch (error) {
@@ -3074,7 +3089,7 @@ async function downloadFile2(url, destination) {
3074
3089
  resolve2();
3075
3090
  });
3076
3091
  fileStream.on("error", (error) => {
3077
- fs6.unlink(destination).catch(() => {
3092
+ fs7.unlink(destination).catch(() => {
3078
3093
  });
3079
3094
  reject(error);
3080
3095
  });
@@ -3180,11 +3195,12 @@ var YtDlpService = class _YtDlpService extends DownloadService {
3180
3195
  });
3181
3196
  const artistName = trackMetadata.artists?.[0]?.name || "Unknown Artist";
3182
3197
  const outputName = `${artistName} - ${trackMetadata.trackName}`;
3198
+ const uriSlug = trackMetadata.uri?.replace(/::/g, "-").replace(/:/g, "-") ?? "";
3183
3199
  const format = "flac";
3184
- const filename = `${outputName}.${format}`;
3185
- const fullPath = path7.join(getDownloadDir(), filename);
3200
+ const filename = uriSlug ? `${outputName} ${uriSlug}.${format}` : `${outputName}.${format}`;
3201
+ const fullPath = path7.join(getTempDownloadDir(), filename);
3186
3202
  let localFile;
3187
- if (fs7.existsSync(fullPath)) {
3203
+ if (fs8.existsSync(fullPath)) {
3188
3204
  this.logger.info(`File already exists, skipping download: ${filename}`);
3189
3205
  localFile = {
3190
3206
  state: "found",
@@ -3194,14 +3210,14 @@ var YtDlpService = class _YtDlpService extends DownloadService {
3194
3210
  sourceUrl: trackUrl
3195
3211
  };
3196
3212
  } else {
3197
- if (!fs7.existsSync(getDownloadDir())) {
3198
- fs7.mkdirSync(getDownloadDir(), {
3213
+ if (!fs8.existsSync(getTempDownloadDir())) {
3214
+ fs8.mkdirSync(getTempDownloadDir(), {
3199
3215
  recursive: true
3200
3216
  });
3201
3217
  }
3202
3218
  const cookiesPath = path7.join(getBinDir(), "cookies.txt");
3203
3219
  let downloadOptions = {
3204
- paths: getDownloadDir(),
3220
+ paths: getTempDownloadDir(),
3205
3221
  output: filename,
3206
3222
  audioFormat: format,
3207
3223
  extractAudio: true,
@@ -3213,7 +3229,7 @@ var YtDlpService = class _YtDlpService extends DownloadService {
3213
3229
  this.logger.debug(`Download progress: ${progress.percentage}% ${progress.speed_str} (ETA: ${progress.eta_str})`);
3214
3230
  }, "onProgress")
3215
3231
  };
3216
- if (fs7.existsSync(cookiesPath)) {
3232
+ if (fs8.existsSync(cookiesPath)) {
3217
3233
  this.logger.debug(`Using cookies from ${cookiesPath}`);
3218
3234
  downloadOptions.cookies = cookiesPath;
3219
3235
  } else {
@@ -3386,6 +3402,23 @@ function buildFlowSettingsItems(flowSettings, metadataProviders, downloadProvide
3386
3402
  }
3387
3403
  }), "set")
3388
3404
  });
3405
+ items.push({
3406
+ kind: "textInput",
3407
+ indent: 0,
3408
+ label: "\u{1F5C1} Temporary download directory",
3409
+ get: /* @__PURE__ */ __name(() => flowSettings.download.outputTemporaryDir, "get"),
3410
+ set: /* @__PURE__ */ __name((v) => onChange({
3411
+ download: {
3412
+ outputTemporaryDir: v
3413
+ }
3414
+ }), "set")
3415
+ });
3416
+ items.push({
3417
+ kind: "action",
3418
+ indent: 0,
3419
+ label: "\u26D2 Clear temporary downloads",
3420
+ run: /* @__PURE__ */ __name(() => clearTempDownloads(), "run")
3421
+ });
3389
3422
  if (downloadProviders.length > 0) {
3390
3423
  items.push({
3391
3424
  kind: "subHeader",
@@ -3831,10 +3864,10 @@ import { spawn as spawn2 } from "child_process";
3831
3864
  import * as net from "net";
3832
3865
  import * as path9 from "path";
3833
3866
  import * as os2 from "os";
3834
- import * as fs9 from "fs";
3867
+ import * as fs10 from "fs";
3835
3868
 
3836
3869
  // src/utils/mpv-setup.ts
3837
- import * as fs8 from "fs/promises";
3870
+ import * as fs9 from "fs/promises";
3838
3871
  import { createWriteStream as createWriteStream3 } from "fs";
3839
3872
  import * as path8 from "path";
3840
3873
  import * as https3 from "https";
@@ -3874,13 +3907,13 @@ async function ensureMpv() {
3874
3907
  const binaryName = `mpv_${version}.exe`;
3875
3908
  const binaryPath = path8.join(getBinDir(), binaryName);
3876
3909
  try {
3877
- await fs8.access(binaryPath);
3910
+ await fs9.access(binaryPath);
3878
3911
  globalLogger.info(`mpv ${version} already at ${binaryPath}`);
3879
3912
  return binaryPath;
3880
3913
  } catch {
3881
3914
  globalLogger.info(`Downloading mpv ${version}...`);
3882
3915
  }
3883
- await fs8.mkdir(getBinDir(), {
3916
+ await fs9.mkdir(getBinDir(), {
3884
3917
  recursive: true
3885
3918
  });
3886
3919
  await cleanupOldVersions3("mpv_", binaryName);
@@ -3892,8 +3925,8 @@ async function ensureMpv() {
3892
3925
  await downloadFile3(asset.browser_download_url, archivePath);
3893
3926
  globalLogger.info("Extracting mpv.exe...");
3894
3927
  await extract7z(archivePath, getBinDir(), "mpv.exe");
3895
- await fs8.rename(path8.join(getBinDir(), "mpv.exe"), binaryPath);
3896
- await fs8.unlink(archivePath);
3928
+ await fs9.rename(path8.join(getBinDir(), "mpv.exe"), binaryPath);
3929
+ await fs9.unlink(archivePath);
3897
3930
  globalLogger.info(`mpv ${version} installed at ${binaryPath}`);
3898
3931
  return binaryPath;
3899
3932
  } catch (err) {
@@ -3950,11 +3983,11 @@ async function extract7z(archivePath, destDir, fileFilter) {
3950
3983
  __name(extract7z, "extract7z");
3951
3984
  async function findExistingBinary3(prefix, suffix) {
3952
3985
  try {
3953
- const files = await fs8.readdir(getBinDir());
3986
+ const files = await fs9.readdir(getBinDir());
3954
3987
  const matches = files.filter((f) => f.startsWith(prefix) && f.endsWith(suffix)).sort().reverse();
3955
3988
  if (!matches.length) return null;
3956
3989
  const p = path8.join(getBinDir(), matches[0]);
3957
- await fs8.access(p);
3990
+ await fs9.access(p);
3958
3991
  return p;
3959
3992
  } catch {
3960
3993
  return null;
@@ -3963,10 +3996,10 @@ async function findExistingBinary3(prefix, suffix) {
3963
3996
  __name(findExistingBinary3, "findExistingBinary");
3964
3997
  async function cleanupOldVersions3(prefix, currentName) {
3965
3998
  try {
3966
- const files = await fs8.readdir(getBinDir());
3999
+ const files = await fs9.readdir(getBinDir());
3967
4000
  for (const f of files) {
3968
4001
  if (f.startsWith(prefix) && f.endsWith(".exe") && f !== currentName) {
3969
- await fs8.unlink(path8.join(getBinDir(), f));
4002
+ await fs9.unlink(path8.join(getBinDir(), f));
3970
4003
  globalLogger.info(`Removed old mpv binary: ${f}`);
3971
4004
  }
3972
4005
  }
@@ -3991,7 +4024,7 @@ async function downloadFile3(url, destination) {
3991
4024
  res.pipe(ws);
3992
4025
  ws.on("finish", () => ws.close(() => resolve2()));
3993
4026
  ws.on("error", (e) => {
3994
- fs8.unlink(destination).catch(() => {
4027
+ fs9.unlink(destination).catch(() => {
3995
4028
  });
3996
4029
  reject(e);
3997
4030
  });
@@ -4048,8 +4081,8 @@ var MpvPlayer = class extends EventEmitter2 {
4048
4081
  return this._starting;
4049
4082
  }
4050
4083
  async _start() {
4051
- if (process.platform !== "win32" && fs9.existsSync(this.socketPath)) {
4052
- fs9.unlinkSync(this.socketPath);
4084
+ if (process.platform !== "win32" && fs10.existsSync(this.socketPath)) {
4085
+ fs10.unlinkSync(this.socketPath);
4053
4086
  }
4054
4087
  const mpvBin = await ensureMpv();
4055
4088
  this.mpvProcess = spawn2(mpvBin, [
@@ -4083,7 +4116,7 @@ var MpvPlayer = class extends EventEmitter2 {
4083
4116
  return new Promise((resolve2, reject) => {
4084
4117
  const deadline = Date.now() + timeoutMs;
4085
4118
  const poll = /* @__PURE__ */ __name(() => {
4086
- if (fs9.existsSync(this.socketPath)) {
4119
+ if (fs10.existsSync(this.socketPath)) {
4087
4120
  resolve2();
4088
4121
  } else if (Date.now() >= deadline) {
4089
4122
  reject(new Error("Timed out waiting for mpv IPC socket"));
@@ -7493,7 +7526,7 @@ var MetadataPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
7493
7526
  // src/components/SecondaryPanel/DownloadPanel/DownloadPanel.tsx
7494
7527
  import React45, { useState as useState15, useEffect as useEffect14 } from "react";
7495
7528
  import { Box as Box33, Text as Text42, useInput as useInput9 } from "ink";
7496
- import fs11 from "fs";
7529
+ import fs12 from "fs";
7497
7530
 
7498
7531
  // src/components/SecondaryPanel/DownloadPanel/DownloadSourceTree/DownloadSourceTree.tsx
7499
7532
  import React39 from "react";
@@ -7819,7 +7852,7 @@ var DownloadSourceTree = /* @__PURE__ */ __name(({ sources, selectedSourceIndex,
7819
7852
  import React44, { useState as useState14, useEffect as useEffect13 } from "react";
7820
7853
  import { Box as Box32, Text as Text41, useInput as useInput8 } from "ink";
7821
7854
  import * as path11 from "path";
7822
- import * as fs10 from "fs";
7855
+ import * as fs11 from "fs";
7823
7856
 
7824
7857
  // src/components/SecondaryPanel/DownloadPanel/PlaybackBar.tsx
7825
7858
  import React40 from "react";
@@ -8238,7 +8271,7 @@ var DownloadSourceDetail = /* @__PURE__ */ __name(({ source, savedSource, compil
8238
8271
  }
8239
8272
  const isSaved = !!source.savedFile;
8240
8273
  const borderColor = isSaved ? "white" : "green";
8241
- const outputFileExists = !isSaved && compiled !== null && fs10.existsSync(path11.join(outputDir, computeOutputFilename(compiled)));
8274
+ const outputFileExists = !isSaved && compiled !== null && fs11.existsSync(path11.join(outputDir, computeOutputFilename(compiled)));
8242
8275
  const headerLabel = isSaved ? "FILE ON DISK" : "NEW FILE";
8243
8276
  const headerColor = isSaved ? "white" : "green";
8244
8277
  const innerW = width - 2;
@@ -8544,7 +8577,7 @@ var DownloadPanel = /* @__PURE__ */ __name(({ selectedTask, width, height }) =>
8544
8577
  hint: "Absolute path to the FLAC file"
8545
8578
  });
8546
8579
  if (!newPath.trim()) return;
8547
- if (!fs11.existsSync(newPath)) return;
8580
+ if (!fs12.existsSync(newPath)) return;
8548
8581
  typedTask?.updateLocalFile(sourceIdx, newPath);
8549
8582
  } catch {
8550
8583
  }
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  start
4
- } from "./chunk-DJTET7PY.js";
4
+ } from "./chunk-IVBZEO3V.js";
5
5
  import "./chunk-ECLAXOR2.js";
6
6
 
7
7
  // src/cli.ts
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  src_default,
3
3
  start,
4
4
  withFullScreen
5
- } from "./chunk-DJTET7PY.js";
5
+ } from "./chunk-IVBZEO3V.js";
6
6
  import "./chunk-ECLAXOR2.js";
7
7
  export {
8
8
  src_default as default,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "goblin-malin",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "A keyboard-driven terminal UI for downloading and tagging music tracks with metadata from Spotify and YouTube",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -16,7 +16,8 @@
16
16
  },
17
17
  "files": [
18
18
  "dist",
19
- "README.md"
19
+ "README.md",
20
+ "docs/screenshots"
20
21
  ],
21
22
  "scripts": {
22
23
  "dev": "tsx src/cli.ts",