* no unauthorized init

* more dynamic Preview element
This commit is contained in:
lukas 2021-04-16 21:12:56 +02:00
parent 57a7a9a827
commit fdcecb0a75
14 changed files with 142 additions and 108 deletions

View File

@ -15,7 +15,6 @@ const (
TagNode = iota TagNode = iota
SettingsNode = iota SettingsNode = iota
ActorNode = iota ActorNode = iota
InitNode = iota
) )
type actionStruct struct { type actionStruct struct {
@ -42,9 +41,6 @@ func ServerInit() {
http.Handle(APIPREFIX+"/settings", oauth.ValidateToken(settingsHandler)) http.Handle(APIPREFIX+"/settings", oauth.ValidateToken(settingsHandler))
http.Handle(APIPREFIX+"/actor", oauth.ValidateToken(actorHandler)) http.Handle(APIPREFIX+"/actor", oauth.ValidateToken(actorHandler))
// initialization api calls to check if password is neccessaray
http.Handle(APIPREFIX+"/init", http.HandlerFunc(initHandler))
// initialize oauth service and add corresponding auth routes // initialize oauth service and add corresponding auth routes
oauth.InitOAuth() oauth.InitOAuth()
} }
@ -85,10 +81,6 @@ func settingsHandler(rw http.ResponseWriter, req *http.Request) {
handlefunc(rw, req, SettingsNode) handlefunc(rw, req, SettingsNode)
} }
func initHandler(rw http.ResponseWriter, req *http.Request) {
handlefunc(rw, req, InitNode)
}
func handlefunc(rw http.ResponseWriter, req *http.Request, node int) { func handlefunc(rw http.ResponseWriter, req *http.Request, node int) {
// only allow post requests // only allow post requests
if req.Method != "POST" { if req.Method != "POST" {

View File

@ -1,39 +0,0 @@
package api
import (
"encoding/json"
"openmediacenter/apiGo/database/settings"
"regexp"
"strings"
)
func AddInitHandlers() {
passwordNeeded()
}
func passwordNeeded() {
AddHandler("loadInitialData", InitNode, nil, func() []byte {
sett := settings.LoadSettings()
type InitialDataTypeResponse struct {
DarkMode bool
Pasword bool
MediacenterName string
VideoPath string
}
regexMatchUrl := regexp.MustCompile("^http(|s):\\/\\/([0-9]){1,3}\\.([0-9]){1,3}\\.([0-9]){1,3}\\.([0-9]){1,3}:[0-9]{1,5}")
videoUrl := regexMatchUrl.FindString(sett.VideoPath)
serverVideoPath := strings.TrimPrefix(sett.VideoPath, videoUrl)
res := InitialDataTypeResponse{
DarkMode: sett.DarkMode,
Pasword: sett.Pasword != "-1",
MediacenterName: sett.Mediacenter_name,
VideoPath: serverVideoPath,
}
str, _ := json.Marshal(res)
return str
})
}

View File

@ -1,9 +1,13 @@
package api package api
import ( import (
"encoding/json"
"openmediacenter/apiGo/api/types" "openmediacenter/apiGo/api/types"
"openmediacenter/apiGo/database" "openmediacenter/apiGo/database"
"openmediacenter/apiGo/database/settings"
"openmediacenter/apiGo/videoparser" "openmediacenter/apiGo/videoparser"
"regexp"
"strings"
) )
func AddSettingsHandlers() { func AddSettingsHandlers() {
@ -17,6 +21,30 @@ func getSettingsFromDB() {
result := database.GetSettings() result := database.GetSettings()
return jsonify(result) return jsonify(result)
}) })
AddHandler("loadInitialData", SettingsNode, nil, func() []byte {
sett := settings.LoadSettings()
type InitialDataTypeResponse struct {
DarkMode bool
Pasword bool
MediacenterName string
VideoPath string
}
regexMatchUrl := regexp.MustCompile("^http(|s):\\/\\/([0-9]){1,3}\\.([0-9]){1,3}\\.([0-9]){1,3}\\.([0-9]){1,3}:[0-9]{1,5}")
videoUrl := regexMatchUrl.FindString(sett.VideoPath)
serverVideoPath := strings.TrimPrefix(sett.VideoPath, videoUrl)
res := InitialDataTypeResponse{
DarkMode: sett.DarkMode,
Pasword: sett.Pasword != "-1",
MediacenterName: sett.Mediacenter_name,
VideoPath: serverVideoPath,
}
str, _ := json.Marshal(res)
return str
})
} }
func saveSettingsToDB() { func saveSettingsToDB() {

View File

@ -30,7 +30,6 @@ func main() {
api.AddSettingsHandlers() api.AddSettingsHandlers()
api.AddTagHandlers() api.AddTagHandlers()
api.AddActorsHandlers() api.AddActorsHandlers()
api.AddInitHandlers()
// add the static files // add the static files
static.ServeStaticFiles() static.ServeStaticFiles()

View File

@ -9,7 +9,7 @@ import style from './App.module.css';
import SettingsPage from './pages/SettingsPage/SettingsPage'; import SettingsPage from './pages/SettingsPage/SettingsPage';
import CategoryPage from './pages/CategoryPage/CategoryPage'; import CategoryPage from './pages/CategoryPage/CategoryPage';
import {APINode, apiTokenValid, callApiUnsafe, refreshAPIToken} from './utils/Api'; import {APINode, apiTokenValid, callAPI, refreshAPIToken} from './utils/Api';
import {BrowserRouter as Router, NavLink, Route, Switch} from 'react-router-dom'; import {BrowserRouter as Router, NavLink, Route, Switch} from 'react-router-dom';
import Player from './pages/Player/Player'; import Player from './pages/Player/Player';
@ -75,7 +75,7 @@ class App extends React.Component<{}, state> {
initialAPICall(): void { initialAPICall(): void {
// this is the first api call so if it fails we know there is no connection to backend // this is the first api call so if it fails we know there is no connection to backend
callApiUnsafe(APINode.Init, {action: 'loadInitialData'}, (result: SettingsTypes.initialApiCallData) => { callAPI(APINode.Settings, {action: 'loadInitialData'}, (result: SettingsTypes.initialApiCallData) => {
// set theme // set theme
GlobalInfos.enableDarkTheme(result.DarkMode); GlobalInfos.enableDarkTheme(result.DarkMode);
@ -99,7 +99,15 @@ class App extends React.Component<{}, state> {
if (this.state.password === true) { if (this.state.password === true) {
// render authentication page if auth is neccessary // render authentication page if auth is neccessary
return <AuthenticationPage onSuccessLogin={(): void => this.setState({password: false})} />; return (
<AuthenticationPage
onSuccessLogin={(): void => {
this.setState({password: false});
// reinit general infos
this.initialAPICall();
}}
/>
);
} else if (this.state.password === false) { } else if (this.state.password === false) {
return ( return (
<Router> <Router>

View File

@ -9,7 +9,6 @@ interface Props<T> {
interface state<T> { interface state<T> {
loadeditems: T[]; loadeditems: T[];
selectionnr: number;
} }
/** /**
@ -24,8 +23,7 @@ class DynamicContentContainer<T> extends React.Component<Props<T>, state<T>> {
super(props); super(props);
this.state = { this.state = {
loadeditems: [], loadeditems: []
selectionnr: 0
}; };
} }
@ -35,6 +33,22 @@ class DynamicContentContainer<T> extends React.Component<Props<T>, state<T>> {
this.loadPreviewBlock(this.props.initialLoadNr ? this.props.initialLoadNr : 16); this.loadPreviewBlock(this.props.initialLoadNr ? this.props.initialLoadNr : 16);
} }
componentDidUpdate(prevProps: Props<T>): void {
// when source props change force update!
if (prevProps.data.length !== this.props.data.length) {
this.clean();
this.loadPreviewBlock(this.props.initialLoadNr ? this.props.initialLoadNr : 16);
}
}
/**
* clear all elements rendered...
*/
clean(): void {
this.loadindex = 0;
this.setState({loadeditems: []});
}
render(): JSX.Element { render(): JSX.Element {
return ( return (
<div className={style.maincontent}> <div className={style.maincontent}>

View File

@ -3,17 +3,18 @@ import style from './Preview.module.css';
import {Spinner} from 'react-bootstrap'; import {Spinner} from 'react-bootstrap';
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import GlobalInfos from '../../utils/GlobalInfos'; import GlobalInfos from '../../utils/GlobalInfos';
import {APINode, callAPIPlain} from '../../utils/Api';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faPhotoVideo} from '@fortawesome/free-solid-svg-icons'; import {faPhotoVideo} from '@fortawesome/free-solid-svg-icons';
interface PreviewProps { interface PreviewProps {
name: string; name: string;
movieId: number; picLoader: (callback: (pic: string) => void) => void;
linkPath?: string;
onClick?: () => void;
} }
interface PreviewState { interface PreviewState {
previewpicture: string | null; picLoaded: boolean | null;
} }
/** /**
@ -21,49 +22,61 @@ interface PreviewState {
* floating side by side * floating side by side
*/ */
class Preview extends React.Component<PreviewProps, PreviewState> { class Preview extends React.Component<PreviewProps, PreviewState> {
// store the picture to display
pic?: string;
constructor(props: PreviewProps) { constructor(props: PreviewProps) {
super(props); super(props);
this.state = { this.state = {
previewpicture: null picLoaded: null
}; };
} }
componentDidMount(): void { componentDidMount(): void {
callAPIPlain(APINode.Video, {action: 'readThumbnail', movieid: this.props.movieId}, (result) => { this.props.picLoader((result) => {
this.pic = result;
this.setState({ this.setState({
previewpicture: result picLoaded: result !== ''
}); });
}); });
} }
render(): JSX.Element { render(): JSX.Element {
if (this.props.linkPath !== undefined) {
return <Link to={this.props.linkPath}>{this.content()}</Link>;
} else {
return this.content();
}
}
content(): JSX.Element {
const themeStyle = GlobalInfos.getThemeStyle(); const themeStyle = GlobalInfos.getThemeStyle();
return ( return (
<Link to={'/player/' + this.props.movieId}> <div
<div className={style.videopreview + ' ' + themeStyle.secbackground + ' ' + themeStyle.preview}> className={style.videopreview + ' ' + themeStyle.secbackground + ' ' + themeStyle.preview}
<div className={style.previewtitle + ' ' + themeStyle.lighttextcolor}>{this.props.name}</div> onClick={this.props.onClick}>
<div className={style.previewpic}> <div className={style.previewtitle + ' ' + themeStyle.lighttextcolor}>{this.props.name}</div>
{this.state.previewpicture === '' ? ( <div className={style.previewpic}>
<FontAwesomeIcon {this.state.picLoaded === false ? (
style={{ <FontAwesomeIcon
color: 'white', style={{
marginTop: '55px' color: 'white',
}} marginTop: '55px'
icon={faPhotoVideo} }}
size='5x' icon={faPhotoVideo}
/> size='5x'
) : this.state.previewpicture === null ? ( />
<span className={style.loadAnimation}> ) : this.state.picLoaded === null ? (
<Spinner animation='border' /> <span className={style.loadAnimation}>
</span> <Spinner animation='border' />
) : ( </span>
<img className={style.previewimage} src={this.state.previewpicture} alt='Pic loading.' /> ) : (
)} <img className={style.previewimage} src={this.pic} alt='Pic loading.' />
</div> )}
<div className={style.previewbottom} />
</div> </div>
</Link> <div className={style.previewbottom} />
</div>
); );
} }
} }

View File

@ -2,6 +2,7 @@ import React from 'react';
import Preview from '../Preview/Preview'; import Preview from '../Preview/Preview';
import {VideoTypes} from '../../types/ApiTypes'; import {VideoTypes} from '../../types/ApiTypes';
import DynamicContentContainer from '../DynamicContentContainer/DynamicContentContainer'; import DynamicContentContainer from '../DynamicContentContainer/DynamicContentContainer';
import {APINode, callAPIPlain} from '../../utils/Api';
interface Props { interface Props {
data: VideoTypes.VideoUnloadedType[]; data: VideoTypes.VideoUnloadedType[];
@ -11,7 +12,23 @@ interface Props {
const VideoContainer = (props: Props): JSX.Element => { const VideoContainer = (props: Props): JSX.Element => {
return ( return (
<DynamicContentContainer <DynamicContentContainer
renderElement={(el): JSX.Element => <Preview key={el.MovieId} name={el.MovieName} movieId={el.MovieId} />} renderElement={(el): JSX.Element => (
<Preview
key={el.MovieId}
picLoader={(callback: (pic: string) => void): void => {
callAPIPlain(
APINode.Video,
{
action: 'readThumbnail',
movieid: el.MovieId
},
(result) => callback(result)
);
}}
name={el.MovieName}
linkPath={'/player/' + el.MovieId}
/>
)}
data={props.data}> data={props.data}>
{props.children} {props.children}
</DynamicContentContainer> </DynamicContentContainer>

View File

@ -4,9 +4,10 @@ import {ActorType} from '../../types/VideoTypes';
import ActorTile from '../../elements/ActorTile/ActorTile'; import ActorTile from '../../elements/ActorTile/ActorTile';
import PageTitle from '../../elements/PageTitle/PageTitle'; import PageTitle from '../../elements/PageTitle/PageTitle';
import SideBar from '../../elements/SideBar/SideBar'; import SideBar from '../../elements/SideBar/SideBar';
import style from './ActorOverviewPage.module.css'; // import style from './ActorOverviewPage.module.css';
import {Button} from '../../elements/GPElements/Button'; import {Button} from '../../elements/GPElements/Button';
import NewActorPopup from '../../elements/Popups/NewActorPopup/NewActorPopup'; import NewActorPopup from '../../elements/Popups/NewActorPopup/NewActorPopup';
import DynamicContentContainer from '../../elements/DynamicContentContainer/DynamicContentContainer';
interface Props {} interface Props {}
@ -36,11 +37,12 @@ class ActorOverviewPage extends React.Component<Props, state> {
<SideBar> <SideBar>
<Button title='Add Actor' onClick={(): void => this.setState({NActorPopupVisible: true})} /> <Button title='Add Actor' onClick={(): void => this.setState({NActorPopupVisible: true})} />
</SideBar> </SideBar>
<div className={style.container}> <DynamicContentContainer
{this.state.actors.map((el) => ( renderElement={(el): JSX.Element => <ActorTile key={el.ActorId} actor={el} />}
<ActorTile key={el.ActorId} actor={el} /> data={this.state.actors}
))} initialLoadNr={36}
</div> />
{this.state.NActorPopupVisible ? ( {this.state.NActorPopupVisible ? (
<NewActorPopup <NewActorPopup
onHide={(): void => { onHide={(): void => {

View File

@ -48,7 +48,7 @@ export class ActorPage extends React.Component<Props, state> {
</div> </div>
<SideBarTitle>Attention: This is an early preview!</SideBarTitle> <SideBarTitle>Attention: This is an early preview!</SideBarTitle>
</SideBar> </SideBar>
{this.state.data.length !== 0 ? <VideoContainer data={this.state.data} /> : <div>No Data found!</div>} <VideoContainer data={this.state.data} />
</> </>
); );
} }

View File

@ -53,19 +53,15 @@ class TagView extends React.Component<Props, TagViewState> {
Add a new Tag! Add a new Tag!
</button> </button>
</SideBar> </SideBar>
{this.state.loadedtags.length !== 0 ? ( <DynamicContentContainer
<DynamicContentContainer data={this.state.loadedtags}
data={this.state.loadedtags} renderElement={(m): JSX.Element => (
renderElement={(m): JSX.Element => ( <Link to={'/categories/' + m.TagId} key={m.TagId}>
<Link to={'/categories/' + m.TagId} key={m.TagId}> <TagPreview name={m.TagName} />
<TagPreview name={m.TagName} /> </Link>
</Link> )}
)} initialLoadNr={20}
initialLoadNr={20} />
/>
) : (
'loading'
)}
{this.handlePopups()} {this.handlePopups()}
</> </>
); );

View File

@ -157,7 +157,7 @@ export class HomePage extends React.Component<Props, state> {
}} }}
/> />
</SideBar> </SideBar>
{this.state.data.length !== 0 ? <VideoContainer data={this.state.data} /> : <div>No Data found!</div>} <VideoContainer data={this.state.data} />
<div className={style.rightinfo} /> <div className={style.rightinfo} />
</Route> </Route>
</Switch> </Switch>

View File

@ -1,8 +1,13 @@
import React from 'react'; import React from 'react';
import Preview from '../../elements/Preview/Preview';
class TVShowPage extends React.Component { class TVShowPage extends React.Component {
render(): JSX.Element { render(): JSX.Element {
return <>TvShowPage</>; return (
<>
<Preview name='myTestItem' picLoader={(callback): void => callback('')} />
</>
);
} }
} }

View File

@ -279,6 +279,5 @@ export enum APINode {
Settings = 'settings', Settings = 'settings',
Tags = 'tags', Tags = 'tags',
Actor = 'actor', Actor = 'actor',
Video = 'video', Video = 'video'
Init = 'init'
} }