mirror of
https://github.com/Viren070/AIOStreams.git
synced 2025-12-01 23:14:04 +01:00
feat: support customising which streams to proxy, fix duplication check bypasses by setting falsy/false values to undefined to match intial state, refactor multi select component usage
closes #8 closes #17 closes #18
This commit is contained in:
@@ -434,6 +434,31 @@ export class AIOStreams {
|
||||
};
|
||||
}
|
||||
|
||||
private shouldProxyStream(stream: ParsedStream): boolean {
|
||||
const mediaFlowConfig = getMediaFlowConfig(this.config);
|
||||
if (!mediaFlowConfig.mediaFlowEnabled) return false;
|
||||
if (!stream.url) return false;
|
||||
// now check if mediaFlowConfig.proxiedAddons or mediaFlowConfig.proxiedServices is not null
|
||||
|
||||
if (
|
||||
mediaFlowConfig.proxiedAddons &&
|
||||
!mediaFlowConfig.proxiedAddons.includes(stream.addon.id)
|
||||
)
|
||||
return false;
|
||||
|
||||
if (
|
||||
(mediaFlowConfig.proxiedServices &&
|
||||
stream.provider &&
|
||||
!mediaFlowConfig.proxiedServices.includes(stream.provider.id)) ||
|
||||
(mediaFlowConfig.proxiedServices &&
|
||||
!stream.provider &&
|
||||
!mediaFlowConfig.proxiedServices.includes('none'))
|
||||
)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async createStreamObject(
|
||||
parsedStream: ParsedStream
|
||||
): Promise<Stream | null> {
|
||||
@@ -476,10 +501,8 @@ export class AIOStreams {
|
||||
];
|
||||
|
||||
let stream: Stream;
|
||||
const isMediaFlowEnabled =
|
||||
this.config.mediaFlowConfig?.mediaFlowEnabled ||
|
||||
Settings.DEFAULT_MEDIAFLOW_URL;
|
||||
if (isMediaFlowEnabled && parsedStream?.url) {
|
||||
const shouldProxy = this.shouldProxyStream(parsedStream);
|
||||
if (shouldProxy) {
|
||||
try {
|
||||
const mediaFlowStream = this.createMediaFlowStream(
|
||||
parsedStream,
|
||||
|
||||
@@ -137,23 +137,27 @@
|
||||
}
|
||||
|
||||
.mediaFlowConfig {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-radius: var(--borderRadius);
|
||||
border-color: #777777;
|
||||
padding: 10px;
|
||||
transition:
|
||||
margin-top 0.4s,
|
||||
opacity 0.3s,
|
||||
transform 0.6s;
|
||||
margin-top 0.6s,
|
||||
opacity 0.6s,
|
||||
transform 1s;
|
||||
}
|
||||
|
||||
.mediaFlowConfig.hidden {
|
||||
opacity: 0;
|
||||
margin-top: -340px;
|
||||
margin-top: -655px;
|
||||
transform: scale(0);
|
||||
transition:
|
||||
margin-top 0.5s,
|
||||
opacity 0.2s,
|
||||
transform 0.4s;
|
||||
}
|
||||
|
||||
.mediaFlowSection {
|
||||
padding: 10px;
|
||||
margin: 5px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-radius: var(--borderRadius);
|
||||
border-color: #777777;
|
||||
}
|
||||
|
||||
@@ -137,7 +137,6 @@ const defaultServices = serviceDetails.map((service) => ({
|
||||
}));
|
||||
|
||||
export default function Configure() {
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
const [resolutions, setResolutions] =
|
||||
useState<Resolution[]>(defaultResolutions);
|
||||
const [qualities, setQualities] = useState<Quality[]>(defaultQualities);
|
||||
@@ -175,6 +174,12 @@ export default function Configure() {
|
||||
const [mediaFlowProxyUrl, setmediaFlowProxyUrl] = useState<string>('');
|
||||
const [mediaFlowApiPassword, setmediaFlowApiPassword] = useState<string>('');
|
||||
const [mediaFlowPublicIp, setMediaFlowPublicIp] = useState<string>('');
|
||||
const [mediaFlowProxiedAddons, setMediaFlowProxiedAddons] = useState<
|
||||
string[] | null
|
||||
>(null);
|
||||
const [mediaFlowProxiedServices, setMediaFlowProxiedServices] = useState<
|
||||
string[] | null
|
||||
>(null);
|
||||
const [disableButtons, setDisableButtons] = useState<boolean>(false);
|
||||
const [manualManifestUrl, setManualManifestUrl] = useState<string | null>(
|
||||
null
|
||||
@@ -227,6 +232,8 @@ export default function Configure() {
|
||||
proxyUrl: mediaFlowProxyUrl,
|
||||
apiPassword: mediaFlowApiPassword,
|
||||
publicIp: mediaFlowPublicIp,
|
||||
proxiedAddons: mediaFlowProxiedAddons,
|
||||
proxiedServices: mediaFlowProxiedServices,
|
||||
},
|
||||
addons,
|
||||
services,
|
||||
@@ -311,6 +318,7 @@ export default function Configure() {
|
||||
const config = createConfig();
|
||||
|
||||
const { valid, errorCode, errorMessage } = validateConfig(config);
|
||||
console.log('Config', config, 'was valid:', valid);
|
||||
if (!valid) {
|
||||
showToast(
|
||||
errorMessage || 'Invalid config',
|
||||
@@ -526,7 +534,6 @@ export default function Configure() {
|
||||
|
||||
// Load config from the window path if it exists
|
||||
useEffect(() => {
|
||||
setIsClient(true);
|
||||
async function decodeConfig(config: string) {
|
||||
let decodedConfig: Config;
|
||||
if (config.startsWith('E-')) {
|
||||
@@ -837,28 +844,12 @@ export default function Configure() {
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
{isClient ? ( // https://github.com/JedWatson/react-select/issues/5859
|
||||
<MultiSelect
|
||||
options={allowedLanguages
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
.map((language) => ({ value: language, label: language }))}
|
||||
setValues={setPrioritisedLanguages}
|
||||
/>
|
||||
) : (
|
||||
// render a fake select box until the actual one is rendered
|
||||
<div
|
||||
style={{
|
||||
height: '42px',
|
||||
margin: '0',
|
||||
backgroundColor: 'white',
|
||||
borderRadius: 'var(--borderRadius)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<p style={{ margin: '10px', color: '#808090' }}>Select...</p>
|
||||
</div>
|
||||
)}
|
||||
<MultiSelect
|
||||
options={allowedLanguages
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
.map((language) => ({ value: language, label: language }))}
|
||||
setValues={setPrioritisedLanguages}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ marginBottom: '0px' }} className={styles.section}>
|
||||
@@ -873,28 +864,12 @@ export default function Configure() {
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
{isClient ? (
|
||||
<MultiSelect
|
||||
options={allowedLanguages
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
.map((language) => ({ value: language, label: language }))}
|
||||
setValues={setExcludedLanguages}
|
||||
/>
|
||||
) : (
|
||||
// render a fake select box until the actual one is rendered
|
||||
<div
|
||||
style={{
|
||||
height: '42px',
|
||||
margin: '0',
|
||||
backgroundColor: 'white',
|
||||
borderRadius: 'var(--borderRadius)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<p style={{ margin: '10px', color: '#808090' }}>Select...</p>
|
||||
</div>
|
||||
)}
|
||||
<MultiSelect
|
||||
options={allowedLanguages
|
||||
.sort((a, b) => a.localeCompare(b))
|
||||
.map((language) => ({ value: language, label: language }))}
|
||||
setValues={setExcludedLanguages}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1100,61 +1075,119 @@ export default function Configure() {
|
||||
<div
|
||||
className={`${styles.mediaFlowConfig} ${mediaFlowEnabled ? '' : styles.hidden}`}
|
||||
>
|
||||
<div>
|
||||
<div className={styles.mediaFlowSection}>
|
||||
<div>
|
||||
<h3 style={{ padding: '5px' }}>Proxy URL</h3>
|
||||
<p style={{ padding: '5px' }}>
|
||||
The URL of the MediaFlow proxy server
|
||||
</p>
|
||||
<div>
|
||||
<h3 style={{ padding: '5px' }}>Proxy URL</h3>
|
||||
<p style={{ padding: '5px' }}>
|
||||
The URL of the MediaFlow proxy server
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<CredentialInput
|
||||
credential={mediaFlowProxyUrl}
|
||||
setCredential={setmediaFlowProxyUrl}
|
||||
inputProps={{
|
||||
placeholder: 'Enter your MediaFlow proxy URL',
|
||||
disabled: !mediaFlowEnabled,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<CredentialInput
|
||||
credential={mediaFlowProxyUrl}
|
||||
setCredential={setmediaFlowProxyUrl}
|
||||
inputProps={{
|
||||
placeholder: 'Enter your MediaFlow proxy URL',
|
||||
disabled: !mediaFlowEnabled,
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
<h3 style={{ padding: '5px' }}>API Password</h3>
|
||||
<p style={{ padding: '5px' }}>
|
||||
Your MediaFlow's API password
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<CredentialInput
|
||||
credential={mediaFlowApiPassword}
|
||||
setCredential={setmediaFlowApiPassword}
|
||||
inputProps={{
|
||||
placeholder: 'Enter your MediaFlow API password',
|
||||
disabled: !mediaFlowEnabled,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<h3 style={{ padding: '5px' }}>Public IP (Optional)</h3>
|
||||
<p style={{ padding: '5px' }}>
|
||||
Configure this only when running MediaFlow locally with a
|
||||
proxy service. Leave empty if MediaFlow is configured
|
||||
locally without a proxy server or if it's hosted on a
|
||||
remote server.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<CredentialInput
|
||||
credential={mediaFlowPublicIp}
|
||||
setCredential={setMediaFlowPublicIp}
|
||||
inputProps={{
|
||||
placeholder: 'Enter your MediaFlow public IP',
|
||||
disabled: !mediaFlowEnabled,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.mediaFlowSection}>
|
||||
<div>
|
||||
<h3 style={{ padding: '5px' }}>API Password</h3>
|
||||
<p style={{ padding: '5px' }}>
|
||||
Your MediaFlow's API password
|
||||
</p>
|
||||
<div>
|
||||
<h3 style={{ padding: '5px' }}>Proxy Addons (Optional)</h3>
|
||||
<p style={{ padding: '5px' }}>
|
||||
By default, all streams from every addon are proxied.
|
||||
Choose specific addons here to proxy only their streams.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<MultiSelect
|
||||
options={
|
||||
addons.map((addon) => ({
|
||||
value: `${addon.id}-${JSON.stringify(addon.options)}`,
|
||||
label:
|
||||
addon.options.addonName ||
|
||||
addon.options.overrideName ||
|
||||
addon.options.name ||
|
||||
addon.id.charAt(0).toUpperCase() +
|
||||
addon.id.slice(1),
|
||||
})) || []
|
||||
}
|
||||
setValues={(selectedAddons) => {
|
||||
setMediaFlowProxiedAddons(selectedAddons);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<CredentialInput
|
||||
credential={mediaFlowApiPassword}
|
||||
setCredential={setmediaFlowApiPassword}
|
||||
inputProps={{
|
||||
placeholder: 'Enter your MediaFlow API password',
|
||||
disabled: !mediaFlowEnabled,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<h3 style={{ padding: '5px' }}>Public IP (Optional)</h3>
|
||||
<p style={{ padding: '5px' }}>
|
||||
Configure this only when running MediaFlow locally with a
|
||||
proxy service. Leave empty if MediaFlow is configured
|
||||
locally without a proxy server or if it's hosted on a
|
||||
remote server.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<CredentialInput
|
||||
credential={mediaFlowPublicIp}
|
||||
setCredential={setMediaFlowPublicIp}
|
||||
inputProps={{
|
||||
placeholder: 'Enter your MediaFlow public IP',
|
||||
disabled: !mediaFlowEnabled,
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
<h3 style={{ padding: '5px' }}>
|
||||
Proxy Services (Optional)
|
||||
</h3>
|
||||
<p style={{ padding: '5px' }}>
|
||||
By default, all streams whether they are from a serivce or
|
||||
not are proxied. Choose which services you want to proxy
|
||||
through MediaFlow. Selecting None will also proxy streams
|
||||
that are not (detected to be) from a service.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<MultiSelect
|
||||
options={[
|
||||
{ value: 'none', label: 'None' },
|
||||
...serviceDetails.map((service) => ({
|
||||
value: service.id,
|
||||
label: service.name,
|
||||
})),
|
||||
]}
|
||||
setValues={(selectedServices) => {
|
||||
setMediaFlowProxiedServices(selectedServices);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -175,7 +175,7 @@ const AddonsList: React.FC<AddonsListProps> = ({
|
||||
updateOption(
|
||||
index,
|
||||
option.id,
|
||||
e.target.checked ? 'true' : 'false'
|
||||
e.target.checked ? 'true' : undefined
|
||||
)
|
||||
}
|
||||
className={styles.checkbox}
|
||||
@@ -188,7 +188,11 @@ const AddonsList: React.FC<AddonsListProps> = ({
|
||||
<CredentialInput
|
||||
credential={addon.options[option.id] || ''}
|
||||
setCredential={(value) =>
|
||||
updateOption(index, option.id, value)
|
||||
updateOption(
|
||||
index,
|
||||
option.id,
|
||||
value ? value : undefined
|
||||
)
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { useEffect, useState } from 'react';
|
||||
import Select, { StylesConfig } from 'react-select';
|
||||
|
||||
const selectStyles: StylesConfig = {
|
||||
@@ -79,19 +80,41 @@ interface MultiSelectProps {
|
||||
}
|
||||
|
||||
const MultiSelect: React.FC<MultiSelectProps> = ({ options, setValues }) => {
|
||||
const [isClient, setIsClient] = useState(false);
|
||||
useEffect(() => {
|
||||
setIsClient(true);
|
||||
}, []);
|
||||
return (
|
||||
<Select
|
||||
isMulti
|
||||
closeMenuOnSelect={false}
|
||||
options={options}
|
||||
onChange={(selectedOptions: any) => {
|
||||
const selectedLanguages = selectedOptions.map(
|
||||
(option: any) => option.value
|
||||
);
|
||||
setValues(selectedLanguages);
|
||||
}}
|
||||
styles={selectStyles}
|
||||
/>
|
||||
<>
|
||||
{isClient ? (
|
||||
// https://github.com/JedWatson/react-select/issues/5859
|
||||
<Select
|
||||
isMulti
|
||||
closeMenuOnSelect={false}
|
||||
options={options}
|
||||
onChange={(selectedOptions: any) => {
|
||||
const selectedLanguages = selectedOptions.map(
|
||||
(option: any) => option.value
|
||||
);
|
||||
setValues(selectedLanguages);
|
||||
}}
|
||||
styles={selectStyles}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
height: '42px',
|
||||
margin: '0',
|
||||
backgroundColor: 'white',
|
||||
borderRadius: 'var(--borderRadius)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<p style={{ margin: '10px', color: '#808090' }}>Select...</p>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -154,6 +154,8 @@ export interface Config {
|
||||
proxyUrl: string;
|
||||
apiPassword: string;
|
||||
publicIp: string;
|
||||
proxiedAddons: string[] | null;
|
||||
proxiedServices: string[] | null;
|
||||
};
|
||||
addons: {
|
||||
id: string;
|
||||
|
||||
@@ -138,5 +138,7 @@ export function getMediaFlowConfig(userConfig: Config) {
|
||||
apiPassword:
|
||||
mediaFlowConfig?.apiPassword || Settings.DEFAULT_MEDIAFLOW_API_PASSWORD,
|
||||
publicIp: mediaFlowConfig?.publicIp || Settings.DEFAULT_MEDIAFLOW_PUBLIC_IP,
|
||||
proxiedAddons: mediaFlowConfig?.proxiedAddons || null,
|
||||
proxiedServices: mediaFlowConfig?.proxiedServices || null,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user