Skip to content

Commit

Permalink
Merge pull request #247 from thornbill/add-fmp4-setting
Browse files Browse the repository at this point in the history
  • Loading branch information
anthonylavado authored Apr 6, 2021
2 parents ab2134a + 3d6097e commit 804b0e1
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 21 deletions.
15 changes: 8 additions & 7 deletions components/NativeShellWebView.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import React, { useState } from 'react';
import { BackHandler, Platform } from 'react-native';
import { action } from 'mobx';
import { observer } from 'mobx-react';
import compareVersions from 'compare-versions';
import Constants from 'expo-constants';
import { activateKeepAwake, deactivateKeepAwake } from 'expo-keep-awake';
import compareVersions from 'compare-versions';
import { action } from 'mobx';
import { observer } from 'mobx-react';
import React, { useState } from 'react';
import { BackHandler, Platform } from 'react-native';

import { useStores } from '../hooks/useStores';
import MediaTypes from '../constants/MediaTypes';
import { useStores } from '../hooks/useStores';
import { getAppName, getDeviceProfile, getSafeDeviceName } from '../utils/Device';
import StaticScriptLoader from '../utils/StaticScriptLoader';
import { openBrowser } from '../utils/WebBrowser';

import RefreshWebView from './RefreshWebView';

const NativeShellWebView = observer(React.forwardRef(
Expand All @@ -39,7 +40,7 @@ window.ExpoAppSettings = {
isNativeVideoPlayerEnabled: ${rootStore.settingStore.isNativeVideoPlayerEnabled}
};
window.ExpoVideoProfile = ${JSON.stringify(getDeviceProfile())};
window.ExpoVideoProfile = ${JSON.stringify(getDeviceProfile({ enableFmp4: rootStore.settingStore.isFmp4Enabled }))};
function postExpoEvent(event, data) {
window.ReactNativeWebView.postMessage(JSON.stringify({
Expand Down
1 change: 1 addition & 0 deletions langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"keepAwake": "Keep Screen Awake",
"nativeVideoPlayer": "Use Native Video Player [BETA]",
"minimumServerVersion": "Requires Server Version 10.7+",
"fmp4Support": "Prefer fMP4 in HLS",
"lightTheme": "Use Light Theme",
"systemTheme": "Use System Theme",
"rotationLock": "Rotation Lock",
Expand Down
13 changes: 13 additions & 0 deletions screens/SettingsScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,19 @@ const SettingsScreen = observer(() => {
rootStore.isReloadRequired = true;
})
});

if (Platform.Version > 12) {
settingsData.push({
key: 'native-video-fmp4-switch',
title: t('settings.fmp4Support'),
value: rootStore.settingStore.isFmp4Enabled,
disabled: !rootStore.settingStore.isNativeVideoPlayerEnabled,
onValueChange: action(value => {
rootStore.settingStore.isFmp4Enabled = value;
rootStore.isReloadRequired = true;
})
});
}
}

settingsData.push({
Expand Down
7 changes: 7 additions & 0 deletions stores/SettingStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ export default class SettingStore {
*/
isNativeVideoPlayerEnabled = false

/**
* Is fMP4 enabled for the native video player
*/
isFmp4Enabled = false;

get theme() {
const id = this.isSystemThemeEnabled && this.systemThemeId && this.systemThemeId !== 'no-preference' ? this.systemThemeId : this.themeId;
return Themes[id] || Themes.dark;
Expand All @@ -66,6 +71,7 @@ export default class SettingStore {
this.systemThemeId = null;
this.isSystemThemeEnabled = false;
this.isNativeVideoPlayerEnabled = false;
this.isFmp4Enabled = false;
}
}

Expand All @@ -78,6 +84,7 @@ decorate(SettingStore, {
systemThemeId: observable,
isSystemThemeEnabled: observable,
isNativeVideoPlayerEnabled: observable,
isFmp4Enabled: observable,
theme: computed,
reset: action
});
10 changes: 9 additions & 1 deletion stores/__tests__/SettingStore.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
*/
import { Platform } from 'react-native';

import SettingStore from '../SettingStore';
import Themes from '../../themes';
import SettingStore from '../SettingStore';

jest.mock('react-native/Libraries/Utilities/Platform');

Expand All @@ -26,6 +26,8 @@ describe('SettingStore', () => {
expect(store.systemThemeId).toBeUndefined();
expect(store.isSystemThemeEnabled).toBe(false);
expect(store.theme).toBe(Themes.dark);
expect(store.isNativeVideoPlayerEnabled).toBe(false);
expect(store.isFmp4Enabled).toBe(false);
});

it('should disable rotation lock for iPad devices', () => {
Expand Down Expand Up @@ -84,6 +86,8 @@ describe('SettingStore', () => {
store.themeId = 'light';
store.systemThemeId = 'dark';
store.isSystemThemeEnabled = true;
store.isNativeVideoPlayerEnabled = true;
store.isFmp4Enabled = true;

expect(store.activeServer).toBe(99);
expect(store.isRotationLockEnabled).toBe(false);
Expand All @@ -93,6 +97,8 @@ describe('SettingStore', () => {
expect(store.systemThemeId).toBe('dark');
expect(store.isSystemThemeEnabled).toBe(true);
expect(store.theme).toBe(Themes.dark);
expect(store.isNativeVideoPlayerEnabled).toBe(true);
expect(store.isFmp4Enabled).toBe(true);

store.reset();

Expand All @@ -104,5 +110,7 @@ describe('SettingStore', () => {
expect(store.systemThemeId).toBeNull();
expect(store.isSystemThemeEnabled).toBe(false);
expect(store.theme).toBe(Themes.dark);
expect(store.isNativeVideoPlayerEnabled).toBe(false);
expect(store.isFmp4Enabled).toBe(false);
});
});
5 changes: 4 additions & 1 deletion utils/Device.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Platform } from 'react-native';
import iOSProfile from './profiles/ios';
import iOS10Profile from './profiles/ios10';
import iOS12Profile from './profiles/ios12';
import iOSFmp4Profile from './profiles/iosFmp4';

export function getAppName() {
return `Jellyfin Mobile (${Device.osName})`;
Expand All @@ -30,12 +31,14 @@ export function getSafeDeviceName() {
return Device.modelName || 'Jellyfin Mobile Device';
}

export function getDeviceProfile() {
export function getDeviceProfile({ enableFmp4 = false } = {}) {
if (Platform.OS === 'ios') {
if (parseInt(Platform.Version, 10) < 11) {
return iOS10Profile;
} else if (parseInt(Platform.Version, 10) < 13) {
return iOS12Profile;
} else if (enableFmp4) {
return iOSFmp4Profile;
} else {
return iOSProfile;
}
Expand Down
8 changes: 7 additions & 1 deletion utils/__tests__/Device.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import Constants from 'expo-constants';
import { Platform } from 'react-native';

import { getAppName, getDeviceProfile, getSafeDeviceName, isCompact, isSystemThemeSupported } from '../Device';
import iOSProfile from '../profiles/ios';
import iOS10Profile from '../profiles/ios10';
import iOS12Profile from '../profiles/ios12';
import iOSProfile from '../profiles/ios';
import iOSFmp4Profile from '../profiles/iosFmp4';

jest.mock('react-native/Libraries/Utilities/Platform');

Expand Down Expand Up @@ -66,6 +67,11 @@ describe('Device', () => {
expect(getDeviceProfile()).toBe(iOSProfile);
});

it ('should return the correct profile for iOS 13 devices when fMP4 support is enabled', () => {
Platform.Version = '13';
expect(getDeviceProfile({ enableFmp4: true })).toBe(iOSFmp4Profile);
});

it('should return the an empty profile for Android devices', () => {
Platform.OS = 'android';
expect(getDeviceProfile()).toStrictEqual({});
Expand Down
12 changes: 1 addition & 11 deletions utils/profiles/ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import MediaTypes from '../../constants/MediaTypes';

import BaseProfile from './base';

/**
Expand Down Expand Up @@ -119,17 +120,6 @@ export default {
Protocol: 'http',
Type: MediaTypes.Audio
},
{
AudioCodec: 'aac,mp3,flac,alac',
BreakOnNonKeyFrames: true,
Container: 'mp4',
Context: 'Streaming',
MaxAudioChannels: '6',
MinSegments: '2',
Protocol: 'hls',
Type: MediaTypes.Video,
VideoCodec: 'hevc,h264'
},
{
AudioCodec: 'aac,mp3',
BreakOnNonKeyFrames: true,
Expand Down
1 change: 1 addition & 0 deletions utils/profiles/ios10.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import MediaTypes from '../../constants/MediaTypes';

import BaseProfile from './base';

/**
Expand Down
35 changes: 35 additions & 0 deletions utils/profiles/iosFmp4.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import MediaTypes from '../../constants/MediaTypes';

import iOSProfile from './ios';

/**
* Device profile for Expo Video player on iOS 13+ with fMP4 support
*/
export default {
...iOSProfile,
Name: 'Expo iOS fMP4 Video Profile',
TranscodingProfiles: [
// Add all audio profiles from default profile
...iOSProfile.TranscodingProfiles.filter(profile => profile.Type === MediaTypes.Audio),
// Add fMP4 profile
{
AudioCodec: 'aac,mp3,flac,alac',
BreakOnNonKeyFrames: true,
Container: 'mp4',
Context: 'Streaming',
MaxAudioChannels: '6',
MinSegments: '2',
Protocol: 'hls',
Type: MediaTypes.Video,
VideoCodec: 'hevc,h264'
},
// Add all video profiles from default profile
...iOSProfile.TranscodingProfiles.filter(profile => profile.Type === MediaTypes.Video)
]
};

0 comments on commit 804b0e1

Please sign in to comment.