From 9470f8a7ada0a723ef8d807fed9c99b2d04fea0a Mon Sep 17 00:00:00 2001 From: Chaitanya Date: Sat, 6 Jun 2020 18:13:42 +0530 Subject: [PATCH 01/30] change Rc calls to rclone-api --- package.json | 3 ++- src/actions/providerStatusActions.js | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index d6c3562ae..df98e5c77 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,8 @@ "redux-thunk": "^2.3.0", "reselect": "^4.0.0", "simple-line-icons": "^2.4.1", - "typescript": "^3.7.5" + "typescript": "^3.7.5", + "rclone-api": "^1.0.0" }, "devDependencies": { "@babel/core": "^7.10.2", diff --git a/src/actions/providerStatusActions.js b/src/actions/providerStatusActions.js index b83c9bcff..6b49474f6 100644 --- a/src/actions/providerStatusActions.js +++ b/src/actions/providerStatusActions.js @@ -1,7 +1,6 @@ import {addColonAtLast, isLocalRemoteName} from "../utils/Tools"; import {GET_REMOTE_ABOUT, REQUEST_ERROR, REQUEST_SUCCESS} from "../actions/types"; -import axiosInstance from "../utils/API/API"; -import urls from "../utils/API/endpoint"; +import {getRemoteInfo} from "rclone-api"; /** * Gets the information of a provider @@ -29,13 +28,13 @@ export const getAboutRemote = (containerID) => { payload: {} }); - axiosInstance.post(urls.getAbout, {fs: remoteName}) + getRemoteInfo(remoteName) .then((res) => { dispatch({ type: GET_REMOTE_ABOUT, status: REQUEST_SUCCESS, id: containerID, - payload: res.data + payload: res }) }, (res) => { dispatch({ From 7702ddf9efe3f1605af97c5deb65c99cca0d7d29 Mon Sep 17 00:00:00 2001 From: Chaitanya Date: Fri, 22 May 2020 09:53:26 +0530 Subject: [PATCH 02/30] Video Plugin: Integration --- src/actions/pluginActions.js | 26 ++++++++++ src/reducers/pluginsReducer.js | 33 +++++++++++++ src/utils/API/endpoint.js | 2 + .../Base/Widgets/VideoPlayer/VideoPlayer.js | 48 +++++++++++++------ 4 files changed, 94 insertions(+), 15 deletions(-) create mode 100644 src/actions/pluginActions.js create mode 100644 src/reducers/pluginsReducer.js diff --git a/src/actions/pluginActions.js b/src/actions/pluginActions.js new file mode 100644 index 000000000..a3ea7917e --- /dev/null +++ b/src/actions/pluginActions.js @@ -0,0 +1,26 @@ +import axiosInstance from "../utils/API/API"; +import urls from "../utils/API/endpoint"; +import {GET_TEST_PLUGINS, REQUEST_ERROR, REQUEST_SUCCESS} from "./types"; + +/** + * Load test plugins + * @returns {Function} + */ +export const loadTestPlugins = () => { + return (dispatch) => { + axiosInstance.post(urls.listTestPlugins).then((res) => { + dispatch({ + type: GET_TEST_PLUGINS, + status: REQUEST_SUCCESS, + payload: res.data + }) + }, + (error) => { + dispatch({ + type: GET_TEST_PLUGINS, + status: REQUEST_ERROR, + payload: error + }) + }); + } +}; diff --git a/src/reducers/pluginsReducer.js b/src/reducers/pluginsReducer.js new file mode 100644 index 000000000..51d3a363c --- /dev/null +++ b/src/reducers/pluginsReducer.js @@ -0,0 +1,33 @@ +import {GET_TEST_PLUGINS, REQUEST_ERROR, REQUEST_SUCCESS} from "../actions/types"; + +const initialState = { + loadedTestPlugins: {}, + error: "" +}; + +/** + * Specifies the reducers for the job status and other remote ops. + * @param state + * @param action + * @returns {{jobStatus: {}, runningJobs: Array, runningJobsFetchError: {}}|({jobStatus, runningJobs, runningJobsFetchError}&{runningJobs: *, runningJobsFetchError: undefined})|({jobStatus, runningJobs, runningJobsFetchError}&{runningJobsFetchError: *})|({jobStatus, runningJobs, runningJobsFetchError}&{jobStatus: (initialState.jobStatus|{})})} + */ +export default function (state = initialState, action) { + switch (action.type) { + case GET_TEST_PLUGINS: + if (action.status === REQUEST_SUCCESS) { + return { + ...state, + loadedTestPlugins: action.payload.loadedTestPlugins, + } + } else if (action.status === REQUEST_ERROR) { + return { + ...state, + loadedTestPlugins: undefined, + error: action.payload + } + } + break; + default: + return state; + } +} diff --git a/src/utils/API/endpoint.js b/src/utils/API/endpoint.js index a6c8ed705..86ce30e4e 100644 --- a/src/utils/API/endpoint.js +++ b/src/utils/API/endpoint.js @@ -138,5 +138,7 @@ const urls = { */ unmountAll: "mount/unmountall", + listTestPlugins: "pluginsctl/listTestPlugins", + }; export default urls; diff --git a/src/views/Base/Widgets/VideoPlayer/VideoPlayer.js b/src/views/Base/Widgets/VideoPlayer/VideoPlayer.js index e3bba1ac8..3ef8f919d 100644 --- a/src/views/Base/Widgets/VideoPlayer/VideoPlayer.js +++ b/src/views/Base/Widgets/VideoPlayer/VideoPlayer.js @@ -1,11 +1,12 @@ import React, {useState} from "react"; -import {Button, Modal} from "reactstrap"; +import {Button} from "reactstrap"; import * as ReactDOM from "react-dom"; -import {MODAL_ROOT_ELEMENT} from "../../../../utils/Constants"; import * as PropTypes from "prop-types"; import ErrorBoundary from "../../../../ErrorHandling/ErrorBoundary"; import ReactAwesomePlayer from 'react-awesome-player' +import {connect} from "react-redux"; +import {MODAL_ROOT_ELEMENT} from "../../../../utils/Constants"; class PlayerComponent extends React.Component { // loadeddata() { @@ -46,10 +47,15 @@ class PlayerComponent extends React.Component { } } -function VideoPlayer({playbackURL, MimeType}) { +function VideoPlayer({playbackURL, MimeType, loadedTestPlugins}) { const [preview, setPreview] = useState(true); + const pluginUrl = loadedTestPlugins["@rclone/video-plugin"]; + + console.log("Plugin URL: " + pluginUrl, loadedTestPlugins) + + function hideFull(e) { e.stopPropagation(); setPreview(!preview); @@ -67,7 +73,7 @@ function VideoPlayer({playbackURL, MimeType}) { ) } else { - + if (!pluginUrl) return

Cannot load plugin

; // Load the video const subtitleURL = playbackURL.substring(0, playbackURL.lastIndexOf('.')) + ".vtt"; @@ -89,18 +95,25 @@ function VideoPlayer({playbackURL, MimeType}) { }; - element = ReactDOM.createPortal(( - + element = + ReactDOM.createPortal( + <> +
- {/**/} - + {/**/} + + +
+
-
- ), document.getElementById(MODAL_ROOT_ELEMENT)); + , document.getElementById(MODAL_ROOT_ELEMENT)); } return ( @@ -114,7 +127,12 @@ function VideoPlayer({playbackURL, MimeType}) { VideoPlayer.propTypes = { playbackURL: PropTypes.string.isRequired, - MimeType: PropTypes.string.isRequired + MimeType: PropTypes.string.isRequired, + loadedTestPlugins: PropTypes.object.isRequired, }; -export default VideoPlayer; \ No newline at end of file +const mapStateToProps = (state) => ({ + loadedTestPlugins: state.plugins.loadedTestPlugins +}); + +export default connect(mapStateToProps, {})(VideoPlayer); From 91174f03ce8e2188541fab096da404895ff7736c Mon Sep 17 00:00:00 2001 From: Chaitanya Date: Fri, 22 May 2020 11:30:50 +0530 Subject: [PATCH 03/30] plugins: Added plugin dashboard --- src/_nav.js | 6 ++- src/actions/pluginActions.js | 14 ++++++- src/actions/types.js | 2 + src/reducers/pluginsReducer.js | 4 +- src/utils/API/endpoint.js | 8 ++++ .../PluginsDashboard/PluginsDashboard.js | 18 +++++++++ .../PluginsDashboard/PluginsDashboard.test.js | 40 +++++++++++++++++++ 7 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 src/views/PluginsDashboard/PluginsDashboard.js create mode 100644 src/views/PluginsDashboard/PluginsDashboard.test.js diff --git a/src/_nav.js b/src/_nav.js index 1d467bbb2..4ce1e96fa 100644 --- a/src/_nav.js +++ b/src/_nav.js @@ -20,6 +20,11 @@ export default { url: '/rcloneBackend', icon: 'icon-star', }, + { + name: 'Plugins', + url: '/pluginsDashboard', + icon: 'icon-plugins' + }, { name: 'Mounts', url: '/mountDashboard', @@ -30,6 +35,5 @@ export default { url: '/login', icon: 'icon-logout', }, - ], }; diff --git a/src/actions/pluginActions.js b/src/actions/pluginActions.js index a3ea7917e..06c338c0b 100644 --- a/src/actions/pluginActions.js +++ b/src/actions/pluginActions.js @@ -1,6 +1,6 @@ import axiosInstance from "../utils/API/API"; import urls from "../utils/API/endpoint"; -import {GET_TEST_PLUGINS, REQUEST_ERROR, REQUEST_SUCCESS} from "./types"; +import {ADD_TEST_PLUGIN, GET_TEST_PLUGINS, REQUEST_ERROR, REQUEST_SUCCESS} from "./types"; /** * Load test plugins @@ -24,3 +24,15 @@ export const loadTestPlugins = () => { }); } }; + +export const addTestPlugin = (pluginName, pluginUrl) => (dispatch) => { + axiosInstance.post(urls.addTestPlugin, {name: pluginName, loadUrl: pluginUrl, test: true}).then(res => { + dispatch(loadTestPlugins()); + }, (error) => { + dispatch({ + type: ADD_TEST_PLUGIN, + status: REQUEST_ERROR, + payload: error + }) + }) +} diff --git a/src/actions/types.js b/src/actions/types.js index 7f536c588..6fb73cea3 100644 --- a/src/actions/types.js +++ b/src/actions/types.js @@ -42,6 +42,8 @@ export const GET_MOUNT_LIST = "GET_MOUNT_LIST"; export const REMOVE_MOUNT = "REMOVE_MOUNT"; export const CREATE_MOUNT = "CREATE_MOUNT"; +export const ADD_TEST_PLUGIN = "ADD_TEST_PLUGIN"; + export const REQUEST_ERROR = 'ERROR'; export const REQUEST_SUCCESS = 'SUCCESS'; export const REQUEST_LOADING = 'LOADING'; diff --git a/src/reducers/pluginsReducer.js b/src/reducers/pluginsReducer.js index 51d3a363c..3b704eebe 100644 --- a/src/reducers/pluginsReducer.js +++ b/src/reducers/pluginsReducer.js @@ -1,4 +1,4 @@ -import {GET_TEST_PLUGINS, REQUEST_ERROR, REQUEST_SUCCESS} from "../actions/types"; +import {ADD_TEST_PLUGIN, GET_TEST_PLUGINS, REQUEST_ERROR, REQUEST_SUCCESS} from "../actions/types"; const initialState = { loadedTestPlugins: {}, @@ -27,6 +27,8 @@ export default function (state = initialState, action) { } } break; + case ADD_TEST_PLUGIN: + return state; default: return state; } diff --git a/src/utils/API/endpoint.js b/src/utils/API/endpoint.js index 86ce30e4e..b78f4483e 100644 --- a/src/utils/API/endpoint.js +++ b/src/utils/API/endpoint.js @@ -138,7 +138,15 @@ const urls = { */ unmountAll: "mount/unmountall", + /** + * List the loaded test plugins + */ listTestPlugins: "pluginsctl/listTestPlugins", + /** + * Add a test plugin + */ + addTestPlugin: "pluginsctl/addTestPlugin", + }; export default urls; diff --git a/src/views/PluginsDashboard/PluginsDashboard.js b/src/views/PluginsDashboard/PluginsDashboard.js new file mode 100644 index 000000000..2e9a9395f --- /dev/null +++ b/src/views/PluginsDashboard/PluginsDashboard.js @@ -0,0 +1,18 @@ +import React from 'react'; +import {connect} from "react-redux"; + +class PluginsDashboard extends React.Component { + render() { + return ( +
+ + +
); + } +} + +const mapStateToProps = state => ({}); + +PluginsDashboard.propTypes = {}; + +export default connect(mapStateToProps, {})(PluginsDashboard); diff --git a/src/views/PluginsDashboard/PluginsDashboard.test.js b/src/views/PluginsDashboard/PluginsDashboard.test.js new file mode 100644 index 000000000..506b9ef84 --- /dev/null +++ b/src/views/PluginsDashboard/PluginsDashboard.test.js @@ -0,0 +1,40 @@ +import React from "react"; +import {shallow} from "enzyme"; +import {findByTestAttr, testStore} from "../../../Utils"; +import toJson from "enzyme-to-json"; +import PluginsDashboard from "./PluginsDashboard"; + +const setUp = (intialState = {}, props = {}) => { + const store = testStore(intialState); + + const component = shallow(); + return component.childAt(0).dive(); +}; + + +describe('Plugins Dashboard', () => { +}, function () { + describe('renders', function () { + let wrapper; + beforeEach(() => { + const initialState = { + status: { + checkStatus: true + } + }; + + const props = {}; + wrapper = setUp(initialState, props) + }); + + it('should render without crashing', function () { + const component = findByTestAttr(wrapper, "pluginsDashboardComponent"); + expect(component).toHaveLength(1); + }); + + it('should match snapshot', function () { + expect(toJson(wrapper)).toMatchSnapshot() + }); + }); + +}); From 28c6b7d968a0c1eca86b38cae2ada67594e14ad0 Mon Sep 17 00:00:00 2001 From: Chaitanya Date: Wed, 27 May 2020 15:52:15 +0530 Subject: [PATCH 04/30] plugins: load plugins dynamically --- src/views/Base/Widgets/VideoPlayer/VideoPlayer.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/views/Base/Widgets/VideoPlayer/VideoPlayer.js b/src/views/Base/Widgets/VideoPlayer/VideoPlayer.js index 3ef8f919d..b5396f006 100644 --- a/src/views/Base/Widgets/VideoPlayer/VideoPlayer.js +++ b/src/views/Base/Widgets/VideoPlayer/VideoPlayer.js @@ -7,6 +7,7 @@ import ErrorBoundary from "../../../../ErrorHandling/ErrorBoundary"; import ReactAwesomePlayer from 'react-awesome-player' import {connect} from "react-redux"; import {MODAL_ROOT_ELEMENT} from "../../../../utils/Constants"; +import Iframe from "react-iframe"; class PlayerComponent extends React.Component { // loadeddata() { @@ -51,6 +52,9 @@ function VideoPlayer({playbackURL, MimeType, loadedTestPlugins}) { const [preview, setPreview] = useState(true); + if (!loadedTestPlugins) { + return null; + } const pluginUrl = loadedTestPlugins["@rclone/video-plugin"]; console.log("Plugin URL: " + pluginUrl, loadedTestPlugins) @@ -68,7 +72,6 @@ function VideoPlayer({playbackURL, MimeType, loadedTestPlugins}) {
) @@ -103,7 +106,14 @@ function VideoPlayer({playbackURL, MimeType, loadedTestPlugins}) { {/**/} - + {/**/} +