Merge branch 'randompage_filter_tags' into 'master'

make tags on random page filterable

See merge request lukas/openmediacenter!61
This commit is contained in:
Lukas Heiligenbrunner 2022-05-05 16:41:46 +00:00
commit 3d845aaf04
3 changed files with 111 additions and 60 deletions

View File

@ -108,18 +108,13 @@ Debian_Server:
- Minimize_Frontend - Minimize_Frontend
- Build_Backend - Build_Backend
Test_Server: .Test_Server_Common:
stage: deploy stage: deploy
image: luki42/ssh:latest image: luki42/ssh:latest
needs: needs:
- Frontend_Tests - Frontend_Tests
- Backend_Tests - Backend_Tests
- Debian_Server - Debian_Server
rules:
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH # run this always on default branch
- if: $CI_COMMIT_REF_NAME != $CI_DEFAULT_BRANCH # allow triggering this manually
when: manual
allow_failure: true
script: script:
- eval $(ssh-agent -s) - eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | ssh-add - - echo "$SSH_PRIVATE_KEY" | ssh-add -
@ -129,18 +124,23 @@ Test_Server:
- ssh root@192.168.0.42 "DEBIAN_FRONTEND=noninteractive apt-get --reinstall -y -qq install /tmp/OpenMediaCenter-*.deb && rm /tmp/OpenMediaCenter-*.deb" - ssh root@192.168.0.42 "DEBIAN_FRONTEND=noninteractive apt-get --reinstall -y -qq install /tmp/OpenMediaCenter-*.deb && rm /tmp/OpenMediaCenter-*.deb"
allow_failure: true allow_failure: true
Test_Server_2: Test_Server_CD:
extends: .Test_Server_Common
only:
refs:
- master
Test_Server_MANUAL:
extends: .Test_Server_Common
when: manual
.Test_Server_2_Common:
stage: deploy stage: deploy
image: luki42/ssh:latest image: luki42/ssh:latest
needs: needs:
- Frontend_Tests - Frontend_Tests
- Backend_Tests - Backend_Tests
- Debian_Server - Debian_Server
rules:
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH # run this always on default branch
- if: $CI_COMMIT_REF_NAME != $CI_DEFAULT_BRANCH # allow triggering this manually
when: manual
allow_failure: true
script: script:
- eval $(ssh-agent -s) - eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY_2" | ssh-add - - echo "$SSH_PRIVATE_KEY_2" | ssh-add -
@ -150,3 +150,12 @@ Test_Server_2:
- ssh root@192.168.0.209 "DEBIAN_FRONTEND=noninteractive apt-get --reinstall -y -qq install /tmp/OpenMediaCenter-*.deb && rm /tmp/OpenMediaCenter-*.deb" - ssh root@192.168.0.209 "DEBIAN_FRONTEND=noninteractive apt-get --reinstall -y -qq install /tmp/OpenMediaCenter-*.deb && rm /tmp/OpenMediaCenter-*.deb"
allow_failure: true allow_failure: true
Test_Server_2_CD:
extends: .Test_Server_2_Common
only:
refs:
- master
Test_Server_2_MANUAL:
extends: .Test_Server_2_Common
when: manual

View File

@ -1,6 +1,7 @@
package api package api
import ( import (
"encoding/json"
"fmt" "fmt"
"net/url" "net/url"
"openmediacenter/apiGo/api/api" "openmediacenter/apiGo/api/api"
@ -9,6 +10,7 @@ import (
"openmediacenter/apiGo/database" "openmediacenter/apiGo/database"
"os" "os"
"strconv" "strconv"
"strings"
) )
func addVideoHandlers() { func addVideoHandlers() {
@ -162,7 +164,8 @@ func getVideoHandlers() {
*/ */
api.AddHandler("getRandomMovies", api.VideoNode, api.PermUser, func(context api.Context) { api.AddHandler("getRandomMovies", api.VideoNode, api.PermUser, func(context api.Context) {
var args struct { var args struct {
Number int Number int
TagFilter []uint32
} }
if api.DecodeRequest(context.GetRequest(), &args) != nil { if api.DecodeRequest(context.GetRequest(), &args) != nil {
context.Text("unable to decode request") context.Text("unable to decode request")
@ -174,34 +177,52 @@ func getVideoHandlers() {
Videos []types.VideoUnloadedType Videos []types.VideoUnloadedType
} }
query := fmt.Sprintf("SELECT movie_id,movie_name FROM videos ORDER BY RAND() LIMIT %d", args.Number) whereclause := "WHERE 1"
result.Videos = readVideosFromResultset(database.Query(query)) if len(args.TagFilter) > 0 {
d, _ := json.Marshal(args.TagFilter)
vals := strings.Trim(string(d), "[]")
var ids string whereclause = fmt.Sprintf("WHERE tag_id IN (%s)", vals)
for i := range result.Videos {
ids += "video_tags.video_id=" + strconv.Itoa(result.Videos[i].MovieId)
if i < len(result.Videos)-1 {
ids += " OR "
}
} }
// add the corresponding tags query := fmt.Sprintf(`
query = fmt.Sprintf(`SELECT t.tag_name,t.tag_id FROM video_tags SELECT video_tags.video_id,v.movie_name FROM video_tags join videos v on v.movie_id = video_tags.video_id
%s
group by video_id
ORDER BY RAND()
LIMIT %d`, whereclause, args.Number)
result.Videos = readVideosFromResultset(database.Query(query))
if len(result.Videos) > 0 {
var ids string
for i := range result.Videos {
ids += "video_tags.video_id=" + strconv.Itoa(result.Videos[i].MovieId)
if i < len(result.Videos)-1 {
ids += " OR "
}
}
// add the corresponding tags
query = fmt.Sprintf(`SELECT t.tag_name,t.tag_id FROM video_tags
INNER JOIN tags t on video_tags.tag_id = t.tag_id INNER JOIN tags t on video_tags.tag_id = t.tag_id
WHERE %s WHERE %s
GROUP BY t.tag_id`, ids) GROUP BY t.tag_id`, ids)
rows := database.Query(query) rows := database.Query(query)
if rows != nil {
for rows.Next() { for rows.Next() {
var tag types.Tag var tag types.Tag
err := rows.Scan(&tag.TagName, &tag.TagId) err := rows.Scan(&tag.TagName, &tag.TagId)
if err != nil { if err != nil {
panic(err.Error()) // proper Error handling instead of panic in your app panic(err.Error()) // proper Error handling instead of panic in your app
}
// append to final array
result.Tags = append(result.Tags, tag)
}
} }
// append to final array } else {
result.Tags = append(result.Tags, tag) result.Tags = []types.Tag{}
} }
context.Json(result) context.Json(result)

View File

@ -2,16 +2,21 @@ import React from 'react';
import style from './RandomPage.module.css'; import style from './RandomPage.module.css';
import SideBar, {SideBarTitle} from '../../elements/SideBar/SideBar'; import SideBar, {SideBarTitle} from '../../elements/SideBar/SideBar';
import Tag from '../../elements/Tag/Tag'; import Tag from '../../elements/Tag/Tag';
import PageTitle from '../../elements/PageTitle/PageTitle'; import PageTitle, {Line} from '../../elements/PageTitle/PageTitle';
import VideoContainer from '../../elements/VideoContainer/VideoContainer'; import VideoContainer from '../../elements/VideoContainer/VideoContainer';
import {APINode, callAPI} from '../../utils/Api'; import {APINode, callAPI} from '../../utils/Api';
import {TagType} from '../../types/VideoTypes'; import {TagType} from '../../types/VideoTypes';
import {VideoTypes} from '../../types/ApiTypes'; import {VideoTypes} from '../../types/ApiTypes';
import {addKeyHandler, removeKeyHandler} from '../../utils/ShortkeyHandler'; import {addKeyHandler, removeKeyHandler} from '../../utils/ShortkeyHandler';
import {IconButton} from '../../elements/GPElements/Button';
import {faPlusCircle} from '@fortawesome/free-solid-svg-icons';
import AddTagPopup from '../../elements/Popups/AddTagPopup/AddTagPopup';
interface state { interface state {
videos: VideoTypes.VideoUnloadedType[]; videos: VideoTypes.VideoUnloadedType[];
tags: TagType[]; tags: TagType[];
filterTags: TagType[];
addTagPopup: boolean;
} }
interface GetRandomMoviesType { interface GetRandomMoviesType {
@ -29,8 +34,10 @@ class RandomPage extends React.Component<{}, state> {
super(props); super(props);
this.state = { this.state = {
addTagPopup: false,
videos: [], videos: [],
tags: [] tags: [],
filterTags: []
}; };
this.keypress = this.keypress.bind(this); this.keypress = this.keypress.bind(this);
@ -56,42 +63,56 @@ class RandomPage extends React.Component<{}, state> {
{this.state.tags.map((m) => ( {this.state.tags.map((m) => (
<Tag key={m.TagId} tagInfo={m} /> <Tag key={m.TagId} tagInfo={m} />
))} ))}
<Line />
<SideBarTitle>Filter Tags:</SideBarTitle>
{this.state.filterTags.map((m) => (
<Tag key={m.TagId} tagInfo={m} />
))}
<IconButton
title='Add'
icon={faPlusCircle}
onClick={(): void => {
this.setState({addTagPopup: true});
}}
/>
</SideBar> </SideBar>
{this.state.videos.length !== 0 ? ( {this.state.videos.length !== 0 ? <VideoContainer data={this.state.videos} /> : <div>No Data found!</div>}
<VideoContainer data={this.state.videos}> <div className={style.Shufflebutton}>
<div className={style.Shufflebutton}> <button onClick={(): void => this.loadShuffledvideos(this.LoadNR)} className={style.btnshuffle}>
<button onClick={(): void => this.shuffleclick()} className={style.btnshuffle}> Shuffle
Shuffle </button>
</button> </div>
</div> {this.state.addTagPopup ? (
</VideoContainer> <AddTagPopup
) : ( onHide={(): void => this.setState({addTagPopup: false})}
<div>No Data found!</div> submit={(tagId: number, tagName: string): void => {
)} this.setState({filterTags: [...this.state.filterTags, {TagId: tagId, TagName: tagName}]}, (): void => {
this.loadShuffledvideos(this.LoadNR);
});
}}
/>
) : null}
</div> </div>
); );
} }
/**
* click handler for shuffle btn
*/
shuffleclick(): void {
this.loadShuffledvideos(this.LoadNR);
}
/** /**
* load random videos from backend * load random videos from backend
* @param nr number of videos to load * @param nr number of videos to load
*/ */
loadShuffledvideos(nr: number): void { loadShuffledvideos(nr: number): void {
callAPI<GetRandomMoviesType>(APINode.Video, {action: 'getRandomMovies', Number: nr}, (result) => { callAPI<GetRandomMoviesType>(
this.setState({videos: []}); // needed to trigger rerender of main videoview APINode.Video,
this.setState({ {action: 'getRandomMovies', Number: nr, TagFilter: this.state.filterTags.map((t) => t.TagId)},
videos: result.Videos, (result) => {
tags: result.Tags this.setState({videos: []}); // needed to trigger rerender of main videoview
}); this.setState({
}); videos: result.Videos,
tags: result.Tags
});
}
);
} }
/** /**