about summary refs log tree commit diff
path: root/users/Profpatsch/lyric/extension/src/upload-lrc.ts
diff options
context:
space:
mode:
authorProfpatsch <mail@profpatsch.de>2024-10-01T17·10+0200
committerProfpatsch <mail@profpatsch.de>2024-10-05T13·49+0000
commitcf68a34b0d882374a02aa418eccae4f588483094 (patch)
tree0d9a0ffd8674b47edba2cc6d4dc52eae7a5b6ece /users/Profpatsch/lyric/extension/src/upload-lrc.ts
parentad711b15a06be8c3f6146f14454621f4a56997ba (diff)
feat(users/Profpatsch/lyric/ext): add lrc upload & ms offset r/8767
This adds support for uploading the lyrics part of an .lrc file to
lrclib, see https://lrclib.net/docs

I pretty much only used ChatGPT to translate the rust “proof of work”
challenge to nodejs and it worked first try lol.

Before uploading the lyrics, I construct a webview with a preview of
what is going to be uploaded, and then only upload when that is
accepted. Pretty sweet.

Also adds two commands for increasing/decreasing the current timestamp
by 100ms and starting playback from 2 seconds before that, very handy
for fine-tuning lines.

Change-Id: Ia6adfe26d0c21c62554c8f8c55e97e2caec95d1e
Reviewed-on: https://cl.tvl.fyi/c/depot/+/12561
Reviewed-by: Profpatsch <mail@profpatsch.de>
Tested-by: BuildkiteCI
Diffstat (limited to 'users/Profpatsch/lyric/extension/src/upload-lrc.ts')
-rw-r--r--users/Profpatsch/lyric/extension/src/upload-lrc.ts114
1 files changed, 114 insertions, 0 deletions
diff --git a/users/Profpatsch/lyric/extension/src/upload-lrc.ts b/users/Profpatsch/lyric/extension/src/upload-lrc.ts
new file mode 100644
index 000000000000..6686483847d8
--- /dev/null
+++ b/users/Profpatsch/lyric/extension/src/upload-lrc.ts
@@ -0,0 +1,114 @@
+import * as crypto from 'crypto';
+
+// Helper function to convert a hex string to a Buffer
+function hexToBytes(hex: string): Buffer {
+  return Buffer.from(hex, 'hex');
+}
+
+// Function to verify the nonce
+function verifyNonce(result: Buffer, target: Buffer): boolean {
+  if (result.length !== target.length) {
+    return false;
+  }
+
+  for (let i = 0; i < result.length - 1; i++) {
+    if (result[i] > target[i]) {
+      return false;
+    } else if (result[i] < target[i]) {
+      break;
+    }
+  }
+
+  return true;
+}
+
+// Function to solve the challenge
+function solveChallenge(prefix: string, targetHex: string): string {
+  let nonce = 0;
+  let hashed: Buffer;
+  const target = hexToBytes(targetHex);
+
+  while (true) {
+    const input = `${prefix}${nonce}`;
+    hashed = crypto.createHash('sha256').update(input).digest();
+
+    if (verifyNonce(hashed, target)) {
+      break;
+    } else {
+      nonce += 1;
+    }
+  }
+
+  return nonce.toString();
+}
+
+async function getUploadNonce() {
+  try {
+    const response = await fetch('https://lrclib.net/api/request-challenge', {
+      method: 'POST',
+      headers: {
+        'Content-Type': 'application/json',
+        'User-Agent': 'lyric tool (https://code.tvl.fyi/tree/users/Profpatsch/lyric)',
+        'Lrclib-Client': 'lyric tool (https://code.tvl.fyi/tree/users/Profpatsch/lyric)',
+      },
+    });
+
+    if (!response.ok) {
+      throw new Error(`HTTP error! Status: ${response.status}`);
+    }
+
+    const challengeData = (await response.json()) as { prefix: string; target: string };
+
+    return {
+      prefix: challengeData.prefix,
+      nonce: solveChallenge(challengeData.prefix, challengeData.target),
+    };
+  } catch (error) {
+    console.error('Error fetching the challenge:', error);
+  }
+}
+
+// Interface for the request body
+/**
+ * Represents a request to publish a track with its associated information.
+ */
+export interface PublishRequest {
+  trackName: string;
+  artistName: string;
+  albumName: string;
+  /** In seconds? Milliseconds? mm:ss? */
+  duration: number;
+  plainLyrics: string;
+  syncedLyrics: string;
+}
+
+// Function to publish lyrics using the solved challenge
+export async function publishLyrics(
+  requestBody: PublishRequest,
+): Promise<true | undefined> {
+  const challenge = await getUploadNonce();
+  if (!challenge) {
+    return;
+  }
+  const publishToken = `${challenge.prefix}:${challenge.nonce}`;
+
+  const response = await fetch('https://lrclib.net/api/publish', {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'User-Agent': 'lyric tool (https://code.tvl.fyi/tree/users/Profpatsch/lyric)',
+      'Lrclib-Client': 'lyric tool (https://code.tvl.fyi/tree/users/Profpatsch/lyric)',
+      'X-Publish-Token': publishToken,
+    },
+    body: JSON.stringify(requestBody),
+  });
+
+  if (response.status === 201) {
+    console.log('Lyrics successfully published.');
+    return true;
+  } else {
+    const errorResponse = (await response.json()) as { [key: string]: string };
+    console.error('Failed to publish lyrics:', errorResponse);
+    return;
+  }
+}