Skip to content

Instantly share code, notes, and snippets.

@zhuochun
Last active December 22, 2024 07:12
Show Gist options
  • Save zhuochun/f06e9fa18d9b7fab843e5dd8116312ea to your computer and use it in GitHub Desktop.
Save zhuochun/f06e9fa18d9b7fab843e5dd8116312ea to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name Hacker News Comment Extractor
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Extract and copy comments from Hacker News threads
// @author YourName
// @match https://news.ycombinator.com/item*
// @grant GM_setClipboard
// ==/UserScript==
(function() {
'use strict';
// Create and add clipboard button
const addClipboardButton = () => {
const submissionRow = document.querySelector('tr.athing.submission');
if (!submissionRow) return;
const button = document.createElement('button');
button.textContent = 'πŸ“‹ Copy Comments';
button.style.marginLeft = '2px';
button.style.cursor = 'pointer';
button.title = 'Copy processed comments to clipboard';
button.addEventListener('click', () => {
const comments = Array.from(
document.querySelectorAll('table.comment-tree .commtext.c00')
).map(div => div.textContent.trim())
.filter(text => text.split(/\s+/).length >= 20)
.map(text => text.replace(/\n+/g, '\n').trim());
if (comments.length > 100) {
comments.length = 100; // Limit to 100 comments
}
const finalText = `Summarize these comments by groups:\n\n${comments.join('\n\n')}`;
GM_setClipboard(finalText);
// Provide some feedback to the user
button.textContent = 'βœ…';
setTimeout(() => { button.textContent = 'πŸ“‹'; }, 2000);
});
submissionRow.appendChild(button);
};
// Add button after page load
window.addEventListener('load', addClipboardButton);
})();
// ==UserScript==
// @name YouTube Transcript to Clipboard
// @namespace http://tampermonkey.net/
// @version 1.3
// @description Adds a button to copy the transcript to the clipboard.
// @author You
// @match https://www.youtube.com/*
// @grant GM_setClipboard
// ==/UserScript==
(function() {
'use strict';
// A helper function to wait for an element to appear in the DOM.
function waitForElement(selector, timeout = 10000) {
return new Promise((resolve, reject) => {
const start = Date.now();
(function check() {
const element = document.querySelector(selector);
if (element) {
resolve(element);
} else if (Date.now() - start >= timeout) {
reject(new Error(`Element ${selector} not found within ${timeout}ms`));
} else {
requestAnimationFrame(check);
}
})();
});
}
// Create the clipboard button
function createClipboardButton() {
const btn = document.createElement('button');
btn.style.display = 'inline-flex';
btn.style.alignItems = 'center';
btn.style.justifyContent = 'center';
btn.style.marginLeft = '8px';
btn.style.padding = '4px';
btn.style.border = 'none';
btn.style.borderRadius = '2px';
btn.style.cursor = 'pointer';
btn.style.background = 'transparent';
btn.style.color = 'var(--yt-spec-text-primary)';
btn.title = 'Copy transcript';
// Adding a clipboard icon (using an emoji for simplicity)
btn.textContent = 'πŸ“‹';
btn.addEventListener('click', async () => {
try {
const transcriptPanel = document.querySelector('ytd-engagement-panel-section-list-renderer[target-id="engagement-panel-searchable-transcript"]');
if (!transcriptPanel) {
alert("Transcript panel not found!");
return;
}
// Extract title
const titleElement = document.querySelector('h1.ytd-watch-metadata');
const title = titleElement ? titleElement.textContent.trim() : 'Untitled';
// Extract text from the transcript panel
let text = transcriptPanel.querySelector('#segments-container')?.textContent || '';
// Split by newline, trim lines, remove empty lines
const lines = text
.split('\n')
.map(line => line.trim())
.filter(line => line !== '');
// Combine lines based on timestamps
const timestampRegex = /^\d{1,2}:\d{2}(?::\d{2})?$/;
const combinedLines = [];
let currentEntry = '';
for (const line of lines) {
if (timestampRegex.test(line)) {
if (currentEntry) {
combinedLines.push(currentEntry.trim());
}
currentEntry = line; // Start a new entry with the timestamp
} else {
currentEntry += ` ${line}`; // Append line to the current entry
}
}
if (currentEntry) {
combinedLines.push(currentEntry.trim());
}
const finalText = `Title: ${title}\nTranscript:\n${combinedLines.join('\n')}`;
GM_setClipboard(finalText);
// Provide some feedback to the user
btn.textContent = 'βœ…';
setTimeout(() => { btn.textContent = 'πŸ“‹'; }, 2000);
} catch (err) {
console.error("Failed to copy text: ", err);
}
});
return btn;
}
// Wait for the owner container and add the clipboard button
waitForElement('#owner').then(ownerContainer => {
// Ensure we only add one button if the script re-runs
if (!ownerContainer.querySelector('.clipboard-button')) {
const clipboardButton = createClipboardButton();
clipboardButton.classList.add('clipboard-button');
ownerContainer.appendChild(clipboardButton);
}
}).catch((error) => {
console.warn(error);
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment