add files
This commit is contained in:
parent
6d79dafadb
commit
1dd7e97f94
15 changed files with 229 additions and 33 deletions
BIN
.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite
vendored
Normal file
BIN
.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite
vendored
Normal file
Binary file not shown.
BIN
.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-shm
vendored
Normal file
BIN
.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-shm
vendored
Normal file
Binary file not shown.
BIN
.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-wal
vendored
Normal file
BIN
.wrangler/state/v3/cache/miniflare-CacheObject/metadata.sqlite-wal
vendored
Normal file
Binary file not shown.
BIN
extension/_metadata/generated_indexed_rulesets/_ruleset1
Normal file
BIN
extension/_metadata/generated_indexed_rulesets/_ruleset1
Normal file
Binary file not shown.
4
extension/content.js
Normal file
4
extension/content.js
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
const s = document.createElement('script');
|
||||||
|
s.src = chrome.runtime.getURL('inject.js');
|
||||||
|
(document.head || document.documentElement).appendChild(s);
|
||||||
|
s.remove();
|
||||||
BIN
extension/icons/128.png
Normal file
BIN
extension/icons/128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
BIN
extension/icons/16.png
Normal file
BIN
extension/icons/16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
extension/icons/48.png
Normal file
BIN
extension/icons/48.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
1
extension/inject.js
Normal file
1
extension/inject.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
window.__tidalOriginExtension = true;
|
||||||
47
extension/manifest.json
Normal file
47
extension/manifest.json
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"manifest_version": 3,
|
||||||
|
"name": "Monochrome Tidal Origin",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Adds Origin: https://listen.tidal.com to Tidal CDN requests so audio plays without a proxy",
|
||||||
|
"permissions": ["declarativeNetRequest", "scripting", "declarativeNetRequestWithHostAccess"],
|
||||||
|
"host_permissions": [
|
||||||
|
"*://*.tidal.com/*",
|
||||||
|
"*://monochrome.tf/*",
|
||||||
|
"*://monochrome.samidy.com/*",
|
||||||
|
"*://lossless.wtf/*",
|
||||||
|
"*://localhost:*/*"
|
||||||
|
],
|
||||||
|
"declarative_net_request": {
|
||||||
|
"rule_resources": [
|
||||||
|
{
|
||||||
|
"id": "rules",
|
||||||
|
"enabled": true,
|
||||||
|
"path": "rules.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"content_scripts": [
|
||||||
|
{
|
||||||
|
"matches": [
|
||||||
|
"*://monochrome.samidy.com/*",
|
||||||
|
"*://monochrome.tf/*",
|
||||||
|
"*://lossless.wtf/*",
|
||||||
|
"*://localhost:*/*"
|
||||||
|
],
|
||||||
|
"js": ["content.js"],
|
||||||
|
"run_at": "document_start",
|
||||||
|
"all_frames": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"icons": {
|
||||||
|
"16": "icons/16.png",
|
||||||
|
"48": "icons/48.png",
|
||||||
|
"128": "icons/128.png"
|
||||||
|
},
|
||||||
|
"web_accessible_resources": [
|
||||||
|
{
|
||||||
|
"resources": ["inject.js"],
|
||||||
|
"matches": ["*://monochrome.samidy.com/*", "*://monochrome.tf/*", "*://lossless.wtf/*", "*://localhost:*/*"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
55
extension/rules.json
Normal file
55
extension/rules.json
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"priority": 1,
|
||||||
|
"action": {
|
||||||
|
"type": "modifyHeaders",
|
||||||
|
"responseHeaders": [
|
||||||
|
{
|
||||||
|
"header": "Access-Control-Allow-Origin",
|
||||||
|
"operation": "set",
|
||||||
|
"value": "*"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": "Access-Control-Allow-Methods",
|
||||||
|
"operation": "set",
|
||||||
|
"value": "GET, POST, PUT, DELETE, PATCH, OPTIONS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": "Access-Control-Allow-Headers",
|
||||||
|
"operation": "set",
|
||||||
|
"value": "*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"condition": {
|
||||||
|
"urlFilter": "||tidal.com*",
|
||||||
|
"initiatorDomains": ["monochrome.tf", "monochrome.samidy.com"],
|
||||||
|
"resourceTypes": ["xmlhttprequest", "media"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"priority": 1,
|
||||||
|
"action": {
|
||||||
|
"type": "modifyHeaders",
|
||||||
|
"requestHeaders": [
|
||||||
|
{
|
||||||
|
"header": "Origin",
|
||||||
|
"operation": "set",
|
||||||
|
"value": "https://listen.tidal.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"header": "Referer",
|
||||||
|
"operation": "set",
|
||||||
|
"value": "https://listen.tidal.com/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"condition": {
|
||||||
|
"urlFilter": "||tidal.com*",
|
||||||
|
"initiatorDomains": ["monochrome.tf", "monochrome.samidy.com"],
|
||||||
|
"resourceTypes": ["xmlhttprequest", "media"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
65
functions/proxy-audio.js
Normal file
65
functions/proxy-audio.js
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
export async function onRequest(context) {
|
||||||
|
const { request } = context;
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const targetUrl = url.searchParams.get('url');
|
||||||
|
|
||||||
|
if (!targetUrl) {
|
||||||
|
return new Response('Missing url parameter', { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const cacheUrl = new URL(request.url);
|
||||||
|
try {
|
||||||
|
const tidalUrl = new URL(targetUrl);
|
||||||
|
cacheUrl.searchParams.set('cache_key', tidalUrl.pathname);
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
const cacheKey = new Request(cacheUrl.toString(), request);
|
||||||
|
const cache = caches.default;
|
||||||
|
let response = await cache.match(cacheKey);
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
const headers = new Headers(request.headers);
|
||||||
|
headers.delete('host');
|
||||||
|
headers.delete('referer');
|
||||||
|
headers.set('Origin', 'https://listen.tidal.com');
|
||||||
|
headers.set(
|
||||||
|
'User-Agent',
|
||||||
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
||||||
|
);
|
||||||
|
|
||||||
|
response = await fetch(targetUrl, {
|
||||||
|
method: request.method,
|
||||||
|
headers: headers,
|
||||||
|
redirect: 'follow',
|
||||||
|
cf: {
|
||||||
|
cacheTtl: 2592000,
|
||||||
|
cacheEverything: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (request.method === 'GET' && response.ok) {
|
||||||
|
const cacheResponse = new Response(response.body, response);
|
||||||
|
cacheResponse.headers.set('Access-Control-Allow-Origin', '*');
|
||||||
|
cacheResponse.headers.set('Cache-Control', 'public, max-age=2592000');
|
||||||
|
|
||||||
|
cacheResponse.headers.delete('Set-Cookie');
|
||||||
|
|
||||||
|
context.waitUntil(cache.put(cacheKey, cacheResponse.clone()));
|
||||||
|
response = cacheResponse;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
|
||||||
|
const newResponse = new Response(response.body, response);
|
||||||
|
newResponse.headers.set('Access-Control-Allow-Origin', '*');
|
||||||
|
newResponse.headers.set('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS');
|
||||||
|
newResponse.headers.set('Access-Control-Expose-Headers', '*');
|
||||||
|
newResponse.headers.delete('content-security-policy');
|
||||||
|
newResponse.headers.delete('x-frame-options');
|
||||||
|
|
||||||
|
return newResponse;
|
||||||
|
} catch (error) {
|
||||||
|
return new Response('Proxy Error: ' + error.message, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
53
js/HiFi.ts.rej
Normal file
53
js/HiFi.ts.rej
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
@@ -2097,45 +2185,46 @@
|
||||||
|
offset,
|
||||||
|
countryCode: this.#countryCode,
|
||||||
|
},
|
||||||
|
signal,
|
||||||
|
openApiToken
|
||||||
|
);
|
||||||
|
- return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, data: fallback });
|
||||||
|
+ return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, data: parseOpenApiSearch(fallback) });
|
||||||
|
}
|
||||||
|
|
||||||
|
const openApiToken = await this.getOpenApiToken(signal);
|
||||||
|
+ const includeAll = 'albums.artists,albums.coverArt,artists.profileArt,playlists.coverArt,tracks.albums,tracks.albums.coverArt,tracks.artists,videos.artists,videos.image';
|
||||||
|
|
||||||
|
const mapping: Array<[string | undefined, string, Params]> = [
|
||||||
|
[
|
||||||
|
q,
|
||||||
|
`https://openapi.tidal.com/v2/searchResults/${encodeURIComponent(q || '')}`,
|
||||||
|
{
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
- include: 'albums,artists,tracks,videos,playlists,topHits',
|
||||||
|
+ include: includeAll,
|
||||||
|
countryCode: this.#countryCode,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
s,
|
||||||
|
`https://openapi.tidal.com/v2/searchResults/${encodeURIComponent(s || '')}`,
|
||||||
|
- { limit, offset, include: 'tracks', countryCode: this.#countryCode },
|
||||||
|
+ { limit, offset, include: includeAll, countryCode: this.#countryCode },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
a,
|
||||||
|
`https://openapi.tidal.com/v2/searchResults/${encodeURIComponent(a || '')}`,
|
||||||
|
- { limit, offset, include: 'artists,tracks', countryCode: this.#countryCode },
|
||||||
|
+ { limit, offset, include: includeAll, countryCode: this.#countryCode },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
al,
|
||||||
|
`https://openapi.tidal.com/v2/searchResults/${encodeURIComponent(al || '')}`,
|
||||||
|
- { limit, offset, include: 'albums', countryCode: this.#countryCode },
|
||||||
|
+ { limit, offset, include: includeAll, countryCode: this.#countryCode },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
v,
|
||||||
|
`https://openapi.tidal.com/v2/searchResults/${encodeURIComponent(v || '')}`,
|
||||||
|
- { limit, offset, include: 'videos', countryCode: this.#countryCode },
|
||||||
|
+ { limit, offset, include: includeAll, countryCode: this.#countryCode },
|
||||||
|
],
|
||||||
|
[
|
||||||
|
p,
|
||||||
|
`https://openapi.tidal.com/v2/searchResults/${encodeURIComponent(p || '')}`,
|
||||||
4
js/proxy-utils.js
Normal file
4
js/proxy-utils.js
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
export const getProxyUrl = (url) => {
|
||||||
|
if (window.__tidalOriginExtension) return url;
|
||||||
|
return `/proxy-audio?url=${encodeURIComponent(url)}`;
|
||||||
|
};
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
import { HiFiClient } from './js/HiFi.ts';
|
|
||||||
import { LosslessAPI } from './js/api.js';
|
|
||||||
|
|
||||||
// mock out modules to make LosslessAPI load in bun
|
|
||||||
import { mock } from 'bun:test';
|
|
||||||
mock.module('./js/icons.ts', () => ({}));
|
|
||||||
mock.module('./js/settings.js', () => ({
|
|
||||||
devModeSettings: { isEnabled: () => false },
|
|
||||||
syncManager: {},
|
|
||||||
musicProviderSettings: {},
|
|
||||||
audioSettings: {},
|
|
||||||
apiSettings: {},
|
|
||||||
}));
|
|
||||||
|
|
||||||
globalThis.localStorage = { getItem: () => null, setItem: () => {}, removeItem: () => {} };
|
|
||||||
globalThis.window = { matchMedia: () => ({ matches: false }) };
|
|
||||||
|
|
||||||
async function test() {
|
|
||||||
await HiFiClient.initialize();
|
|
||||||
const api = new LosslessAPI({ getInstances: () => [] });
|
|
||||||
|
|
||||||
// mock cache
|
|
||||||
api.cache = { get: () => null, set: () => {} };
|
|
||||||
|
|
||||||
api.fetchWithRetry = async function (relativePath, options) {
|
|
||||||
console.log('fetchWithRetry called:', relativePath);
|
|
||||||
return HiFiClient.instance.query(relativePath);
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = await api.search('coldplay');
|
|
||||||
console.log('Returned tracks:', res.tracks?.items?.length);
|
|
||||||
}
|
|
||||||
test().catch(console.error);
|
|
||||||
Loading…
Reference in a new issue