A music player that connects to your cloud/distributed storage.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fixes playback issue with Google Drive on Safari

+75 -14
+66 -10
src/Javascript/Workers/service.js
··· 5 5 // This worker is responsible for caching the application 6 6 // so it can be used offline. 7 7 8 + import { stringify } from "postcss" 9 + 8 10 9 11 const KEY = 10 12 /* eslint-disable no-undef */ ··· 18 20 ] 19 21 20 22 23 + const GOOGLE_DRIVE = "https://www.googleapis.com/drive/" 24 + 25 + 26 + 27 + // 🙈 28 + 29 + 21 30 const isNativeWrapper = location.host === "localhost:44999" || location.host === "127.0.0.1:44999" 31 + let googleDriveToken 22 32 23 33 24 34 ··· 72 82 ) 73 83 74 84 // When doing a request with access token in the url, put it in the headers instead 75 - } else if (event.request.url.includes("access_token=")) { 85 + } else if (event.request.url.includes("bearer_token=")) { 76 86 const url = new URL(event.request.url) 77 - const token = url.searchParams.get("access_token") 87 + const token = url.searchParams.get("bearer_token") 78 88 79 - url.searchParams.delete("access_token") 89 + if (url.href.startsWith(GOOGLE_DRIVE)) googleDriveToken = token 90 + 91 + url.searchParams.delete("bearer_token") 80 92 url.search = "?" + url.searchParams.toString() 81 93 82 94 newRequestWithAuth( ··· 93 105 : cacheThenNetwork(event) 94 106 ) 95 107 108 + } else if (event.request.url && event.request.url.startsWith(GOOGLE_DRIVE) && event.request.url.includes("alt=media")) { 109 + // For some reason Safari starts using the non bearer-token URL while playing audio 110 + googleDriveToken 111 + ? newRequestWithAuth( 112 + event, 113 + event.request.url.toString(), 114 + "Bearer " + googleDriveToken 115 + ) 116 + : event.respondWith( 117 + network(event) 118 + ) 119 + 96 120 } 97 121 }) 98 122 ··· 125 149 126 150 127 151 function newRequestWithAuth(event, urlWithoutToken, authToken, mode) { 128 - const newHeaders = new Headers(event.request.headers) 129 - newHeaders.set("authorization", authToken) 152 + const request = event.request 153 + const newHeaders = Object.fromEntries( 154 + request.headers.entries() 155 + ) 130 156 131 - const newRequest = new Request(new Request(urlWithoutToken, event.request), { 132 - headers: newHeaders, 133 - mode: mode || "cors", 134 - cache: "no-cache" 157 + newHeaders["authorization"] = authToken 158 + 159 + const newRequest = new Request( 160 + new Request(urlWithoutToken, event.request), 161 + { 162 + headers: newHeaders, 163 + credentials: request.credentials, 164 + cache: request.cache, 165 + destination: request.destination, 166 + method: request.method, 167 + mode: request.mode, 168 + redirect: request.redirect, 169 + referrer: request.referrer, 170 + } 171 + ) 172 + 173 + let retries = 0 174 + 175 + const makeFetch = () => fetch(newRequest).then(r => { 176 + if (r.ok) { 177 + retries = 0 178 + return r 179 + } else { 180 + return r.text().then(text => { 181 + throw new Error(text) 182 + }) 183 + } 184 + }).catch(err => { 185 + retries++ 186 + if (retries <= 1000) return new Promise((resolve, reject) => setTimeout(makeFetch().then(resolve, reject), 1000)) 187 + else throw new Error(err) 188 + 135 189 }) 136 190 137 - event.respondWith(fetch(newRequest)) 191 + event.respondWith( 192 + makeFetch() 193 + ) 138 194 }
+8 -3
src/Javascript/audio-engine.js
··· 130 130 audioElementsContainer.querySelector("audio") 131 131 132 132 if (SINGLE_AUDIO_NODE && audioNode) { 133 + const crossorigin = isCrossOrginUrl(queueItem.url) ? "use-credentials" : "anonymous" 134 + audioNode.setAttribute("crossorigin", crossorigin) 133 135 audioNode.setAttribute("src", queueItem.url) 134 136 audioNode.setAttribute("rel", queueItem.trackId) 135 137 audioNode.load() ··· 169 171 if (is) fn.call(orchestrion, event) 170 172 } 171 173 172 - const crossorigin = queueItem.url.includes("service_worker_authentication") 173 - ? "use-credentials" 174 - : "anonymous" 174 + const crossorigin = isCrossOrginUrl(queueItem.url) ? "use-credentials" : "anonymous" 175 175 176 176 audio = new Audio() 177 177 audio.setAttribute("crossorigin", crossorigin) ··· 234 234 if (audio.paused) audio.play() 235 235 audio.currentTime = audio.duration * percentage 236 236 } 237 + } 238 + 239 + 240 + export function isCrossOrginUrl(url) { 241 + return url.includes("service_worker_authentication") 237 242 } 238 243 239 244
+1 -1
src/Library/Sources/Services/Google.elm
··· 306 306 String.concat 307 307 [ "https://www.googleapis.com/drive/v3/files/" 308 308 , Url.percentEncode fileId 309 - , "?alt=media&access_token=" 309 + , "?alt=media&bearer_token=" 310 310 , Url.percentEncode accessToken 311 311 ]