Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proxy live streams when "Proxy Videos Through Invidious" is enabled #5649

Merged
merged 1 commit into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const HTTP_IN_HEX = 0x68747470
const USE_OVERFLOW_MENU_WIDTH_THRESHOLD = 600

const RequestType = shaka.net.NetworkingEngine.RequestType
const AdvancedRequestType = shaka.net.NetworkingEngine.AdvancedRequestType
const TrackLabelFormat = shaka.ui.Overlay.TrackLabelFormat

/** @type {Map<string, string>} */
Expand Down Expand Up @@ -1232,7 +1233,47 @@ export default defineComponent({
response.data = new TextEncoder().encode(cleaned).buffer
}
}
} else if (type === RequestType.MANIFEST && context.type === AdvancedRequestType.MEDIA_PLAYLIST) {
const url = new URL(response.uri)

// Fixes proxied HLS manifests, as Invidious replaces the path parameters with query parameters,
// so shaka-player isn't able to infer the mime type from the `/file/seg.ts` part like it does for non-proxied HLS manifests.
// Shaka-player does attempt to detect it with HEAD request but the `Content-Type` header is `application/octet-stream`,
// which still doesn't tell shaka-player how to handle the stream because that's the equivalent of saying "binary data".
if (url.searchParams.has('local')) {
const stringBody = new TextDecoder().decode(response.data)
const fixed = stringBody.replaceAll(/https?:\/\/.+$/gm, hlsProxiedUrlReplacer)

response.data = new TextEncoder().encode(fixed).buffer
}
}
}

/**
* @param {string} match
*/
function hlsProxiedUrlReplacer(match) {
const url = new URL(match)

let fileValue
for (const [key, value] of url.searchParams) {
if (key === 'file') {
fileValue = value
continue
} else if (key === 'hls_chunk_host') {
// Add the host parameter so some Invidious instances stop complaining about the missing host parameter
// Replace .c.youtube.com with .googlevideo.com as the built-in Invidious video proxy only accepts host parameters with googlevideo.com
url.pathname += `/host/${encodeURIComponent(value.replace('.c.youtube.com', '.googlevideo.com'))}`
}

url.pathname += `/${key}/${encodeURIComponent(value)}`
}

// This has to be right at the end so that shaka-player can read the file extension
url.pathname += `/file/${encodeURIComponent(fileValue)}`

url.search = ''
return url.toString()
}

// #endregion request/response filters
Expand Down
22 changes: 20 additions & 2 deletions src/renderer/views/Watch/Watch.js
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,16 @@ export default defineComponent({
// this.manifestSrc = src
// this.manifestMimeType = MANIFEST_TYPE_DASH
// } else {
this.manifestSrc = result.streaming_data.hls_manifest_url
let hlsManifestUrl = result.streaming_data.hls_manifest_url

if (this.proxyVideos) {
const url = new URL(hlsManifestUrl)
url.searchParams.set('local', 'true')

hlsManifestUrl = url.toString().replace(url.origin, this.currentInvidiousInstanceUrl)
}

this.manifestSrc = hlsManifestUrl
this.manifestMimeType = MANIFEST_TYPE_HLS
// }
}
Expand Down Expand Up @@ -811,7 +820,16 @@ export default defineComponent({
// // Proxying doesn't work for live or post live DVR DASH, so use HLS instead
// // https://github.com/iv-org/invidious/pull/4589
// if (this.proxyVideos) {
this.manifestSrc = result.hlsUrl

let hlsManifestUrl = result.hlsUrl

if (this.proxyVideos) {
const url = new URL(hlsManifestUrl)
url.searchParams.set('local', 'true')
hlsManifestUrl = url.toString()
}

this.manifestSrc = hlsManifestUrl
this.manifestMimeType = MANIFEST_TYPE_HLS

// The HLS manifests only contain combined audio+video streams, so we can't do audio only
Expand Down