This is my script for exporting links from freetube to obsidian using clipboard.
Usage
- Open dev tools (F12) in freetube
- Go to tab sources
- Create new snippet, past code and run it (ctr+enter)
- When compiled or when
exportYoutubeMetadata();
is called it will print formatted link on console and copy it to system clipboard.
Link formatting
By default it will export link like this:
- Video title - [DD/MM/YYYY](https:youtu.be/xxxxxxxxxxx) - [[Channel]]
Customisation
Link Format
- Change function
formatOutput()
Change Channel Name
- There is map
NamesToConvert
that stores channel names that should be converted to other name.
- First string is original channel name
- Second is the name that you want in your link.
Video titles
Script
Contributions
- I would want to add button to freetube UI that run this code without using dev tools, but I’m not js dev, and never worked with electron apps. If you know how to do this, I’m open for suggestions.
1 Like
const {clipboard} = require('electron');
const NamesToConvert = new Map();
NamesToConvert.set('VisualPolitik EN', "VisualPolitik");
const TitlesToTrim = new Map();
TitlesToTrim.set("VisualPolitik", [" | VisualPolitik EN"," | @VisualPolitikEN"," | <U+202A>@VisualPolitikEN<U+202C>", " <U+202A>@VisualPolitikEN<U+202C>", " - VisualPolitik EN"]);
class Chapters {
constructor(){
this.data = [];
return this;
}
generate(exportURLs, videoURL) {
const htmlCol = document.getElementsByClassName("chapterTimestamp");
for (let i=0;i<htmlCol.length;i++) {
const url = exportURLs ? this.getChapterURL(
htmlCol[i], videoURL, i) : "";
const desc = this.getDescription(htmlCol[i]);
if (exportURLs) {
this.data[i] = desc + " " + url
} else {
this.data[i] = desc
}
}
}
getChapterURL(htmlDiv, url, pos) {
const i = pos + 1;
const ts = this.getTimestamp(htmlDiv.outerText
.removewhitespaces()
.split(":").reverse());
return "["+i+"]("+url+"?t="+ts+")"
}
getDescription(htmlHandle) {
const sib = htmlHandle.nextElementSibling;
return sib.outerText.removewhitespaces()
}
getTimestamp(array) {
if (!(array instanceof Array)) {
throw new TypeError('Argument must be an instance of Array');
}
if (array.length > 2) {
throw new Error('Days are unsupported');
}
let seconds = 0;
for (let i=0;i<array.length;i++) {
const val = parseInt(array[i]);
if (i == 0) {
seconds += val;
} else if (i == 1) {
seconds += val * 60;
} else {
seconds += val * 60 * 60;
}
}
return seconds;
}
}
function getElement (className) {
return document.getElementsByClassName(className)[0].
textContent;
}
function getYoutubeTitle(channel) {
title = getElement("videoTitle");
trimText = TitlesToTrim.get(channel);
if (trimText !== undefined) {
trimText.forEach((text) => {
text = new RegExp(text.escape(), 'i');
title = title.replace(text, '');
});
}
return title.removewhitespaces().uncapitalize();
}
function defaultValue(value, defaultValue) {
return value === undefined ? defaultValue : value;
}
function getYoutubeChannel() {
const str = getElement("channelName").removewhitespaces();
const newChannelName = NamesToConvert.get(str)
return defaultValue(newChannelName, str)
}
function getYouTubeURL() {
// eg: file:///app/freetube/resources/app.asar/dist/index.html#/watch/XXXXXXXXXXX
const freeURL = document.URL.split("/").pop();
return "https:youtu.be/" + freeURL
}
function getPublishDate() {
const text = getElement("datePublishedAndViewCount").
replace('Published on ', '');
const date = text.slice(0, text.indexOf('•') - 1).removewhitespaces();
return new Date(date).toLocaleDateString();
}
function formatOutput(title, currentURL, channel, date, chapters) {
let text = "- " + title + " - [" + date + "](" + currentURL + ") - [[" + channel + "]]";
for (let i=0;i<chapters.data.length;i++) {
text += "\n\t- " + chapters.data[i];
}
return text;
}
async function exportToClipboard(output) {
await clipboard.writeText(output);
}
function exportYoutubeMetadata(chapters, chapterURLs) {
const channel = getYoutubeChannel();
const title = getYoutubeTitle(channel);
const currentURL = getYouTubeURL();
const publishdate = getPublishDate();
const chapter = new Chapters();
if (chapters == true)
chapter.generate(chapterURLs, currentURL);
const output = formatOutput(title, currentURL, channel, publishdate, chapter);
exportToClipboard(output);
show(output);
show(currentURL);
}
String.prototype.removewhitespaces = function() {
// show(this.match("/\xA0/g"));
// show(this.match("/\n/g"));
// show(this.match("/\u202C/g"));
return this.replace(/\n/g, '').
replace(/\xA0/g, " ").// \xA0 is ASCI 160 - no breaking space
replace(/\u202C/g, "").
trim();
}
String.prototype.uncapitalize = function() {
return this.split(' ')
.map(word => {
if (word.isCapitalized()) {
return word.slice(0, 1).toUpperCase() + word.slice(1).toLowerCase();
} else {
return word;
}
})
.join(' ');
};
String.prototype.isCapitalized = function () {
for (var c of this) {
if (c !== c.toUpperCase()) {
return false;
}
}
return true;
}
String.prototype.escape = function () {
return this.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}
function show(str) {
console.log(str)
}
exportYoutubeMetadata(true, false);
I have added two functions to this script.
- Now
exportYoutubeMetadata(exportChapters?, exportTimestamps?)
take two booleans as arguments. First determines if video chapters will be exported, second will determine if exported chapters has generated timestamps. Default script behaviour is generate chapters without timestamps.
- It will automatically uncapitalize video titles. You can disable it by removing call
.uncapitalize()
from getYoutubeTitle()
function.
Examples
Without timestamps:
- Something Is Causing Stars To Escape Super Fast From the Nearby Dwarf Galaxy - [21/02/2025](https:youtu.be/HjGOTBDLZYk) - [[Anton Petrov]]
- Hypervelocity stars from Large Magellanic Cloud
- What are hypervelocity stars and how do they form?
- Studies on hypervelocity stars in the Milky Way and the halo
- New study makes a surprising discovery
- What's forming them? Potential explanations
- Implications and conclusions
With timestamps:
- Something Is Causing Stars To Escape Super Fast From the Nearby Dwarf Galaxy - [21/02/2025](https:youtu.be/HjGOTBDLZYk) - [[Anton Petrov]]
- Hypervelocity stars from Large Magellanic Cloud [1](https:youtu.be/HjGOTBDLZYk?t=0)
- What are hypervelocity stars and how do they form? [2](https:youtu.be/HjGOTBDLZYk?t=32)
- Studies on hypervelocity stars in the Milky Way and the halo [3](https:youtu.be/HjGOTBDLZYk?t=305)
- New study makes a surprising discovery [4](https:youtu.be/HjGOTBDLZYk?t=365)
- What's forming them? Potential explanations [5](https:youtu.be/HjGOTBDLZYk?t=445)
- Implications and conclusions [6](https:youtu.be/HjGOTBDLZYk?t=480)