- Introduction
- Prerequisites
- Setup for local development
- Inner Workings of this template
- Terminal commands
- License
BetterYTM is a UserScript that enhances the YouTube and YouTube Music experience by adding features and fixing bugs. It has a plugin system that allows you to create your own plugins to further customize the experience and make use of its API.
This template is perfect for anyone who wants to create a plugin for BetterYTM but doesn't want to deal with the hassle of setting up a working development environment.
BetterYTM (BYTM for short) is included as a Git submodule to ensure that both projects' versions stay compatible until you decide to update your plugin.
This also conveniently allows you to inspect BetterYTM's code for finding specific details about the API and even more conveniently, allows you to navigate through to the BYTM API by ctrl+clicking on imported members.
Just make sure to read the plugin sublicense to find out which parts you are allowed to include in your plugin's bundle in dist/
.
If you only import with the type
keyword (or without, when importing enum
s), you should be fine.
The plugin will be built with Vite and pnpm and written in TypeScript to provide a modern and lightning fast development experience.
You may also import plain JavaScript files (with the extension .mjs) if you prefer that instead.
It is also set up to be easily hosted on a local server for testing and to be built for production with a single command.
If you use a UserScript manager extension such as Violentmonkey, you can easily test the plugin by opening the local server URL in your browser.
The extension will keep updating the userscript automatically when any changes are made, as long as you clicked the "track external edits" button.
Configure this behavior in the nodemonConfig
object in package.json
and src/tools/serve.ts
.
The library UserUtils is also included to provide a plethora of useful functions and classes for UserScripts.
I highly recommend checking it out! It is included on the BYTM API via unsafeWindow.BYTM.UserUtils
.
Note
If your plugin is published, send me a quick E-Mail or message on Discord so I can add it to the BetterYTM Plugin List.
You can also contact me for questions and help or to request features to be exposed on the API.
Have fun creating your plugin!
- Reading the BetterYTM Contributing Guide (or the latest in-dev version here).
It contains all the information you need to know about the BYTM API and how to create a plugin. - Having basic knowledge of writing UserScripts with JavaScript and ideally also having basic TypeScript knowledge.
- Installing a powerful IDE like VS Code to get extension recommendations, be able to inspect TS types and BYTM-internal code and to get auto-completion for the members of the BYTM API.
- Reading this whole document to understand how to set up and use this template correctly.
- Reading the BetterYTM plugin sublicense
- Install Node.js (current version or LTS) and pnpm (can be done with
npm i -g pnpm
) - Create a repository based on this template.
- Clone the repository to your local machine.
- Use
git submodule update --init --recursive
to clone the BetterYTM submodule. - Copy
.env.template
to.env
and modify it to your needs. - Install BetterYTM from the releases page.
If you wanna prepare your code for the latest version that's still in development, check out the latest pull request for the download and changelog. - Open a terminal in the project root and run
pnpm i
to install dependencies. - Run
pnpm run dev
to build the plugin and host it on a local server for testing.
Open this URL with your UserScript manager extension to easily test the plugin.
I recommend using Violentmonkey, which will automatically update the userscript when any changes are made.
Refer to the commands section for more information on the available commands.
- The root folder of the project (import prefix:
@root/
) contains the following:.env.template
is an example file for an environment configuration.
Copy the file to.env
and modify it to your needs to change the behavior of the build process..gitmodules
contains the submodule configuration, where the BetterYTM repository is linked. Only modify this via terminal commands.changelog.md
documents all changes between your plugin's versions. It is recommended to keep this up to date.eslint.config.mjs
contains the ESLint configuration in the new v9 format. Feel free to modify this to your liking.
The default settings include 2-space indentation, double quotes, trailing commas, and more.
Things to look out for:- The rule
@typescript-eslint/no-empty-object-type
is turned off, which means you can use the type{}
, but be careful since this doesn't mean "empty object" and is a common pitfall - Unused function arguments will yield a warning, unless they start with an underscore
- The rule
package.json
is the single source of truth for lots of your plugin's metadata, like the name, version, description, etc.
Make sure to update this file to match your plugin's details.tsconfig.json
contains the TypeScript configuration. Feel free to modify this to your needs.vite.config.ts
contains the Vite build configuration. This is where your userscript metadata and some default values are defined.
It is also where resources are parsed, the SRI hash is calculated and where you can add your own tweaks to the build process.
- The
src/
folder contains the source code of the plugin (import prefix:@/
).index.ts
is the main file that will be compiled into the userscript. In there, hook all the functions you want to run when the plugin is loaded.types.ts
makes sure that the BYTM API is available in your code by providing its global types.
If you need more global events, you will need to manually enter them in theinterface WindowEventMap
by following the format of the other entries and cross-referencing the BYTM API documentation.example/
contains example code to show you how to interact with the BYTM API.utils/
contains utility functions for better organization of your code (import prefix:@utils/
).constants.ts
contains constants that are used throughout the plugin.
The example values that are in there (build mode and number) are inserted by the custom vite plugin invite.config.ts
.
You can add your own constants in there and use them throughout your code as a single source of truth.logging.ts
has shorthand functions for logging stuff to the console. This has the benefit of adding a common prefix to all log messages and in here you can hook your own functions to improve the logging system.plugin.ts
contains the plugin definition object, the plugin registration logic and constants exposed by the registration (token and event emitter instance).
- The
assets/
folder contains all the assets that are used in the plugin, think image, audio, video files, HTML, CSS, markdown, whatever.
These assets can be linked to a@resource
directive by editing thevite.config.ts
file, where you can then useGM.getResourceUrl()
to get the URL of the asset, which you can thenfetch()
or point to in an img, video, audio, etc. tag.resources.json
is where you define the@resource
directives for your plugin, which can then be fetched withGM.getResourceUrl()
andGM.getResourceText()
.
The keys of this object are the identifiers you use to fetch the resources and the values are the paths to the resources, or an options object.
If a string path is given and it starts with a slash, it will be resolved relative to the root of the project, otherwise relative to theassets/
folder.
If an object is given, it has to have the keyspath
(follows the same logic as above) and an optionalintegrity
key, which will by default automatically calculate the SRI hash for the asset and append it to the URL in the metadata block, unless explicitly set tofalse
.
If you include files that can change outside your influence like libraries, make sure you use a CDN with versioned URLs, so the file doesn't change (because the hash will only be calculated once at build time). An example of this can be found in theresources.json
file.
- The
bytm
folder contains BetterYTM's entire repository as a submodule (import prefix:@bytm/
).
The branch of this submodule dictates which version of BetterYTM your plugin is compatible with.
main
is the latest release version,develop
is the latest in-dev version.
I recommend you read up on Git submodules to understand how they work and how to update them.
This folder is also where you can find the BYTM API documentation and inspect the code to find out how to use the API.
You can also quickly navigate through the BYTM API's internal code via ctrl+clicking. - In the
dist/
folder, the final build of your userscript will be created by vite.
This is the file you will want to publish on platforms like GitHub, GreasyFork, OpenUserJS or your own website.
- The TS file imports you encounter will end in
.js
. This is deliberate, because it is the latest ES module format. Just think of it as if you are trying to import the file that will exist after TypeScript has compiled it.
If you are using VS Code, the IntelliSense imports will automatically follow this format (configured in.vscode/settings.json
).- All file imports use prefixed paths (starting with
@
), which are defined in thetsconfig.json
file. - JSON imports also have the extended syntax
with { type: "json" }
, which too is the latest ES module standard.
- All file imports use prefixed paths (starting with
- You need to publish your userscript on at least one (but ideally as many as possible) of the following platforms:
- GitHub
- GreasyFork
- OpenUserJS
- Your own website
- Subresource Integrity for
@resource
directives is supported out of the box by this template.
This is to combat the risk of your externally loaded in assets being tampered with by a third party, reducing the possibility of MITM and XSS-type attacks.
By default, each asset'sintegrity
property istrue
inassets/resources.json
, making the plugin automatically calculate the SRI hash and add it to the asset's URL.
Note that this means you will have to rebuild the plugin every time you change an asset that has SRI enabled.
If you mess up the strict build and commit order, the hash will be wrong and people will not be able to install your userscript. - Make sure to retain the notice at the bottom of this file that your plugin contains code from BetterYTM in the readme and/or in a
console.log()
that is called on each page load. - If you're using VS Code, for showing linter errors and to get automatic code formatting you can install the extension
dbaeumer.vscode-eslint
.
Then you can add the following to yourUser Settings (JSON)
or the.vscode/settings.json
file to automatically format your code on manual saves:Alternatively if you want a manual keybind for this, press F1, enter"editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit", },
Preferences: Open Keyboard Shortcuts
, search for@command:eslint.executeAutofix
and bind it to a key of your choice.
pnpm run dev
- Builds the plugin using thebuild-dev
command and hosts it on a local server for testing using theserve
command on default settings (or whatever is set in.env
).
This will also watch for changes and automatically rebuild the plugin, so the browser extension may automatically refresh it too.
The default URL ishttp://localhost:8767/betterytm-plugin-template.user.js
(file name is created fromuserscriptName
inpackage.json
).pnpm run build
- Builds the plugin for production into thedist
folder.
This should be committed for easy inspection and universal installation. This then also allows you to easily permalink to every version's code for users to install.pnpm run build-dev
- Builds the plugin for development into thedist
folder.
By default this only changes where assets are served from, but you can add your own tweaks invite.config.ts
.pnpm run serve
- Serves a few folders includingdist
andassets
on a locally hosted HTTP server.
This is useful for development and testing purposes.
Use--port=n
to specify a different port (8767 by default) and--auto-exit-time=n
to auto-shutdown the server after n seconds.pnpm run lint
- Lints the code with ESLint.
Feel free to modify the config ateslint.config.mjs
to your liking.pnpm run format
- Formats all auto-fixable problems in the code with ESLint, according to the config.pnpm run node-ts path/to/file.ts
- Runs the given TS file using the normal Node.js binary and the ts-node ESM loader. This is basically like runningnode file.js
but with TypeScript & ESM and it allows you to use arguments intrinsic to Node.
This project (minus Git submodules) is licensed under the WTFPL - do whatever you want with it.
It is based on BetterYTM, which itself is licensed under the AGPL-3.0 license.
Make sure to include this in your plugin's readme to comply with BetterYTM's plugin sublicense.