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