Browse Source

commit: add bs-viewer and improved search

HOME 4 years atrás
parent
commit
1f3b08b019

+ 1 - 0
public/electron.js

@@ -31,6 +31,7 @@ ipcMain.on('launch-events', (_, event, message) => {
   switch(event) {
     case 'check-launch-events':
       mainWindow.webContents.send('launch-events', 'launch-events', launchEvents)
+      mainWindow.webContents.openDevTools();
       launchEvents = {
         songs: {
           details: [],

+ 96 - 5
src/actions/songListActions.js

@@ -1,18 +1,74 @@
-import { FETCH_NEW, FETCH_TOP_DOWNLOADS, FETCH_TOP_FINISHED, FETCH_LOCAL_SONGS, ADD_BSABER_RATING, SET_SCROLLTOP, SET_LOADING, SET_LOADING_MORE, LOAD_MORE, SET_RESOURCE, DISPLAY_WARNING } from './types'
+import { FETCH_NEW, FETCH_TOP_DOWNLOADS, FETCH_TOP_FINISHED, FETCH_SEARCH, FETCH_LOCAL_SONGS, ADD_BSABER_RATING, SET_SCROLLTOP, SET_LOADING, SET_LOADING_MORE, LOAD_MORE, SET_RESOURCE, DISPLAY_WARNING } from './types'
 import { SONG_LIST } from '../constants/views'
 import { BEATSAVER, LIBRARY } from '../constants/resources'
 import { BEATSAVER_BASE_URL, BSABER_BASE_URL } from '../constants/urls'
 import { hashAndWriteToMetadata } from './queueActions'
 import { setView } from './viewActions'
+import { stat } from 'original-fs'
 
 const { remote } = window.require('electron')
 const fs = remote.require('fs')
 const path = remote.require('path')
 
+var SEARCH_KEYWORD = "tv-size";
+var RESULT_PAGE=0;
+
 const resourceUrl = {
     'BEATSAVER_NEW_SONGS': `${BEATSAVER_BASE_URL}/api/maps/latest`,
     'BEATSAVER_TOP_DOWNLOADED_SONGS': `${BEATSAVER_BASE_URL}/api/maps/downloads`,
-    'BEATSAVER_TOP_FINISHED_SONGS': `${BEATSAVER_BASE_URL}/api/maps/plays`
+    'BEATSAVER_TOP_FINISHED_SONGS': `${BEATSAVER_BASE_URL}/api/maps/plays`,
+    'BEATSAVER_SEARCH_SONGS':`${BEATSAVER_BASE_URL}/api/search/text`
+}
+
+export const fetchSearch = () => (dispatch, getState) => {
+  setView(SONG_LIST)(dispatch, getState)
+  dispatch({
+    type: SET_LOADING,
+    payload: true
+  })
+  dispatch({
+    type: SET_SCROLLTOP,
+    payload: 0
+  })
+  dispatch({
+    type: SET_RESOURCE,
+    payload: BEATSAVER.SEARCH_SONGS
+  })
+
+  RESULT_PAGE=0;
+  SEARCH_KEYWORD = document.getElementById("SearchSongsInput").value.trim() === "" 
+    ? "tv-size" : document.getElementById("SearchSongsInput").value.trim() ;
+
+  fetch(`${BEATSAVER_BASE_URL}/api/search/text/0?q=${SEARCH_KEYWORD}`)
+    .then(res => res.json())
+    .then(data =>  {
+      console.log(data)
+      dispatch({
+        type: FETCH_SEARCH,
+        payload:  data
+      })
+      dispatch({
+        type: SET_LOADING,
+        payload: false
+      })
+      console.log(data);
+      for(let i = 0; i < data.docs.length; i++) {
+        fetch(`${BSABER_BASE_URL}/wp-json/bsaber-api/songs/${data.docs[i].key}/ratings`)
+        .then(res => res.json())
+        .then(bsaberData => {
+          dispatch({
+            type: ADD_BSABER_RATING,
+            payload: { i, bsaberData }
+          })
+        })
+        .catch((err) => {
+          dispatch({
+            type: ADD_BSABER_RATING,
+            payload: { i, bsaberData: { overall_rating: 'Error' } }
+          })
+        })
+      }
+    })
 }
 
 export const fetchNew = () => (dispatch, getState) => {
@@ -29,6 +85,9 @@ export const fetchNew = () => (dispatch, getState) => {
     type: SET_RESOURCE,
     payload: BEATSAVER.NEW_SONGS
   })
+
+  RESULT_PAGE=0;
+
   fetch(`${BEATSAVER_BASE_URL}/api/maps/latest`)
     .then(res => res.json())
     .then(data =>  {
@@ -75,6 +134,9 @@ export const fetchTopDownloads = () => (dispatch, getState) => {
     type: SET_RESOURCE,
     payload: BEATSAVER.TOP_DOWNLOADED_SONGS
   })
+
+  RESULT_PAGE=0;
+
   fetch(`${BEATSAVER_BASE_URL}/api/maps/downloads`)
     .then(res => res.json())
     .then(data => {
@@ -86,7 +148,7 @@ export const fetchTopDownloads = () => (dispatch, getState) => {
         type: SET_LOADING,
         payload: false
       })
-      for(let i = 0; i < data.songs.length; i++) {
+      for(let i = 0; i < data.docs.length; i++) {
         fetch(`${BSABER_BASE_URL}/wp-json/bsaber-api/songs/${data.docs[i].key}/ratings`)
         .then(res => res.json())
         .then(bsaberData => {
@@ -119,6 +181,9 @@ export const fetchTopFinished = () => (dispatch, getState) => {
     type: SET_RESOURCE,
     payload: BEATSAVER.TOP_FINISHED_SONGS
   })
+
+  RESULT_PAGE=0;
+
   fetch(`${BEATSAVER_BASE_URL}/api/maps/plays`)
     .then(res => res.json())
     .then(data => {
@@ -211,16 +276,42 @@ export const fetchLocalSongs = () => (dispatch, getState) => {
 }
 
 export const loadMore = () => (dispatch, getState) => {
+
+  if(RESULT_PAGE<0) return;
+
   dispatch({
     type: SET_LOADING_MORE,
     payload: true
   })
   let state = getState()
-  fetch(resourceUrl[state.resource] + '/' + state.songs.songs.length)
+
+  let url = state.resource === "BEATSAVER_SEARCH_SONGS" 
+    ? resourceUrl[state.resource] + '/' + (++RESULT_PAGE) + `?q=${SEARCH_KEYWORD}`
+    : resourceUrl[state.resource] + '/' + (++RESULT_PAGE);
+
+  fetch(url)
     .then(res => {
       return res.json()
     })
     .then(data => {
+      if(data.docs.length===0){
+        dispatch({
+          type: SET_LOADING_MORE,
+          payload: false
+        })
+
+        dispatch({
+          type: DISPLAY_WARNING,
+          payload: {
+            text: 'No more'
+          }
+        })   
+
+        RESULT_PAGE=-1;
+
+        return;
+      }
+
       dispatch({
         type: LOAD_MORE,
         payload: data
@@ -229,7 +320,7 @@ export const loadMore = () => (dispatch, getState) => {
         type: SET_LOADING_MORE,
         payload: false
       })
-      console.log(data)
+
       for(let i = state.songs.songs.length; i < state.songs.songs.length + data.docs.length; i++) {
         fetch(`${BSABER_BASE_URL}/wp-json/bsaber-api/songs/${data.docs[i - state.songs.songs.length].key}/ratings`)
         .then(res => res.json())

+ 1 - 0
src/actions/types.js

@@ -5,6 +5,7 @@ export const RESET_APP                  = 'RESET_APP'
 export const FETCH_TOP_DOWNLOADS        = 'FETCH_TOP_DOWNLOADS'
 export const FETCH_TOP_FINISHED         = 'FETCH_TOP_FINISHED'
 export const FETCH_NEW                  = 'FETCH_NEW'
+export const FETCH_SEARCH               = 'FETCH_SEARCH'
 export const FETCH_LOCAL_SONGS          = 'FETCH_LOCAL_SONGS'
 export const FETCH_LOCAL_PLAYLISTS      = 'FETCH_LOCAL_PLAYLISTS'
 export const REFRESH                    = 'REFRESH'

+ 5 - 2
src/components/SideBar.js

@@ -7,7 +7,7 @@ import PropTypes from 'prop-types'
 import Badge from './Badge'
 
 import { setView } from '../actions/viewActions'
-import { fetchNew, fetchTopDownloads, fetchTopFinished, fetchLocalSongs } from '../actions/songListActions'
+import { fetchNew, fetchTopDownloads, fetchSearch, fetchTopFinished, fetchLocalSongs } from '../actions/songListActions'
 import { fetchApprovedMods, fetchRecommendedMods, fetchModCategories, fetchLocalMods, fetchActivatedMods, checkInstalledMods } from '../actions/modActions'
 import { fetchLocalPlaylists } from '../actions/playlistsActions'
 import { setResource } from '../actions/sourceActions'
@@ -34,6 +34,8 @@ class SideBar extends Component {
             <li title={ `New Songs${this.props.offlineMode ? ' (Not available in offline mode)' : ''}` } className={ `fetch-new-songs${this.props.view === VIEWS.SONG_LIST && this.props.resource === RESOURCES.BEATSAVER.NEW_SONGS ? ' selected' : ''}${this.props.offlineMode ? ' disabled' : ''}` } onClick={ this.props.offlineMode ? null : this.props.fetchNew }>New Songs</li>
             <li title={ `Top Finished${this.props.offlineMode ? ' (Not available in offline mode)' : ''}` } className={ `fetch-top-finished ${this.props.view === VIEWS.SONG_LIST  && this.props.resource === RESOURCES.BEATSAVER.TOP_FINISHED_SONGS ? 'selected' : ''}${this.props.offlineMode ? ' disabled' : ''}` } onClick={ this.props.offlineMode ? null : this.props.fetchTopFinished }>Top Finished</li>
             <li title={ `Top Downloaded${this.props.offlineMode ? ' (Not available in offline mode)' : ''}` } className={ `fetch-top-downloaded ${this.props.view === VIEWS.SONG_LIST && this.props.resource === RESOURCES.BEATSAVER.TOP_DOWNLOADED_SONGS ? 'selected' : ''}${this.props.offlineMode ? ' disabled' : ''}` } onClick={ this.props.offlineMode ? null : this.props.fetchTopDownloads }>Top Downloaded</li>
+            <li title={ `Search Songs${this.props.offlineMode ? ' (Not available in offline mode)' : ''}` } className={ `search-songs-input ${this.props.view === VIEWS.SONG_LIST && this.props.resource === RESOURCES.BEATSAVER.SEARCH_SONGS ? 'selected' : ''}${this.props.offlineMode ? ' disabled' : ''}` } ><input id="SearchSongsInput" placeholder="tv-size"/></li>
+            <li title={ `Search Songs${this.props.offlineMode ? ' (Not available in offline mode)' : ''}` } className={ `search-songs ${this.props.view === VIEWS.SONG_LIST && this.props.resource === RESOURCES.BEATSAVER.SEARCH_SONGS ? 'selected' : ''}${this.props.offlineMode ? ' disabled' : ''}` } onClick={ this.props.offlineMode ? null : this.props.fetchSearch }>Search Songs</li>
           </ul>
         </div>
         <h4 title={ `Mods` } className={ `section section-mods${this.props.section === SECTIONS.MODS ? ' selected' : ''}` } onClick={ () => { this.props.setSection(SECTIONS.MODS); if(!this.props.offlineMode) { this.props.fetchApprovedMods() } else { this.props.fetchLocalMods() } } }>MODS</h4>
@@ -64,6 +66,7 @@ class SideBar extends Component {
 SideBar.propTypes = {
   fetchNew: PropTypes.func.isRequired,
   fetchTopDownloads: PropTypes.func.isRequired,
+  fetchSearch:PropTypes.func.isRequired,
   fetchTopFinished: PropTypes.func.isRequired,
   fetchLocalSongs: PropTypes.func.isRequired,
   fetchLocalPlaylists: PropTypes.func.isRequired,
@@ -87,7 +90,7 @@ const mapStateToProps = state => ({
   updates: state.mods.updates
 })
 
-export default connect(mapStateToProps, { fetchNew, fetchTopDownloads, fetchTopFinished, fetchLocalSongs, fetchLocalPlaylists, fetchApprovedMods, fetchLocalMods, fetchActivatedMods, fetchRecommendedMods, fetchModCategories, setResource, resizeSidebar, setSection, checkInstalledMods, setView })(SideBar)
+export default connect(mapStateToProps, { fetchNew, fetchTopDownloads, fetchTopFinished, fetchSearch, fetchLocalSongs, fetchLocalPlaylists, fetchApprovedMods, fetchLocalMods, fetchActivatedMods, fetchRecommendedMods, fetchModCategories, setResource, resizeSidebar, setSection, checkInstalledMods, setView })(SideBar)
 
 /*
 <li className={ `library-conflicted-mods${this.props.view === MODS_VIEW && this.props.resource === RESOURCES.LIBRARY.MODS.CONFLICTS ? ' selected' : ''}` }>Mod Packs</li>

+ 2 - 1
src/components/SongDetails.js

@@ -205,7 +205,8 @@ class SongDetails extends Component {
             <Description details={ this.props.details } />
             <Uploader details={ this.props.details } />
             <Difficulties difficulties={ this.props.details.difficultyLevels || this.props.details._difficultyBeatmapSets || this.props.details.metadata.difficulties } />
-            <div className="preview"><b>Preview:</b><br /><audio id="preview" src={ this.props.details.audioSource } controls controlsList="nodownload" /></div>
+            <div className="preview"><audio id="preview" src={ this.props.details.audioSource } controls controlsList="nodownload" /></div>
+            <div className="bs-viewer"><iframe class="bs-viewer-frame" title="wtf" width="1000" height="720" src={"https://skystudioapps.com/bs-viewer/?id="+this.props.details.key}></iframe></div>
           </div>
           <BeatSaver details={ this.props.details } />
           <PlaylistPicker song={ this.props.details } />

+ 2 - 1
src/constants/resources.js

@@ -11,7 +11,8 @@ export const LIBRARY = {
 export const BEATSAVER = {
   NEW_SONGS: 'BEATSAVER_NEW_SONGS',
   TOP_FINISHED_SONGS: 'BEATSAVER_TOP_FINISHED_SONGS',
-  TOP_DOWNLOADED_SONGS: 'BEATSAVER_TOP_DOWNLOADED_SONGS'
+  TOP_DOWNLOADED_SONGS: 'BEATSAVER_TOP_DOWNLOADED_SONGS',
+  SEARCH_SONGS:'BEATSAVER_SEARCH_SONGS'
 }
 
 export const BEATMODS = {

+ 28 - 0
src/css/SideBar.scss

@@ -299,6 +299,20 @@
         }
       }
 
+      &.search-songs {
+        &::before {
+          //TODO Fix asset names
+          background-image: url('../assets/search.png');
+        }
+      }
+
+      &.search-songs-input {
+        &::before {
+          //TODO Fix asset names
+          background-image: url('../assets/text.png');
+        }
+      }
+
       &.fetch-local-songs {
         &::before {
           background-image: url('../assets/song-filled.png');
@@ -499,6 +513,20 @@
         }
       }
 
+      &.search-songs {
+        &::before {
+          //TODO Fix asset names
+          background-image: url('../assets/dark/search.png');
+        }
+      }
+
+      &.search-songs-input {
+        &::before {
+          //TODO Fix asset names
+          background-image: url('../assets/dark/text.png');
+        }
+      }
+
       &.fetch-local-songs {
         &::before {
           background-image: url('../assets/dark/song-filled.png');

+ 4 - 0
src/css/SongDetails.scss

@@ -1,6 +1,10 @@
 //TODO Clean up this component A LOT. Could definitly use Modal in multiple places.
 @import './StyleConstants';
 
+.bs-viewer-frame{
+  border: none;
+}
+
 #song-details {
   display: flex;
   flex-flow: row nowrap;

+ 2 - 1
src/reducers/songListReducer.js

@@ -1,4 +1,4 @@
-import { FETCH_NEW, FETCH_TOP_DOWNLOADS, FETCH_TOP_FINISHED, LOAD_MORE, REFRESH, ADD_BSABER_RATING, FETCH_LOCAL_SONGS, SET_DOWNLOADED_SONGS, SET_SCROLLTOP, SET_DOWNLOADING_COUNT, SET_WAIT_LIST, SET_SCANNING_FOR_SONGS, SET_DISCOVERED_FILES, SET_PROCESSED_FILES } from '../actions/types'
+import { FETCH_NEW, FETCH_TOP_DOWNLOADS, FETCH_TOP_FINISHED, FETCH_SEARCH, LOAD_MORE, REFRESH, ADD_BSABER_RATING, FETCH_LOCAL_SONGS, SET_DOWNLOADED_SONGS, SET_SCROLLTOP, SET_DOWNLOADING_COUNT, SET_WAIT_LIST, SET_SCANNING_FOR_SONGS, SET_DISCOVERED_FILES, SET_PROCESSED_FILES } from '../actions/types'
 
 const initialState = {
   songs: [],
@@ -15,6 +15,7 @@ const initialState = {
 export default function(state = initialState, action) {
   switch(action.type) {
     case FETCH_NEW:
+    case FETCH_SEARCH:
     case FETCH_TOP_DOWNLOADS:
     case FETCH_TOP_FINISHED:
     case REFRESH: