SynQApp / Extension

Your music companion for the web, with a portable mini player and the ability to listen to any music link on your preferred service!
https://www.synqapp.io
Apache License 2.0
26 stars 5 forks source link

Project structure and stratgies/implementations for Apple, Amazon, and YouTube Music #1

Closed tekkeon closed 1 year ago

tekkeon commented 1 year ago

Overview

This is admittedly quite a large PR, so brace yourselves. From a top level, the changes are:

  1. Setup the project structure for the extension
  2. Figured out how to get access to existing window and element methods with the relay message script and utils (more below)
  3. Define the Controller interface, a generic interface that each music service needs an implementation for.
  4. Implemented majority of interaction controls in the Controller interface for YouTube Music, Amazon Music, and Apple Music. Was waiting for Aren for Spotify though that should be ready to work on now.
  5. Created some general utils

Explanations

Message Relay

Chrome extension content scripts get injected into the specified page and run alongside all of the other JS on the page. However, the default behavior is for content scripts to run in an isolated "world", meaning they do not have access to any other JS objects living on the page, including those attached to the window, document, or DOM elements. To enable access to those objects, we must run our content script in the "main world", however doing so means we do not have access to the chrome APIs. To bridge the gap, the message relay script relays messages between the main world and the Chrome APIs.

Background Fetch

I created this util thinking I would need it for the Amazon Music Controller, but I ended up finding a different method. However, this is still a good generic utility to have available to us. Basically, in order to get around CORS or Content Security Policy errors, we can execute fetch calls in the background script. This utility is a fetch API compliant util for MAIN world content scripts to execute fetch calls in the background script.

YouTube Music Implementation

YouTube Music has a navigate function available to us which we can use to start a specific track programmatically. However, it expects the object passed in to be of a specific class instance. We have no direct access to the class definition. To get around this, I wrapped the navigate function so that we capture an instance of the class instance which we can then clone to then use from our script. If the user starts a song, we capture the instance. If the user does not start a song by the time we need to programmatically control it (when a session is active) we navigate to the explore page, disable the navigate function being able to start songs, and simulate clicking on a song which allows us to capture an instance of the object.

Amazon Music Implementation

Amazon Music uses Redux as its state management solution. They have set it up to work with the official Redux Chrome extension. This gives us an entry to grab the store for ourselves, which we can then use to dispatch actions. Doing it this way ensures the state of the Amazon Music application doesn't end up in an error state, as other options I was trying were frequently showing how brittle the Amazon Music application is to programmatic attempts to control it. This option has proven to be fairly reliable so far. The reason there is a separate script for setting up the Redux store logic is the difference in the run_at config. We need this script to run before the main Amazon Music scripts run because the Amazon Music scripts will check if our function is available on the window object.