Skip to content

Commit

Permalink
Change from Ultimate Guitar integration to Freetar (#2)
Browse files Browse the repository at this point in the history
* first iteration

* new logic, refactor

* remove package, update readme

* add release workflow

* refactor for freetar site

* fix copy to clipboard from popup

* further documentation
  • Loading branch information
asanvicentec authored Jun 27, 2024
1 parent 54ad0ef commit 0360578
Show file tree
Hide file tree
Showing 12 changed files with 730 additions and 859 deletions.
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# UltimateNotationConvert
extension to convert from Letter notation to Solfège notation for ultimate-guitar website
# UltimateNotation
Convert notes to Solfège representation on [Freetar](https://freetar.de/) site. This extension was originally meant to be used on the original Ultimate Guitar, but HTML is way harder to edit there, also Freetar removes a lot of UG bloat.

Right now this project acts as a demonstration of integrating Parcel bundler in a Chrome extension and develop using TypeScript,
as the script's logic is incomplete, and there are some issues involving clipboard API.
## Disclaimer
This extension has been tested, but it may not cover all use cases, if you happen to use it and find an error, please let me now.

# For developers
When viewing changes on-the-fly when developing, you can run `npm run start` and load the uncompressed extension
by pointing to the dist folder in `chrome://extensions/`
1,394 changes: 644 additions & 750 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@
},
"keywords": [
"notation",
"music"
"music",
"notes",
"instrument"
],
"author": "asanvicentec",
"license": "ISC",
"devDependencies": {
"@parcel/config-webextension": "^2.11.0",
"@types/chrome": "^0.0.260",
"parcel": "^2.11.0",
"@parcel/config-webextension": "^2.12.0",
"@types/chrome": "^0.0.268",
"parcel": "^2.12.0",
"ts-loader": "^9.5.1",
"typescript": "^5.3.3"
"typescript": "^5.5.2"
}
}
32 changes: 32 additions & 0 deletions src/assets/styles/content.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* Experiment to to give a visual aid and to help cases where two notes are too close togheter after conversion (nasty workaround)
To inject the CSS, add the following to the content script object matching freetar site:
"css": ["assets/styles/content.css"]
*/

[data-original="C"] {
color: rgba(235, 35, 15, 1);
}

[data-original="D"] {
color: rgba(252, 146, 8, 1);
}

[data-original="E"] {
color: rgba(254, 251, 1, 1);
}

[data-original="F"] {
color: rgba(2, 141, 2, 1);
}

[data-original="G"] {
color: rgba(115, 212, 254, 1);
}

[data-original="A"] {
color: rgba(7, 24, 147, 1);
}

[data-original="B"] {
color: rgba(82, 29, 144, 1);
}
File renamed without changes.
11 changes: 3 additions & 8 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,12 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ultimate Notation</title>
<link rel="stylesheet" href="assets/styles/main.css">
<link rel="stylesheet" href="assets/styles/popup.css">
</head>
<body>
<h1>Ultimate Notation</h1>
<script type="module" src="popup.ts"></script>
<span id="infoLabel" data-tooltip="Due to unexpected behaviour with Clipboard API,
songs will automatically be copied when website starts.
Solfege conversion is experimental, it will only work fine in songs with basic chords">Info</div>
</span>
<!--<button id="copyBasicBtn">Copy</button>
<button id="copyAdvancedBtn">Copy - Experimental</button>
<span id="result"></span>-->
<button id="copyToClipboardBtn">Copy</button>
<span id="result"></span>
</body>
</html>
2 changes: 1 addition & 1 deletion src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
{
"js": ["scripts/content.ts"],
"matches": [
"https://tabs.ultimate-guitar.com/tab/*"
"https://freetar.de/tab/*"
]
}
]
Expand Down
26 changes: 16 additions & 10 deletions src/popup.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { CopyType } from "./scripts/enum";
import { copyTextToClipboard } from "./scripts/utils";

document.getElementById("copyBasicBtn")!.addEventListener("click", () => copySong(CopyType.basic));
document.getElementById("copyAdvancedBtn")!.addEventListener("click", () => copySong(CopyType.advanced));
document.getElementById("copyToClipboardBtn")!.addEventListener("click", () => copySong());
const resNode = document.querySelector("#result");

async function copySong(btnAction: CopyType) {
const getContent = () => {
return document.querySelector('.tab').innerText;
}

const [tab] = await chrome.tabs.query({active: true, lastFocusedWindow: true});

await chrome.tabs.update(tab.id, { active: true });
await chrome.windows.update(tab.windowId, { focused: true });

const response = await chrome.tabs.sendMessage(tab.id, {copySong: btnAction});
async function copySong() {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
chrome.scripting.executeScript({
target: { tabId: tabs[0].id },
func: getContent
}, (result) => {
copyTextToClipboard(result[0].result);
resNode!.innerHTML = "Copied successfully!"
});
});
}
91 changes: 17 additions & 74 deletions src/scripts/content.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
import { copyTextToClipboard } from "./utils";
import { CopyType } from "./enum";



const solfegeDictionary = {
c: "DO",
d: "RE",
Expand All @@ -12,82 +7,30 @@ const solfegeDictionary = {
a: "LA",
b: "SI",
};
const solfegeList: string[] = ["DO", "RE", "MI", "FA", "SOL", "LA", "SI"];

const article: HTMLElement | null = document.querySelector('code');
// Cloning node so we don't mess with the dom

const articleClone: HTMLElement | undefined = article?.cloneNode(true) as HTMLElement;
// `document.querySelector` may return null if the selector doesn't match anything.

if (articleClone) {

const song: HTMLElement | null = articleClone.querySelector('pre');
// Each song line includes it's own chords
let songLines: HTMLCollection = song!.children;

for (let i = 0; i < songLines.length; i++) {
const scores: string[] = <string[]>Array.prototype.map.call(songLines[i].querySelectorAll('[data-name]'), function (tag: HTMLElement) { return tag.textContent; });
const tab: HTMLElement | null = document.querySelector('.tab');

scores.forEach((e: string) => {
if (tab) {
tab.childNodes.forEach((item, index) => {
if (item.nodeName.toLowerCase() === "span") {

const score: string = e.substring(0, 1);
const note = item.firstChild!.textContent;
let spacesToRemove = item.firstChild!.textContent.substring(1).length;

// If for whatever reason both conditions fail we set type to undefined
let regex: RegExp | undefined = undefined;
// Conversion
item.firstChild!.textContent = solfegeDictionary[note[0].toLowerCase()] + item.firstChild!.textContent.substring(1)

/* Not feeling like doing solfegeDictionary[score.toLowerCase() as keyof typeof solfegeDictionary],
there has to be a more elegant solution */
const solfegeRepresentation: string = solfegeDictionary[score.toLowerCase()];

// Any note that is not SOL
if (solfegeRepresentation.length == 2) {
regex = new RegExp(score + '\\w*([\r| ]{1}|$)', 'gmi');
}
// SOL Case
else if (solfegeRepresentation.length == 3) {
regex = new RegExp(score + '\\w*([\r| ]{2}|$)', 'gmi');
if (item.lastChild!.classList.contains("chord-bass")) {
item.lastChild!.textContent = solfegeDictionary[item.lastChild!.textContent.toLowerCase()] + item.lastChild!.textContent.substring(1)
spacesToRemove += -Math.abs(item.lastChild!.textContent!.length) + 1;
}

// Grab original score and replace it with Solfege representation
let solfege: string = e.replace(score, solfegeRepresentation);
// Remove spaces
if (tab.childNodes[index + 1].nodeName === "#text") {

let inlineChords = songLines[i].children[0].textContent;

if (inlineChords && regex) {
//If original score is actually a Solfege, the score has already been replaced, leave as is, otherwise replace it
songLines[i].children[0].textContent = inlineChords.replaceAll(regex, (originalScore: string) =>
solfegeList.includes(originalScore.replace(/\s+/g, '')) ? originalScore : solfege);
spacesToRemove += -Math.abs(item.firstChild!.textContent!.length) + 1;
tab.childNodes[index + 1].textContent = tab.childNodes[index + 1].textContent!.slice(0, spacesToRemove);
}

});
}
copyTextToClipboard(song.innerText)


chrome.runtime.onMessage.addListener(async function (request, sender, sendResponse) {
switch (request.copySong) {
case CopyType.basic:
transposeBasic(song!);
break;
case CopyType.advanced:
// transposeAdvanced();
break;
default:
sendResponse({ response: "Incorrect copy type!"});

}
articleClone?.focus()

song === null ? console.log("No song found") : await copyTextToClipboard(song.innerText);
});
}

async function transposeBasic(song: HTMLElement) {
await song.querySelectorAll('[data-name]').forEach(e => {
if (e.textContent !== null) {
const score = e.textContent.substring(0, 1)
e.textContent = e.textContent.replace(score, solfegeDictionary[score.toLowerCase()])
}
});
}
})
}
4 changes: 0 additions & 4 deletions src/scripts/enum.ts

This file was deleted.

3 changes: 1 addition & 2 deletions src/scripts/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
//#region Copy to clipboard
// Helped by: https://stackoverflow.com/a/30810322
export async function copyTextToClipboard(text: string) {
await navigator.clipboard.writeText(text).then(function () {
console.log("Copying to clipboard was successful!");
console.log("Copied successfully!");
}, function (err) {
console.error("Could not copy text: ", err);
});
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"skipLibCheck": true,
"noEmit": true,
"paths": {
"@/*": ["./src/*", "./dist/*"]
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
Expand Down

0 comments on commit 0360578

Please sign in to comment.