Selaa lähdekoodia

merge beta to stable

StarGazer1258 5 vuotta sitten
vanhempi
commit
eca4007749

+ 1 - 1
package-lock.json

@@ -1,6 +1,6 @@
 {
   "name": "beatdrop",
-  "version": "2.5.2",
+  "version": "2.5.5",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {

+ 1 - 1
package.json

@@ -2,7 +2,7 @@
   "name": "beatdrop",
   "description": "A desktop app for downloading Beat Saber songs.",
   "author": "Nathaniel Johns (StarGazer1258)",
-  "version": "2.5.2",
+  "version": "2.5.5",
   "private": false,
   "license": "CC-BY-NC-SA-4.0",
   "repository": {

+ 19 - 14
src/actions/detailsActions.js

@@ -2,6 +2,7 @@ import { LOAD_DETAILS, CLEAR_DETAILS, SET_DETAILS_LOADING, SET_VIEW, DISPLAY_WAR
 import { SONG_DETAILS } from '../views'
 
 import AdmZip from 'adm-zip'
+import { hashAndWriteToMetadata } from './queueActions'
 const { remote } = window.require('electron')
 const fs = remote.require('fs')
 const path = remote.require('path')
@@ -10,7 +11,7 @@ const path = remote.require('path')
  * Loads and presents the details page for a song from a file.
  * @param {string} file The path to the song
  */
-export const loadDetailsFromFile = file => dispatch => {
+export const loadDetailsFromFile = file => (dispatch, getState) => {
   dispatch({
     type: CLEAR_DETAILS
   })
@@ -27,19 +28,23 @@ export const loadDetailsFromFile = file => dispatch => {
     let details = JSON.parse(data)
     let dir = path.dirname(file)
     details.coverURL = `file://${ path.join(dir, (details.coverImagePath || details._coverImageFilename)) }`
-    details.file = path.join(dir, 'info.json' || 'info.dat')
-    dispatch({
-      type: LOAD_DETAILS,
-      payload: details
-    })
-    dispatch({
-      type: LOAD_DETAILS,
-      payload: { audioSource: `file://${ path.join(dir, details._songFilename) }` }
-    })
-    dispatch({
-      type: SET_DETAILS_LOADING,
-      payload: false
-    })
+    details.file = path.join(dir, 'info.dat')
+    hashAndWriteToMetadata(path.join(dir, 'info.dat'))(dispatch, getState)
+      .then(hash => {
+        details.hash = hash
+        dispatch({
+          type: LOAD_DETAILS,
+          payload: details
+        })
+        dispatch({
+          type: LOAD_DETAILS,
+          payload: { audioSource: `file://${ path.join(dir, details._songFilename) }` }
+        })
+        dispatch({
+          type: SET_DETAILS_LOADING,
+          payload: false
+        })
+      })
   })
 }
 

+ 11 - 43
src/actions/playlistsActions.js

@@ -1,11 +1,11 @@
 import { FETCH_LOCAL_PLAYLISTS, LOAD_NEW_PLAYLIST_IMAGE, SET_NEW_PLAYLIST_OPEN, SET_PLAYLIST_PICKER_OPEN, CLEAR_PLAYLIST_DIALOG, LOAD_PLAYLIST_DETAILS, LOAD_PLAYLIST_SONGS, CLEAR_PLAYLIST_DETAILS, SET_PLAYLIST_EDITING, SET_VIEW, SET_LOADING, DISPLAY_WARNING } from './types'
 import { PLAYLIST_LIST, PLAYLIST_DETAILS } from '../views'
 import { defaultPlaylistIcon } from '../b64Assets'
+import { hashAndWriteToMetadata } from './queueActions';
 
 const { remote } = window.require('electron')
 const fs = remote.require('fs')
 const path = remote.require('path')
-const md5 = remote.require('md5')
 
 export const fetchLocalPlaylists = (doSetView) => (dispatch, getState) => {
   let state = getState()
@@ -313,51 +313,19 @@ export const addSongToPlaylist = (song, playlistFile) => (dispatch, getState) =>
       return
     }
     let playlist = JSON.parse(data)
-    if(song.hash || song.hashMd5) {
-      if(song.key) {
-        playlist.songs.push({
-          hash: song.hash || song.hashMd5,
-          key: song.key,
-          songName: song.name || song.songName
-        })
-      } else {
-        playlist.songs.push({
-          hash: song.hash || song.hashMd5,
-          songName: song.name || song.songName
-        })
-      }
+    if(song.hash) {
+      playlist.songs.push({
+        hash: song.hash,
+        songName: song.name || song._songName
+      })
     } else {
-      if(song.file) {
-        let file = song.file
-        delete song.file
-        let to_hash = ''
-        for(let i = 0; i < song.difficultyLevels.length; i++) {
-          try {
-            to_hash += fs.readFileSync(path.join(path.dirname(file), song.difficultyLevels[i].jsonPath), 'UTF8')
-          } catch(err) {
-            dispatch({
-              type: DISPLAY_WARNING,
-              action: { text: 'Error reading difficulty level information, the song\'s files may be corrupt. Try redownloading the song and try again.' }
-            })
-            return
-          }
-        }
-        let hash = md5(to_hash)
-        song.hash = hash
-        fs.writeFile(file, JSON.stringify(song), 'UTF8', (err) => { if(err) return })
-        if(song.key) {
-          playlist.songs.push({
-            hash: hash,
-            key: song.key,
-            songName: song.name || song.songName
-          })
-        } else {
+      hashAndWriteToMetadata(song.file)(dispatch, getState)
+        .then(hash => {
           playlist.songs.push({
-            hash: hash,
-            songName: song.name || song.songName
+            hash,
+            songName: song.name || song._songName
           })
-        }
-      }
+        })
     }
     
     fs.writeFile(playlistFile, JSON.stringify(playlist), 'UTF8', (err) => {

+ 129 - 66
src/actions/queueActions.js

@@ -5,7 +5,7 @@ import { isModInstalled, installEssentialMods } from './modActions';
 const { remote } = window.require('electron')
 const fs = remote.require('fs')
 const path = remote.require('path')
-const md5 = remote.require('md5')
+const crypto = remote.require('crypto')
 const AdmZip = remote.require('adm-zip')
 const request = remote.require('request')
 const rimraf = remote.require('rimraf')
@@ -85,26 +85,12 @@ export const downloadSong = (identity) => (dispatch, getState) => {
                 }
                 let zip = new AdmZip(data)
                 let zipEntries = zip.getEntries()
-                let infoEntry, infoObject
+                let infoEntry
                 for(let i = 0; i < zipEntries.length; i++) {
-                  if(zipEntries[i].entryName.substr(zipEntries[i].entryName.length - 9, 9) === 'info.json' || zipEntries[i].entryName.substr(zipEntries[i].entryName.length - 8, 8) === 'info.dat') {
+                  if(zipEntries[i].entryName.split(path.sep).pop() === 'info.dat') {
                     infoEntry  = zipEntries[i]
                   }
                 }
-                try {
-                  infoObject = JSON.parse(infoEntry.getData().toString('UTF8'))
-                } catch(err) {
-                  dispatch({
-                    type: DISPLAY_WARNING,
-                    payload: {
-                      text: `There was an error unpacking the song "${song.name}." The song's files may be corrupt or use formatting other than UTF-8 (Why UTF-8? The IETF says so! https://tools.ietf.org/html/rfc8259#section-8.1). Please try again and contact the song's uploader, ${song.uploader.username}, if problem persists.`
-                    }
-                  })
-                  return
-                }
-                infoObject.key = song.key
-                infoObject.hash = hash
-                zip.updateFile(infoEntry.entryName, JSON.stringify(infoObject))
                 let extractTo
                 switch(getState().settings.folderStructure) {
                   case 'keySongNameArtistName':
@@ -121,6 +107,42 @@ export const downloadSong = (identity) => (dispatch, getState) => {
                     break
                 }
                 zip.extractAllTo(path.join(getState().settings.installationDirectory, 'Beat Saber_Data', 'CustomLevels', extractTo))
+                let metadataFile = path.join(getState().settings.installationDirectory, 'Beat Saber_Data', 'CustomLevels', extractTo, 'metadata.dat')
+                fs.access(metadataFile, accessErr => {
+                  if(accessErr) {
+                    fs.writeFile(metadataFile, JSON.stringify({ key: song.key, hash: song.hash, downloadTime: utc }), err => {
+                      if(err) {
+                        dispatch({
+                          type: DISPLAY_WARNING,
+                          payload: {
+                            text: `Failed to write metadata file for ${ song.name }. Go to settings and press "Scan for Songs" to try again.`
+                          }
+                        })
+                      }
+                    })
+                    return
+                  }
+                  fs.readFile(metadataFile, 'UTF-8', (err, metadata) => {
+                    if(err) {
+                      dispatch({
+                        type: DISPLAY_WARNING,
+                        payload: {
+                          text: `Failed to read metadata file for ${ song.name }. Go to settings and press "Scan for Songs" to try again.`
+                        }
+                      })
+                    }
+                    fs.writeFile(metadataFile, JSON.stringify({ ...JSON.parse(metadata), key: song.key, hash: song.hash, downloadTime: utc }), err => {
+                      if(err) {
+                        dispatch({
+                          type: DISPLAY_WARNING,
+                          payload: {
+                            text: `Failed to write metadata file for ${ song.name }. Go to settings and press "Scan for Songs" to try again.`
+                          }
+                        })
+                      }
+                    })
+                  })
+                })
                 dispatch({
                   type: SET_DOWNLOADED_SONGS,
                   payload: [...getState().songs.downloadedSongs, { hash, file: path.join(getState().settings.installationDirectory, 'Beat Saber_Data', 'CustomLevels', extractTo, infoEntry.entryName) }]
@@ -393,6 +415,65 @@ export const deleteSong = (identity) => (dispatch, getState) => {
   })
 }
 
+export const hashAndWriteToMetadata = (infoFile) => dispatch => {
+  return new Promise((resolve, reject) => {
+    let metadataFile = path.join(path.dirname(infoFile), 'metadata.dat')
+    fs.readFile(infoFile, { encoding: 'UTF-8' }, (infoReadErr, infoData) => {                             // Read the info.dat file
+      if(infoReadErr) {
+        dispatch({
+          type: DISPLAY_WARNING,
+          payload: {
+            text: `Failed to read info file ${ infoFile }. Go to settings and press "Scan for Songs" to try again.`
+          }
+        })
+        reject(infoReadErr)
+      }
+      let song = JSON.parse(infoData),
+          dataToHash = '',
+          fileToHash,
+          hash
+      fs.readFile(metadataFile, 'UTF-8', (readMetadataErr, metadataData) => {
+        if(readMetadataErr || !JSON.parse(metadataData).hasOwnProperty('hash')) {
+          try {
+            dataToHash += infoData
+            for(let set = 0; set < song._difficultyBeatmapSets.length; set++) {
+              for (let map = 0; map < song._difficultyBeatmapSets[set]._difficultyBeatmaps.length; map++) {
+                fileToHash = path.join(path.dirname(infoFile), song._difficultyBeatmapSets[set]._difficultyBeatmaps[map]._beatmapFilename)
+                dataToHash += fs.readFileSync(fileToHash, 'UTF8')
+              }
+            }
+            hash = crypto.createHash('sha1')                                                  // Calculate song hash
+              .update(dataToHash)
+              .digest('hex')
+          } catch(hashingErr) {
+            dispatch({
+              type: DISPLAY_WARNING,
+              payload: {
+                text: `Failed to calculate hash: ${ fileToHash } could not be accessed.`
+              }
+            })
+            reject(hashingErr)
+          }
+          fs.writeFile(metadataFile, JSON.stringify({ ...(readMetadataErr ? {} : JSON.parse(metadataData)), hash: (readMetadataErr ? hash : JSON.parse(metadataData).hash), scannedTime: Date.now() }), writeErr => {   // Save metadata.dat file
+            if(writeErr) {
+              dispatch({
+                type: DISPLAY_WARNING,
+                payload: {
+                  text: `Failed to write metadata file for ${ song.name }. Go to settings and press "Scan for Songs" to try again.`
+                }
+              })
+              reject(writeErr)
+            }
+          })
+        } else {
+          hash = JSON.parse(metadataData).hash
+        }
+        resolve(hash)
+      })
+    })
+  })
+}
+
 export const checkDownloadedSongs = () => (dispatch, getState) => {
   let discoveredFiles = 0, processedFiles = 0
   dispatch({
@@ -403,7 +484,7 @@ export const checkDownloadedSongs = () => (dispatch, getState) => {
     type: SET_PROCESSED_FILES,
     payload: processedFiles
   })
-  const walk = function(pathName, cb) {
+  function walk(pathName, cb) {
     let songs = []
     fs.readdir(pathName, (err, files) => {
       if(err) return cb(err)
@@ -427,55 +508,37 @@ export const checkDownloadedSongs = () => (dispatch, getState) => {
               if(!--pending) cb(null, songs)
             })
           } else {
-            if(files[i].toLowerCase() === 'info.dat' || files[i].toLowerCase() === 'info.json') {
-              fs.readFile(file, { encoding: 'UTF-8' }, (err, data) => {
-                if(err) return cb(err)
-                let song = JSON.parse(data)
-                if(song.hasOwnProperty('hash')) {
-                  songs.push({ hash: song.hash, file })
-                  dispatch({
-                    type: SET_PROCESSED_FILES,
-                    payload: ++processedFiles
-                  })
-                  if(!--pending) cb(null, songs)
-                } else {
-                  let to_hash = ''
-                  try {
-                    for (let i = 0; i < song.difficultyLevels.length; i++) {
-                      to_hash += fs.readFileSync(path.join(path.dirname(file), song.difficultyLevels[i].jsonPath), 'UTF8')
-                    }
-                    let hash = md5(to_hash)
-                    song.hash = hash
-                    fs.writeFile(file, JSON.stringify(song), 'UTF8', (err) => { if(err) return })
+            switch(files[i].toLowerCase()) {
+              case 'info.dat':                                                                   // In case of an info file
+                hashAndWriteToMetadata(file)(dispatch, getState)
+                  .then(hash => {
                     songs.push({ hash, file })
-                  } catch(err) {
                     dispatch({
-                      type: DISPLAY_WARNING,
-                      payload: {
-                        text: `Failed to generate hash: a file could not be accessed.`
-                      }
+                      type: SET_PROCESSED_FILES,
+                      payload: ++processedFiles
                     })
-                  }
-                  dispatch({
-                    type: SET_PROCESSED_FILES,
-                    payload: ++processedFiles
+                    if(!--pending) cb(null, songs)
+                  }, _ => {
+                    dispatch({
+                      type: SET_PROCESSED_FILES,
+                      payload: ++processedFiles
+                    })
+                    if(!--pending) cb(null, songs)
                   })
-                  if(!--pending) cb(null, songs)
-                }
-              })
-            } else {
-              dispatch({
-                type: SET_PROCESSED_FILES,
-                payload: ++processedFiles
-              })
-              if(!--pending) cb(null, songs)
+                break
+              default:
+                dispatch({
+                  type: SET_PROCESSED_FILES,
+                  payload: ++processedFiles
+                })
+                if(!--pending) cb(null, songs)
+                break
             }
           }
         })
       }
     })
   }
-
   dispatch({
     type: SET_SCANNING_FOR_SONGS,
     payload: true
@@ -483,18 +546,18 @@ export const checkDownloadedSongs = () => (dispatch, getState) => {
   walk(path.join(getState().settings.installationDirectory, 'Beat Saber_Data', 'CustomLevels'), (err, songs) => {
     if (err) {
       dispatch({
+        type: DISPLAY_WARNING,
+        payload: {
+          text: `Could not find CustomLevels folder. Please make sure you have your installation directory and type are set correctly.`
+        }
+      })
+      fs.mkdir(path.join(getState().settings.installationDirectory, 'Beat Saber_Data', 'CustomLevels'), () => {
+        dispatch({
           type: DISPLAY_WARNING,
           payload: {
-              text: `Could not find Custom Levels directory. Please make sure you have your installation directory and type are set correctly.`
+            text: `Attempted to create CustomLevels folder and failed, likely due to permissions.`
           }
-      })
-      fs.mkdir(path.join(getState().settings.installationDirectory, 'Beat Saber_Data', 'CustomLevels'), () => {
-          dispatch({
-              type: DISPLAY_WARNING,
-              payload: {
-                  text: `Attempted to create Custom levels folder and failed, likely due to permissions.`
-              }
-          })
+        })
       })
     }
     dispatch({

+ 27 - 12
src/actions/songListActions.js

@@ -1,6 +1,7 @@
 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, SET_VIEW, DISPLAY_WARNING } from './types'
 import { SONG_LIST } from '../views'
 import { BEATSAVER, LIBRARY } from '../constants/resources'
+import { hashAndWriteToMetadata } from './queueActions';
 
 const { remote } = window.require('electron')
 const fs = remote.require('fs')
@@ -178,29 +179,43 @@ export const fetchLocalSongs = () => (dispatch, getState) => {
   for(let i = 0; i < downloadedSongs.length; i++) {
     fs.readFile(downloadedSongs[i].file, 'UTF-8', (err, data) => {
       if(err) {
+        dispatch({
+          type: DISPLAY_WARNING,
+          payload: {
+            text: `Failed to read ${ downloadedSongs[i].file }: ${ err }`
+          }
+        })
         return
       }
       let song
       try {
         song = JSON.parse(data)
       } catch(err) {
+        dispatch({
+          type: DISPLAY_WARNING,
+          payload: {
+            text: `Failed to parse song: ${ err }`
+          }
+        })
         return
       }
       song.coverUrl = `file://${ path.join(path.dirname(downloadedSongs[i].file), (song.coverImagePath || song._coverImageFilename)) }`
       song.file = downloadedSongs[i].file
-      songs.push(song)
-      console.log(song)
-      if(i >= downloadedSongs.length - 1) {
-        console.log('Bam!')
-        dispatch({
-          type: FETCH_LOCAL_SONGS,
-          payload: songs
-        })
-        dispatch({
-          type: SET_LOADING,
-          payload: false
+      hashAndWriteToMetadata(downloadedSongs[i].file)(dispatch, getState)
+        .then(hash => {
+          song.hash = hash
+          songs.push(song)
+          if(i >= downloadedSongs.length - 1) {
+            dispatch({
+              type: FETCH_LOCAL_SONGS,
+              payload: songs
+            })
+            dispatch({
+              type: SET_LOADING,
+              payload: false
+            })
+          }
         })
-      }
     })
   }
 }

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 58 - 0
src/components/PlaylistPicker.js


+ 1 - 0
src/components/ReleaseNotesModal.js

@@ -36,6 +36,7 @@ class ReleaseNotesModal extends Component {
               <li>Fixed a bug where <b>app would crash when moving to next song in queue after error.</b></li>
               <li>2.5.1: Fixed <b>bugs in new local song code.</b></li>
               <li>2.5.2: Fixed <b>crash when searching for songs.</b></li>
+              <li>2.5.3: Fix <a href="https://github.com/StarGazer1258/BeatDrop/issues/45" onClick={ (e) => { e.preventDefault(); e.stopPropagation(); window.require('electron').shell.openExternal(e.target.href) } }>#45.</a> This is basically implemeting the new hashing calculation, so it should fix numerous issues, such as song not appearing as downloaded, songs showing the wrong leaderboards in-game, playlists not wokring properly, etc.</li>
             </ul>
             <br />
             <Button type="primary" onClick={ () => { this.props.setLatestReleaseNotes(require('../../package.json').version) } }>Awesome!</Button>

+ 2 - 2
src/components/SettingsView.js

@@ -72,7 +72,7 @@ class SettingsView extends Component {
             </td>
             <td>
               <select id="game-version-select" name="game-version-select" value={ this.props.settings.gameVersion } onChange={ (e) => { this.props.setGameVersion(e.target.value) } }>
-                { this.state.gameVersions.map(version => <option value={ version }>{ version }</option>) }
+                { this.state.gameVersions.map(version => <option key={ version } value={ version }>{ version }</option>) }
               </select>
             </td>
           </tr>
@@ -113,7 +113,7 @@ class SettingsView extends Component {
           <option value="beta">Beta</option>
         </select><br /><br />
         {this.props.settings.updateChannel === 'beta' ? <><span style={ { fontWeight: 'bold', color: 'salmon' } }>Warning: Beta builds are unstable, untested and may result in unexpected crashes, loss of files and other adverse effects! By updating to a beta build, you understand and accept these risks.</span><br /><br /></> : null}
-        <Button type={ this.state.updateStatus === 'error' ? 'destructive' : null } onClick={ () => { ipcRenderer.send('electron-updater', 'check-for-updates') } }>{this.updateValue()}</Button>
+        <Button type={ this.state.updateStatus === 'error' ? 'destructive' : null } onClick={ () => { ipcRenderer.send('electron-updater', 'check-for-updates') } }>{ this.updateValue() }</Button>
         <br /><br />
         <hr />
         <h2>Credits</h2>

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 4 - 38
src/components/SongDetails.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 5 - 40
src/components/SongList.js


+ 192 - 0
src/css/PlaylistPicker.scss

@@ -0,0 +1,192 @@
+@import './StyleConstants';
+
+#playlist-picker {
+  position: absolute;
+  top: 35px;
+  left: 0;
+  width: 100%;
+  height: calc(100% - 35px);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background: rgba(0, 0, 0, 0.5);
+  animation: playlist-picker-fade-in .2s forwards;
+
+  h1 {
+    margin-left: 25px;
+    display: inline-block;
+  }
+
+  .button {
+    float: right;
+    margin-top: 25px;
+    margin-right: 25px;
+  }
+
+  #playlist-picker-inner {
+    background: white;
+    width: 575px;
+    height: 500px;
+    max-width: 575px;
+    max-height: 500px;
+    border-radius: 5px;
+    padding: 10px;
+    opacity: 1;
+    animation: playlist-picker-drop-in .2s forwards;
+  }
+
+  #playlist-picker-table {
+    display: flex;
+    flex-flow: row wrap;
+    align-content: flex-start;
+    overflow-y: scroll;
+    height: 415px;
+  }
+
+  .playlist-picker-item {
+    width: calc(50% - 30px);
+    height: 50px;
+    list-style: none;
+    display: flex;
+    align-items: flex-start;
+    padding: 10px;
+    background: rgba(0, 0, 0, 0.1);
+    border-radius: 5px;
+    margin: 5px;
+    cursor: pointer;
+    transition: 0.2s;
+    font-size: 11pt;
+
+    img {
+      min-width: 50px;
+      min-height: 50px;
+      max-width: 50px;
+      max-height: 50px;
+      border-radius: 5px;
+      margin-right: 10px;
+      box-shadow: 2px 2px 5px 0px rgba(0,0,0,0.25);
+      background: silver;
+    }
+
+    .playlist-picker-item-title, .playlist-picker-item-author {
+      white-space: nowrap;
+      width: 195px;
+      overflow: hidden;
+      text-overflow: ellipsis
+    }
+
+    .playlist-picker-item-title {
+      font-weight: bold;
+    }
+
+    &:hover {
+      background: rgba(0, 0, 0, 0.2);
+    }
+  }
+}
+
+#new-playlist-dialog {
+  display: flex;
+  flex-flow: row nowrap;
+  position: absolute;
+  top: calc(50% - 245px);
+  left: calc(50% - 420px);
+  width: 800px;
+  height: 450px;
+  background: white;
+  padding: 20px;
+  transform-origin: 0 0;
+  animation: new-playlist-drop-in .2s forwards ease-out;
+
+  label {
+    display: block;
+    margin-right: 5px;
+    font-weight: bold;
+  }
+
+  #new-playlist-cover-image {
+    display: none;
+  }
+
+  #new-playlist-add-cover-image {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    font-size: 32pt;
+    font-weight: bold;
+    color: white;
+    width: 200px;
+    height: 200px;
+    margin-bottom: 5px;
+    background:  rgba(0, 0, 0, 0.1);
+    border-radius: 5px;
+    cursor: pointer;
+    overflow: hidden;
+
+    img {
+      width: 200px;
+      height: 200px;
+    }
+  }
+
+  #new-playlist-info {
+    margin-top: 30px;
+    margin-left: 30px;
+  }
+}
+
+.theme-dark, .theme-hc {
+  #playlist-picker-inner {
+    color: white;
+  }
+
+  .close-icon {
+    background: url(../assets/dark/close.png);
+    background-size: 25px;
+  }
+}
+
+.theme-dark {
+  #playlist-picker-inner {
+    background: $theme-dark-background-color !important;
+  }
+
+  #new-playlist-dialog {
+    background: $theme-dark-background-color;
+  }
+}
+
+.theme-hc {
+  #playlist-picker-inner {
+    background: $theme-hc-background-color !important;
+    border: 1px solid $theme-hc-line-color;
+    
+    .playlist-picker-item {
+      max-width: calc(50% - 32px);
+      border: 1px solid $theme-hc-line-color;
+
+      img {
+        border: 1px solid $theme-hc-line-color;
+      }
+    }
+  }
+
+  #new-playlist-dialog {
+    background: $theme-hc-background-color;
+    border: 1px solid $theme-hc-line-color;
+  }
+}
+
+
+@keyframes playlist-picker-fade-in {
+  from {
+    background: rgba(0, 0, 0, 0);
+  }
+}
+
+@keyframes playlist-picker-drop-in {
+  from {
+    opacity: 0.0;
+    transform: translateY(-500px);
+  }
+}

+ 0 - 191
src/css/SongDetails.scss

@@ -18,141 +18,6 @@
     }
   }
 
-  #playlist-picker {
-    position: absolute;
-    top: 35px;
-    left: 0;
-    width: 100%;
-    height: calc(100% - 35px);
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    background: rgba(0, 0, 0, 0.5);
-    animation: playlist-picker-fade-in .2s forwards;
-
-    h1 {
-      margin-left: 25px;
-      display: inline-block;
-    }
-
-    .button {
-      float: right;
-      margin-top: 25px;
-      margin-right: 25px;
-    }
-
-    #playlist-picker-inner {
-      background: white;
-      width: 575px;
-      height: 500px;
-      max-width: 575px;
-      max-height: 500px;
-      border-radius: 5px;
-      padding: 10px;
-      opacity: 1;
-      animation: playlist-picker-drop-in .2s forwards;
-    }
-
-    #playlist-picker-table {
-      display: flex;
-      flex-flow: row wrap;
-      align-content: flex-start;
-      overflow-y: scroll;
-      height: 415px;
-    }
-
-    .playlist-picker-item {
-      width: calc(50% - 30px);
-      height: 50px;
-      list-style: none;
-      display: flex;
-      align-items: flex-start;
-      padding: 10px;
-      background: rgba(0, 0, 0, 0.1);
-      border-radius: 5px;
-      margin: 5px;
-      cursor: pointer;
-      transition: 0.2s;
-      font-size: 11pt;
-
-      img {
-        min-width: 50px;
-        min-height: 50px;
-        max-width: 50px;
-        max-height: 50px;
-        border-radius: 5px;
-        margin-right: 10px;
-        box-shadow: 2px 2px 5px 0px rgba(0,0,0,0.25);
-        background: silver;
-      }
-
-      .playlist-picker-item-title, .playlist-picker-item-author {
-        white-space: nowrap;
-        width: 195px;
-        overflow: hidden;
-        text-overflow: ellipsis
-      }
-
-      .playlist-picker-item-title {
-        font-weight: bold;
-      }
-
-      &:hover {
-        background: rgba(0, 0, 0, 0.2);
-      }
-    }
-  }
-
-  #new-playlist-dialog {
-    display: flex;
-    flex-flow: row nowrap;
-    position: absolute;
-    top: calc(50% - 245px);
-    left: calc(50% - 420px);
-    width: 800px;
-    height: 450px;
-    background: white;
-    padding: 20px;
-    transform-origin: 0 0;
-    animation: new-playlist-drop-in .2s forwards ease-out;
-  
-    label {
-      display: block;
-      margin-right: 5px;
-      font-weight: bold;
-    }
-  
-    #new-playlist-cover-image {
-      display: none;
-    }
-  
-    #new-playlist-add-cover-image {
-      display: flex;
-      justify-content: center;
-      align-items: center;
-      font-size: 32pt;
-      font-weight: bold;
-      color: white;
-      width: 200px;
-      height: 200px;
-      margin-bottom: 5px;
-      background:  rgba(0, 0, 0, 0.1);
-      border-radius: 5px;
-      cursor: pointer;
-      overflow: hidden;
-  
-      img {
-        width: 200px;
-        height: 200px;
-      }
-    }
-  
-    #new-playlist-info {
-      margin-top: 30px;
-      margin-left: 30px;
-    }
-  }
-
   .cover-image {
     width: 15vw;
     height: 15vw;
@@ -345,50 +210,7 @@
   }
 }
 
-//TODO Set correct color for each theme
-.theme-dark, .theme-hc {
-  #song-details {
-    #playlist-picker-inner {
-      color: white;
-    }
-
-    .close-icon {
-      background: url(../assets/dark/close.png);
-      background-size: 25px;
-    }
-  }
-}
-
-.theme-dark {
-  #playlist-picker-inner {
-    background: $theme-dark-background-color !important;
-  }
-
-  #new-playlist-dialog {
-    background: $theme-dark-background-color;
-  }
-}
-
 .theme-hc {
-  #playlist-picker-inner {
-    background: $theme-hc-background-color !important;
-    border: 1px solid $theme-hc-line-color;
-    
-    .playlist-picker-item {
-      max-width: calc(50% - 32px);
-      border: 1px solid $theme-hc-line-color;
-
-      img {
-        border: 1px solid $theme-hc-line-color;
-      }
-    }
-  }
-
-  #new-playlist-dialog {
-    background: $theme-hc-background-color;
-    border: 1px solid $theme-hc-line-color;
-  }
-
   .action-buttons .action-button {
     background-color: rgba($theme-hc-accent-color, 0.8) !important;
 
@@ -415,19 +237,6 @@
   }
 }
 
-@keyframes playlist-picker-fade-in {
-  from {
-    background: rgba(0, 0, 0, 0);
-  }
-}
-
-@keyframes playlist-picker-drop-in {
-  from {
-    opacity: 0.0;
-    transform: translateY(-500px);
-  }
-}
-
 @keyframes song-details-fade-in {
   from {
     opacity: 0;