Compare commits
2 Commits
b44c6bd073
...
eb3e73f690
Author | SHA1 | Date |
---|---|---|
Violet Millie | eb3e73f690 | |
Violet Millie | 872b7f2c2a |
|
@ -0,0 +1,30 @@
|
|||
import fs from "fs";
|
||||
|
||||
const outDir = "dist";
|
||||
|
||||
// Removes old files
|
||||
fs.rmdirSync("dist", { recursive: true });
|
||||
|
||||
await Bun.build({
|
||||
entrypoints: [
|
||||
"src/extension/typescript/content.ts",
|
||||
"src/extension/typescript/background.ts",
|
||||
],
|
||||
outdir: outDir,
|
||||
});
|
||||
|
||||
const glob = new Bun.Glob("!.ts");
|
||||
|
||||
const extensionPath = "src/extension";
|
||||
|
||||
// TODO: Update script to preserve folders (not necessary for this extension, as of now)
|
||||
// TODO: Setup directory watch (bun build --watch works, but only for TS files)
|
||||
|
||||
for await (const node of glob.scan(extensionPath)) {
|
||||
const file = Bun.file(`${extensionPath}/${node}`);
|
||||
|
||||
// console.log(`${outDir}/${node}`);
|
||||
|
||||
Bun.write(`${outDir}/${node}`, file);
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "Spotify Web Notifications",
|
||||
"description": "Spotify Web Notifications",
|
||||
"version": "0.1",
|
||||
"homepage_url": "https://code.violetmillie.me/Millie/spotify-web-notifications",
|
||||
|
||||
"manifest_version": 2,
|
||||
|
||||
"permissions": ["notifications"],
|
||||
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "spotifywebextension@violet.millie.me",
|
||||
"strict_min_version": "42.0"
|
||||
}
|
||||
},
|
||||
|
||||
"background": {
|
||||
"scripts": ["background.js"]
|
||||
},
|
||||
|
||||
"content_scripts": [
|
||||
{
|
||||
"exclude_matches": ["*://developer.mozilla.org/*"],
|
||||
"matches": ["*://open.spotify.com/*"],
|
||||
"js": ["content.js"]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
function onRuntimeMessage(message: any) {}
|
||||
|
||||
browser.runtime.onMessage.addListener(onRuntimeMessage);
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import SpotifyAPI from "./modules/SpotifyAPI";
|
||||
|
||||
const API = new SpotifyAPI();
|
||||
|
||||
// use the API
|
||||
|
||||
API.getCurrentTrack();
|
|
@ -0,0 +1,70 @@
|
|||
import SpotifyScraper from "./SpotifyScraper";
|
||||
|
||||
export type Artist = {
|
||||
name: string;
|
||||
URL: string;
|
||||
};
|
||||
|
||||
export type Track = {
|
||||
track: {
|
||||
title: string;
|
||||
URL: string;
|
||||
coverArt: string;
|
||||
|
||||
artist: Artist | Artist[];
|
||||
};
|
||||
|
||||
playing: boolean;
|
||||
};
|
||||
|
||||
export type Playstate = {
|
||||
track: Track;
|
||||
|
||||
isPlaying: boolean;
|
||||
};
|
||||
|
||||
export default class SpotifyAPI {
|
||||
currentPlaystate = {};
|
||||
private scraper: SpotifyScraper;
|
||||
|
||||
constructor() {
|
||||
this.scraper = new SpotifyScraper();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the full URL from a relative Spotify URL
|
||||
* @example `/album/` -> `https://open.spotify.com/album/`
|
||||
*/
|
||||
private generateFullURL(relativeURL: string) {
|
||||
return `${window.location.host}${relativeURL}`;
|
||||
}
|
||||
|
||||
getCurrentTrack() {
|
||||
const titleElement = this.scraper.getBarElement("title")!;
|
||||
const trackURL = `${titleElement.getAttribute("href")}`;
|
||||
|
||||
const artistElement = this.scraper.getBarElement("artist")!;
|
||||
const artistName = artistElement.textContent;
|
||||
const artistURL = artistElement.getAttribute("href");
|
||||
|
||||
// console.log(trackElement.textContent);
|
||||
|
||||
console.log(
|
||||
`Now playing: ${
|
||||
titleElement.textContent
|
||||
} by ${artistName} (${this.generateFullURL(trackURL)})`
|
||||
);
|
||||
}
|
||||
|
||||
getPlaystate() {}
|
||||
|
||||
// TODO: Implement events: onPlay, onPause (?), trackChanged, playstateChanged
|
||||
|
||||
// trackChanged: fire on track changes
|
||||
// onPlay: fired when playback resumes
|
||||
|
||||
// onPause: fired when playback is paused
|
||||
|
||||
// playStateChagned: fired whenever the playstate changes (any of the above)
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
type BarElement = {
|
||||
name: string;
|
||||
selector: string;
|
||||
element?: HTMLElement | null | undefined;
|
||||
|
||||
// isPersisent?: boolean;
|
||||
};
|
||||
|
||||
export default class SpotifyScraper {
|
||||
constructor() {}
|
||||
|
||||
barElements: BarElement[] = [
|
||||
{
|
||||
name: "playbackControlButton",
|
||||
selector: "button[data-testid=control-button-playpause]",
|
||||
},
|
||||
{
|
||||
name: "title",
|
||||
selector: "a[data-testid=context-item-link]",
|
||||
},
|
||||
{
|
||||
name: "artist",
|
||||
selector: "a[data-testid=context-item-info-artist]",
|
||||
},
|
||||
{
|
||||
name: "coverArtImage",
|
||||
selector: "img[data-testid=cover-art-image]",
|
||||
},
|
||||
];
|
||||
|
||||
getBarElement(name: string) {
|
||||
const barElementData = this.barElements.find(
|
||||
(element) => element.name === name
|
||||
);
|
||||
|
||||
if (!barElementData) {
|
||||
// Raise some error
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return document.querySelector(barElementData.selector);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["ESNext"],
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
|
@ -20,3 +20,4 @@
|
|||
"forceConsistentCasingInFileNames": true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue