bugfixes
authormarcelveldt <marcelvanderveldt@MacBook-Pro.local>
Thu, 16 May 2019 12:35:10 +0000 (14:35 +0200)
committermarcelveldt <marcelvanderveldt@MacBook-Pro.local>
Thu, 16 May 2019 12:35:10 +0000 (14:35 +0200)
18 files changed:
music_assistant/modules/homeassistant.py
music_assistant/modules/musicproviders/file.py
music_assistant/modules/web.py
music_assistant/player.py
music_assistant/web/components/headermenu.vue.js
music_assistant/web/components/player.vue.js
music_assistant/web/components/searchbar.vue.js [deleted file]
music_assistant/web/components/searchbox.vue.js [new file with mode: 0644]
music_assistant/web/index.html
music_assistant/web/pages/albumdetails.vue.js
music_assistant/web/pages/artistdetails.vue.js
music_assistant/web/pages/browse.vue.js
music_assistant/web/pages/config.vue.js
music_assistant/web/pages/home.vue.js
music_assistant/web/pages/playlistdetails.vue.js
music_assistant/web/pages/queue.vue.js
music_assistant/web/pages/search.vue.js
music_assistant/web/strings.js [new file with mode: 0644]

index 7cb9dc97a442008f35a2126373aabb62d6d27b44..16de0cb5184464912aeaa626d074539810e5452b 100644 (file)
@@ -41,10 +41,10 @@ def setup(mass):
 def create_config_entries(config):
     ''' get the config entries for this module (list with key/value pairs)'''
     config_entries = [
-        (CONF_ENABLED, False, CONF_ENABLED),
-        ('url', 'localhost', 'URL to homeassistant (e.g. https://homeassistant:8123)'), 
-        ('token', '<password>', 'Long Lived Access Token'),
-        ('publish_players', True, 'Publish players to Home Assistant')
+        (CONF_ENABLED, False, 'enabled'),
+        ('url', 'localhost', 'hass_url'), 
+        ('token', '<password>', 'hass_token'),
+        ('publish_players', True, 'hass_publish')
         ]
     if not config['base'].get('homeassistant'):
         config['base']['homeassistant'] = {}
@@ -54,9 +54,9 @@ def create_config_entries(config):
             config['base']['homeassistant'][key] = def_value
     # append hass player config settings
     if config['base']['homeassistant'][CONF_ENABLED]:
-        hass_player_conf = [("hass_power_entity", "", "Attach player power to homeassistant entity"),
-                        ("hass_power_entity_source", "", "Source on the homeassistant entity (optional)"),
-                        ("hass_volume_entity", "", "Attach player volume to homeassistant entity")]
+        hass_player_conf = [("hass_power_entity", "", "hass_player_power"),
+                        ("hass_power_entity_source", "", "hass_player_source"),
+                        ("hass_volume_entity", "", "hass_player_volume")]
         for key, default, desc in hass_player_conf:
             entry_found = False
             for value in config['player_settings']['__desc__']:
index 5342ee605c2c0c2601a244a34fbd079502ff656b..902151c3badd28fc5917cb70351215042b5e6778 100644 (file)
@@ -28,8 +28,8 @@ def config_entries():
     ''' get the config entries for this provider (list with key/value pairs)'''
     return [
         (CONF_ENABLED, False, CONF_ENABLED),
-        ("music_dir", "", "Path to music files"), 
-        ("playlists_dir", "", "Path to playlists")
+        ("music_dir", "", "file_prov_music_path"), 
+        ("playlists_dir", "", "file_prov_playlists_path")
         ]
 
 class FileProvider(MusicProvider):
index fbb9520e8abe4d6c20143b8027f579f986286722..295139b1aa92c58842a3cd55e503974228c91725 100755 (executable)
@@ -30,9 +30,9 @@ def setup(mass):
 def create_config_entries(config):
     ''' get the config entries for this module (list with key/value pairs)'''
     config_entries = [
-        ('ssl_certificate', '', 'Path to ssl certificate file'), 
-        ('ssl_key', '', 'Path to ssl keyfile'),
-        ('hostname', '', 'Hostname (FQDN used in the certificate)')
+        ('ssl_certificate', '', 'web_ssl_cert'), 
+        ('ssl_key', '', 'web_ssl_key'),
+        ('hostname', '', 'web_ssl_host')
         ]
     if not config['base'].get('web'):
         config['base']['web'] = {}
index a3d341bcdd962b326528506ce0d83cbec3978234..54d3959de173e7460fe4e4eee982edc7e0442122 100755 (executable)
@@ -32,14 +32,14 @@ class Player():
     def create_config_entries(self):
         ''' sets the config entries for this module (list with key/value pairs)'''
         self.mass.config['player_settings']['__desc__'] = [
-            ("enabled", False, "Enable player"),
-            ("name", "", "Custom name for this player"),
-            ("group_parent", "<player>", "Group this player to another player"),
-            ("mute_as_power", False, "Use muting as power control"),
-            ("disable_volume", False, "Disable volume controls"),
-            ("apply_group_volume", False, "Apply group volume to childs (for group players only)"),
-            ("apply_group_power", False, "Apply group power based on childs (for group players only)"),
-            ("play_power_on", False, "Issue play command on power on")
+            ("enabled", False, "player_enabled"),
+            ("name", "", "player_name"),
+            ("group_parent", "<player>", "player_group_with"),
+            ("mute_as_power", False, "player_mute_power"),
+            ("disable_volume", False, "player_disable_vol"),
+            ("apply_group_volume", False, "player_group_vol"),
+            ("apply_group_power", False, "player_group_pow"),
+            ("play_power_on", False, "player_power_play")
         ]
     
     async def players(self):
@@ -95,9 +95,16 @@ class Player():
         ''' handle hass integration in player command '''
         if not self.mass.hass:
             return
-        if cmd == 'power' and cmd_args == 'on' and player.settings.get('hass_power_entity') and player.settings.get('hass_power_entity_source'):
-            service_data = { 'entity_id': player.settings['hass_power_entity'], 'source':player.settings['hass_power_entity_source'] }
-            await self.mass.hass.call_service('media_player', 'select_source', service_data)
+        if cmd == 'power' and player.settings.get('hass_power_entity') and player.settings.get('hass_power_entity_source'):
+            cur_source = await self.mass.hass.get_state(player.settings['hass_power_entity'], attribute='source')
+            if cmd_args == 'on' and not cur_source:
+                service_data = { 'entity_id': player.settings['hass_power_entity'], 'source':player.settings['hass_power_entity_source'] }
+                await self.mass.hass.call_service('media_player', 'select_source', service_data)
+            elif cmd_args == 'off' and cur_source == player.settings['hass_power_entity_source']:
+                service_data = { 'entity_id': player.settings['hass_power_entity'] }
+                await self.mass.hass.call_service('media_player', 'turn_off', service_data)
+            else:
+                LOGGER.warning('Ignoring power command as required source is not active')
         elif cmd == 'power' and player.settings.get('hass_power_entity'):
             domain = player.settings['hass_power_entity'].split('.')[0]
             service_data = { 'entity_id': player.settings['hass_power_entity']}
index 1fe2815be2be9ca74926d6b06ebd8e23aaab97c1..5740e333b0e5589c210c0b523588439cb4b774a5 100755 (executable)
@@ -14,7 +14,8 @@ Vue.component("headermenu", {
         </v-list>
     </v-navigation-drawer>
     
-    <v-toolbar fixed flat dense dark color="transparent" scroll-off-screen > 
+
+    <v-toolbar app flat dense dark v-if="$globals.windowtitle" > 
         <v-layout align-center>
             <v-btn icon v-on:click="menu=!menu">
               <v-icon>menu</v-icon>
@@ -23,8 +24,26 @@ Vue.component("headermenu", {
               <v-icon>arrow_back</v-icon>
             </v-btn>
             <v-spacer></v-spacer>
+            <v-card-title class="title justify-center">
+                      {{ $globals.windowtitle }}
+                  </v-card-title>
             <v-spacer></v-spacer>
-            <v-btn icon v-on:click="$router.push('/search')">
+            <v-btn icon v-on:click="$globals.showsearchbox = true">
+                <v-icon>search</v-icon>
+              </v-btn>
+        </v-layout>
+    </v-toolbar>
+    <v-toolbar flat fixed dense dark scroll-off-screen color="transparent" v-if="!$globals.windowtitle" > 
+        <v-layout align-center>
+            <v-btn icon v-on:click="menu=!menu">
+              <v-icon>menu</v-icon>
+            </v-btn>
+            <v-btn @click="$router.go(-1)" icon>
+              <v-icon>arrow_back</v-icon>
+            </v-btn>
+            <v-spacer></v-spacer>
+            <v-spacer></v-spacer>
+            <v-btn icon v-on:click="$globals.showsearchbox = true">
                 <v-icon>search</v-icon>
               </v-btn>
         </v-layout>
@@ -38,13 +57,13 @@ Vue.component("headermenu", {
     return {
       menu: false,
       items: [
-        { title: "Home", icon: "home", path: "/" },
-        { title: "Artists", icon: "person", path: "/artists" },
-        { title: "Albums", icon: "album", path: "/albums" },
-        { title: "Tracks", icon: "audiotrack", path: "/tracks" },
-        { title: "Playlists", icon: "playlist_play", path: "/playlists" },
-        { title: "Search", icon: "search", path: "/search" },
-        { title: "Config", icon: "settings", path: "/config" }
+        { title: this.$t('home'), icon: "home", path: "/" },
+        { title: this.$t('artists'), icon: "person", path: "/artists" },
+        { title: this.$t('albums'), icon: "album", path: "/albums" },
+        { title: this.$t('tracks'), icon: "audiotrack", path: "/tracks" },
+        { title: this.$t('playlists'), icon: "playlist_play", path: "/playlists" },
+        { title: this.$t('search'), icon: "search", path: "/search" },
+        { title: this.$t('settings'), icon: "settings", path: "/config" }
       ]
     }
   },
index 9bbfc1d71d641fe68aa99eff53a2525aafb5e754..f0a545ff22bc477ba585d8d81388ff3ae70ccb18 100755 (executable)
@@ -39,7 +39,7 @@ Vue.component("player", {
                   <v-btn flat icon @click="$router.push('/queue/' + active_player_id)">
                       <v-flex xs12 class="vertical-btn">
                       <v-icon large>queue_music</v-icon>
-                      <span class="caption">Queue</span>
+                      <span class="caption">{{ $t('queue') }}</span>
                     </v-flex>    
                   </v-btn>
               </v-list-tile-action> 
@@ -116,9 +116,6 @@ Vue.component("player", {
                         <volumecontrol v-bind:players="players" v-bind:player_id="player.player_id" v-on:setPlayerVolume="setPlayerVolume" v-on:togglePlayerPower="togglePlayerPower"/>
                       </v-menu>
                   </v-list-tile-action> 
-
-
-
               </v-list-tile>
             <v-divider></v-divider>
             </div>
diff --git a/music_assistant/web/components/searchbar.vue.js b/music_assistant/web/components/searchbar.vue.js
deleted file mode 100644 (file)
index 5efcf04..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-Vue.component("searchbar", {
-  template: `
-  <section class="section searchbar">
-    <div class="container">
-      <b-field>
-        <b-autocomplete size="is-medium"
-        expanded
-          v-model="searchQuery"
-          :data="filteredDataArray"
-          placeholder="e.g. Eminem"
-          icon="magnify"
-          @select="option => selected = option"
-          @keyup.enter="onClickSearch"
-        ></b-autocomplete>
-        <p class="control" v-if="searchQuery">
-               <button @click="onClickClearSearch" class="button  is-medium "><i class="fas fa-times"></i></button>
-            </p>
-      </b-field>
-    </div>
-  </section>
-  `,
-  data () {
-    return {
-      data: [],
-      searchQuery: '',
-      selected: null
-    }
-  },
-  props: {
-    recentSearch: {
-      type: Array,
-      required: true
-    },
-    newSearchQuery: {
-      type: String,
-      required: true
-    },
-    settings: {
-      type: Object,
-      required: true
-    }
-  },
-  mounted () {
-    this.searchQuery = this.settings.initialSearchQuery
-    this.onClickSearch()
-  },
-  watch: {
-    searchQuery: {
-      handler: _.debounce(function (val) {
-        if (val === '') {
-          this.$store.commit('CLEAR_SEARCH')
-        } else {
-          if (val !== this.newSearchQuery) {
-            this.onClickSearch()
-          }
-        }
-      }, 1000)
-    },
-    newSearchQuery (val) {
-      this.searchQuery = val
-    }
-  },
-  computed: {
-    filteredDataArray () {
-      return this.recentSearch.filter(option => {
-        return (
-          option
-            .toString()
-            .toLowerCase()
-            .indexOf(this.searchQuery.toLowerCase()) >= 0
-        )
-      })
-    }
-  },
-  methods: {
-    onClickSearch () {
-      this.$emit('clickSearch', this.searchQuery)
-    },
-    onClickClearSearch () {
-      this.searchQuery = ''
-      this.$emit('clickClearSearch')
-    }
-  }
-})
-/* <style>
-.searchbar {
-  padding: 1rem 1.5rem!important;
-  width: 100%;
-  box-shadow: 0 0 70px 0 rgba(0, 0, 0, 0.3);
-  background: #fff;
-}
-</style> */
\ No newline at end of file
diff --git a/music_assistant/web/components/searchbox.vue.js b/music_assistant/web/components/searchbox.vue.js
new file mode 100644 (file)
index 0000000..1570ab6
--- /dev/null
@@ -0,0 +1,50 @@
+Vue.component("searchbox", {
+  template: `
+  <v-dialog :value="$globals.showsearchbox" @input="$emit('input', $event)" max-width="500px">
+      <v-text-field
+            solo
+            clearable
+            :label="$t('type_to_search')"
+            prepend-inner-icon="search"
+            v-model="searchQuery">
+          </v-text-field>
+      </v-dialog>
+  `,
+  data () {
+    return {
+      searchQuery: "",
+    }
+  },
+  props: ['value'],
+  mounted () {
+    this.searchQuery = "" // TODO: set to last searchquery ?
+  },
+  watch: {
+    searchQuery: {
+      handler: _.debounce(function (val) {
+        this.onSearch();
+        // if (this.searchQuery)
+        //     this.$globals.showsearchbox = false;
+      }, 1000)
+    },
+    newSearchQuery (val) {
+      this.searchQuery = val
+    }
+  },
+  computed: {},
+  methods: {
+    onSearch () {
+      //this.$emit('clickSearch', this.searchQuery)
+      console.log(this.searchQuery);
+      router.push({ path: '/search', query: {searchQuery: this.searchQuery}});
+    },
+  }
+})
+/* <style>
+.searchbar {
+  padding: 1rem 1.5rem!important;
+  width: 100%;
+  box-shadow: 0 0 70px 0 rgba(0, 0, 0, 0.3);
+  background: #fff;
+}
+</style> */
\ No newline at end of file
index 3157ab836764229f53961700947519b42f2cfb7c..334025156d68ec61bf2f3820a0dfda8932d7387a 100755 (executable)
@@ -20,7 +20,8 @@
                 <v-content>
                     <headermenu></headermenu>
                     <player></player>
-                    <router-view :key="$route.path"></router-view>
+                    <router-view app :key="$route.path"></router-view>      
+                    <searchbox/>             
                 </v-content>
                 <v-dialog
                     v-model="$globals.loading"
                         </v-card-text>
                     </v-card>
                 </v-dialog>
-                
             </v-app>
         </div>
 
 
-        <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>
+        <!-- <script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script> -->
+        <script src="https://unpkg.com/vue/dist/vue.js"></script>
+        <script src="https://unpkg.com/vue-i18n/dist/vue-i18n.js"></script>
         <script src="https://cdn.jsdelivr.net/npm/vuetify/dist/vuetify.js"></script>
         <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
         <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js"></script>
         <script src='./pages/playlistdetails.vue.js'></script>
         <script src='./pages/search.vue.js'></script>
         <script src='./pages/queue.vue.js'></script>
+        <script src='./pages/config.vue.js'></script>
         
 
         <script src='./components/headermenu.vue.js'></script>
         <script src='./components/volumecontrol.vue.js'></script>
         <script src='./components/infoheader.vue.js'></script>
         <script src='./components/qualityicon.vue.js'></script>
-        <script src='./pages/config.vue.js'></script>
+        <script src='./components/searchbox.vue.js'></script>
+        
+        <script src='./strings.js'></script>
         
         <script>
         Vue.use(VueRouter);
         Vue.use(VeeValidate);
         Vue.use(Vuetify);
+        Vue.use(VueI18n);
 
 
         const routes = [
                 windowtitle: 'Home',
                 loading: false,
                 showplaymenu: false,
+                showsearchbox: false,
                 playmenuitem: null
             }
         })
         Vue.prototype.toggleLibrary = toggleLibrary;
         Vue.prototype.showPlayMenu = showPlayMenu;
         Vue.prototype.clickItem= clickItem;
+        
+        const i18n = new VueI18n({
+            locale: navigator.language.split('-')[0],
+            fallbackLocale: 'en',
+            enableInSFC: true,
+            messages
+            })
 
         var app = new Vue({
+            i18n,
             el: '#app',
             watch: {},
             mounted() {
-
             },
             data: { },
             methods: {},
index b8d59af9c38f25f03a142c0ed803d0b7534171d1..4f60a91e1feb42df7f0a696e027692b3f4d9919e 100755 (executable)
@@ -56,7 +56,7 @@ var AlbumDetails = Vue.component('AlbumDetails', {
     }
   },
   created() {
-    this.$globals.windowtitle = "Album info"
+    this.$globals.windowtitle = ""
     this.getInfo();
     this.getAlbumTracks();
   },
index 961430c6754d70031bc71be02d1de84fc3994930..073c3d9e59882eee8f0d1266bdad9411720b7108 100755 (executable)
@@ -59,7 +59,7 @@ var ArtistDetails = Vue.component('ArtistDetails', {
     }
   },
   created() {
-    this.$globals.windowtitle = "Artist info"
+    this.$globals.windowtitle = ""
     this.getInfo();
   },
   methods: {
index d23a86c778a7a1ea0667b519a788baae8a4c90a8..49bcdc8b0c7cbedc5008be39144ee8dffa7dc371 100755 (executable)
@@ -1,13 +1,6 @@
 var Browse = Vue.component('Browse', {
   template: `
     <section>
-      <v-flex xs12>
-        <v-card class="flex" tile style="background-color:rgba(0,0,0,.54);color:#ffffff;"> 
-          <v-card-title class="title justify-center">
-              {{ $globals.windowtitle }}
-          </v-card-title>
-        </v-card>
-      </v-flex>    
       <v-list two-line>
         <listviewItem 
             v-for="(item, index) in items"
@@ -34,7 +27,7 @@ var Browse = Vue.component('Browse', {
   created() {
     this.showavatar = true;
     mediatitle = 
-    this.$globals.windowtitle = this.mediatype.charAt(0).toUpperCase() + this.mediatype.slice(1);
+    this.$globals.windowtitle = this.$t(this.mediatype)
     this.scroll(this.Browse);
     this.getItems();
   },
index 55c2e16671b7cff2d6484c572f42b7925ce2604f..77299c23d0a280d7c7d4c8a10a296c4bb1194fca 100755 (executable)
@@ -1,13 +1,6 @@
 var Config = Vue.component('Config', {
   template: `
     <section>
-      <v-flex xs12>
-        <v-card class="flex" tile style="background-color:rgba(0,0,0,.54);color:#ffffff;"> 
-          <v-card-title class="title justify-center">
-              {{ $globals.windowtitle }}
-          </v-card-title>
-        </v-card>
-      </v-flex>    
 
       <v-list two-line>
 
@@ -16,7 +9,7 @@ var Config = Vue.component('Config', {
             <template v-slot:activator>
               <v-list-tile>
                 <v-list-tile-content>
-                  <v-list-tile-title>Generic settings</v-list-tile-title>
+                  <v-list-tile-title>{{ $t('generic_settings') }}</v-list-tile-title>
                 </v-list-tile-content>
               </v-list-tile>
             </template>
@@ -29,10 +22,10 @@ var Config = Vue.component('Config', {
                 
                 <div v-for="conf_item_key in conf.base[conf_key].__desc__">
                   <v-list-tile>
-                        <v-switch v-if="typeof(conf_item_key[1]) == 'boolean'" v-model="conf.base[conf_key][conf_item_key[0]]" :label="conf_item_key[2]"></v-switch>
-                        <v-text-field v-else-if="conf_item_key[1] == '<password>'" v-model="conf.base[conf_key][conf_item_key[0]]" :label="conf_item_key[2]" box type="password"></v-text-field>
-                        <v-select v-else-if="conf_item_key[1] == '<player>'" v-model="conf.base[conf_key][conf_item_key[0]]" :label="conf_item_key[2]" box type="password"></v-select>
-                        <v-text-field v-else v-model="conf.base[conf_key][conf_item_key[0]]" :label="conf_item_key[2]" box></v-text-field>
+                        <v-switch v-if="typeof(conf_item_key[1]) == 'boolean'" v-model="conf.base[conf_key][conf_item_key[0]]" :label="$t(conf_item_key[2])"></v-switch>
+                        <v-text-field v-else-if="conf_item_key[1] == '<password>'" v-model="conf.base[conf_key][conf_item_key[0]]" :label="$t(conf_item_key[2])" box type="password"></v-text-field>
+                        <v-select v-else-if="conf_item_key[1] == '<player>'" v-model="conf.base[conf_key][conf_item_key[0]]" :label="$t(conf_item_key[2])" box type="password"></v-select>
+                        <v-text-field v-else v-model="conf.base[conf_key][conf_item_key[0]]" :label="$t(conf_item_key[2])" box></v-text-field>
                   </v-list-tile>
               </div>
               <v-divider></v-divider>
@@ -45,7 +38,7 @@ var Config = Vue.component('Config', {
               <template v-slot:activator>
                 <v-list-tile>
                   <v-list-tile-content>
-                    <v-list-tile-title>Music Providers</v-list-tile-title>
+                    <v-list-tile-title>{{ $t('music_providers') }}</v-list-tile-title>
                   </v-list-tile-content>
                 </v-list-tile>
               </template>
@@ -61,10 +54,10 @@ var Config = Vue.component('Config', {
                   
                   <div v-for="conf_item_key in conf.musicproviders[conf_key].__desc__">
                     <v-list-tile>
-                          <v-switch v-if="typeof(conf_item_key[1]) == 'boolean'" v-model="conf.musicproviders[conf_key][conf_item_key[0]]" :label="conf_item_key[2]"></v-switch>
-                          <v-text-field v-else-if="conf_item_key[1] == '<password>'" v-model="conf.musicproviders[conf_key][conf_item_key[0]]" :label="conf_item_key[2]" box type="password"></v-text-field>
-                          <v-select v-else-if="conf_item_key[1] == '<player>'" v-model="conf.musicproviders[conf_key][conf_item_key[0]]" :label="conf_item_key[2]" box type="password"></v-select>
-                          <v-text-field v-else v-model="conf.musicproviders[conf_key][conf_item_key[0]]" :label="conf_item_key[2]" box></v-text-field>
+                          <v-switch v-if="typeof(conf_item_key[1]) == 'boolean'" v-model="conf.musicproviders[conf_key][conf_item_key[0]]" :label="$t(conf_item_key[2])"></v-switch>
+                          <v-text-field v-else-if="conf_item_key[1] == '<password>'" v-model="conf.musicproviders[conf_key][conf_item_key[0]]" :label="$t(conf_item_key[2])" box type="password"></v-text-field>
+                          <v-select v-else-if="conf_item_key[1] == '<player>'" v-model="conf.musicproviders[conf_key][conf_item_key[0]]" :label="$t(conf_item_key[2])" box type="password"></v-select>
+                          <v-text-field v-else v-model="conf.musicproviders[conf_key][conf_item_key[0]]" :label="$t(conf_item_key[2])" box></v-text-field>
                     </v-list-tile>
                 </div>
                 <v-divider></v-divider>
@@ -76,7 +69,7 @@ var Config = Vue.component('Config', {
               <template v-slot:activator>
                 <v-list-tile>
                   <v-list-tile-content>
-                    <v-list-tile-title>Player Providers</v-list-tile-title>
+                    <v-list-tile-title>{{ $t('player_providers') }}</v-list-tile-title>
                   </v-list-tile-content>
                 </v-list-tile>
               </template>
@@ -92,10 +85,10 @@ var Config = Vue.component('Config', {
                   
                   <div v-for="conf_item_key in conf.playerproviders[conf_key].__desc__">
                     <v-list-tile>
-                          <v-switch v-if="typeof(conf_item_key[1]) == 'boolean'" v-model="conf.playerproviders[conf_key][conf_item_key[0]]" :label="conf_item_key[2]"></v-switch>
-                          <v-text-field v-else-if="conf_item_key[1] == '<password>'" v-model="conf.playerproviders[conf_key][conf_item_key[0]]" :label="conf_item_key[2]" box type="password"></v-text-field>
-                          <v-select v-else-if="conf_item_key[1] == '<player>'" v-model="conf.playerproviders[conf_key][conf_item_key[0]]" :label="conf_item_key[2]" box type="password"></v-select>
-                          <v-text-field v-else v-model="conf.playerproviders[conf_key][conf_item_key[0]]" :label="conf_item_key[2]" box></v-text-field>
+                          <v-switch v-if="typeof(conf_item_key[1]) == 'boolean'" v-model="conf.playerproviders[conf_key][conf_item_key[0]]" :label="$t(conf_item_key[2])"></v-switch>
+                          <v-text-field v-else-if="conf_item_key[1] == '<password>'" v-model="conf.playerproviders[conf_key][conf_item_key[0]]" :label="$t(conf_item_key[2])" box type="password"></v-text-field>
+                          <v-select v-else-if="conf_item_key[1] == '<player>'" v-model="conf.playerproviders[conf_key][conf_item_key[0]]" :label="$t(conf_item_key[2])" box type="password"></v-select>
+                          <v-text-field v-else v-model="conf.playerproviders[conf_key][conf_item_key[0]]" :label="$t(conf_item_key[2])" box></v-text-field>
                     </v-list-tile>
                 </div>
                 <v-divider></v-divider>
@@ -107,7 +100,7 @@ var Config = Vue.component('Config', {
               <template v-slot:activator>
                 <v-list-tile>
                   <v-list-tile-content>
-                    <v-list-tile-title>Player settings</v-list-tile-title>
+                    <v-list-tile-title>{{ $t('player_settings') }}</v-list-tile-title>
                   </v-list-tile-content>
                 </v-list-tile>
               </template>
@@ -121,22 +114,22 @@ var Config = Vue.component('Config', {
                   
                   <div v-for="conf_item_key in conf.player_settings.__desc__" v-if="conf.player_settings[key].enabled">
                     <v-list-tile>
-                          <v-switch v-if="typeof(conf_item_key[1]) == 'boolean'" v-model="conf.player_settings[key][conf_item_key[0]]" :label="conf_item_key[2]"></v-switch>
-                          <v-text-field v-else-if="conf_item_key[1] == '<password>'" v-model="conf.player_settings[key][conf_item_key[0]]" :label="conf_item_key[2]" box type="password"></v-text-field>
-                          <v-select v-else-if="conf_item_key[1] == '<player>'" v-model="conf.player_settings[key][conf_item_key[0]]" :label="conf_item_key[2]
+                          <v-switch v-if="typeof(conf_item_key[1]) == 'boolean'" v-model="conf.player_settings[key][conf_item_key[0]]" :label="$t(conf_item_key[2])"></v-switch>
+                          <v-text-field v-else-if="conf_item_key[1] == '<password>'" v-model="conf.player_settings[key][conf_item_key[0]]" :label="$t(conf_item_key[2])" box type="password"></v-text-field>
+                          <v-select v-else-if="conf_item_key[1] == '<player>'" v-model="conf.player_settings[key][conf_item_key[0]]" :label="$t(conf_item_key[2])
                             :items="playersLst"
                             item-text="name"
                             item-value="id" box>
                           </v-select>
-                          <v-text-field v-else v-model="conf.player_settings[key][conf_item_key[0]]" :label="conf_item_key[2]" box></v-text-field>
+                          <v-text-field v-else v-model="conf.player_settings[key][conf_item_key[0]]" :label="$t(conf_item_key[2])" box></v-text-field>
                     </v-list-tile>
                     <v-list-tile v-if="!conf.player_settings[key].enabled">
-                          <v-switch v-model="conf.player_settings[key].enabled" label="Enabled"></v-switch>
+                          <v-switch v-model="conf.player_settings[key].enabled" :label="$t('enabled')"></v-switch>
                     </v-list-tile>
                 </div>
                 <div v-if="!conf.player_settings[key].enabled">
                     <v-list-tile>
-                        <v-switch v-model="conf.player_settings[key].enabled" label="Enabled"></v-switch>
+                        <v-switch v-model="conf.player_settings[key].enabled" :label="$t('enabled')"></v-switch>
                     </v-list-tile>
                 </div>
                 <v-divider></v-divider>
@@ -166,7 +159,7 @@ var Config = Vue.component('Config', {
 
   },
   created() {
-    this.$globals.windowtitle = "Configuration";
+    this.$globals.windowtitle = this.$t('settings');
     this.getPlayers();
     this.getConfig();
     console.log(this.$globals.all_players);
index be0010ebfe1a1010290c775c1918f7bd330eb819..1c840871406f0ac86fb318ce1790eed3680493f4 100755 (executable)
@@ -1,15 +1,6 @@
 var home = Vue.component("Home", {
   template: `
   <section>
-      <v-flex xs12 justify-center>
-        <v-card color="cyan darken-2" class="white--text" img="../images/info_gradient.jpg">
-        
-          <div class="text-xs-center" style="height:40px" id="whitespace_top"/>      
-          <v-card-title class="display-1 justify-center" style="text-shadow: 1px 1px #000000;">
-              Music Assistant
-          </v-card-title>
-        </v-card>
-      </v-flex>    
       <v-list>
         <v-list-tile 
           v-for="item in items" :key="item.title" @click="$router.push(item.path)">
@@ -34,13 +25,13 @@ var home = Vue.component("Home", {
     };
   },
   created() {
-    this.$globals.windowtitle = "Home"
+    this.$globals.windowtitle = this.$t('home');
     this.items= [
-        { title: "Artists", icon: "person", path: "/artists" },
-        { title: "Albums", icon: "album", path: "/albums" },
-        { title: "Tracks", icon: "audiotrack", path: "/tracks" },
-        { title: "Playlists", icon: "playlist_play", path: "/playlists" },
-        { title: "Search", icon: "search", path: "/search" }
+        { title: this.$t('artists'), icon: "person", path: "/artists" },
+        { title: this.$t('albums'), icon: "album", path: "/albums" },
+        { title: this.$t('tracks'), icon: "audiotrack", path: "/tracks" },
+        { title: this.$t('playlists'), icon: "playlist_play", path: "/playlists" },
+        { title: this.$t('search'), icon: "search", path: "/search" }
     ]
   },
   methods: {
index b2f97dc39454a6b7bf453488bffd50a74e691cec..9ee1da7411dd0aab1228a31181d9c26b0e2bf345 100755 (executable)
@@ -36,7 +36,7 @@ var PlaylistDetails = Vue.component('PlaylistDetails', {
     }
   },
   created() {
-    this.$globals.windowtitle = "Playlist info"
+    this.$globals.windowtitle = ""
     this.getInfo();
     this.getPlaylistTracks();
     this.scroll(this.Browse);
index 7f76632e8cabf577c3939521c35abca8e2f12106..d470208fb0656d80c03ea80cda899963828d8171 100755 (executable)
@@ -1,30 +1,17 @@
 var Queue = Vue.component('Queue', {
   template: `
   <section>
-      <infoheader v-bind:info="info"/>
-      <v-tabs
-          v-model="active"
-          color="transparent"
-          light
-          slider-color="black"
-        >
-          <v-tab ripple>Queue</v-tab>
-          <v-tab-item>
-            <v-card flat>
-            <v-list two-line>
-                  <listviewItem 
-                      v-for="(item, index) in items" 
-                      v-bind:item="item"
-                      :key="item.db_id"
-                      :hideavatar="isMobile()"
-                      :hidetracknum="true"
-                      :hideproviders="isMobile()"
-                      :hidelibrary="isMobile()">
-                  </listviewItem>
-                </v-list>
-            </v-card>
-          </v-tab-item>
-        </v-tabs>
+        <v-list two-line>
+        <listviewItem 
+            v-for="(item, index) in items" 
+            v-bind:item="item"
+            :key="item.db_id"
+            :hideavatar="isMobile()"
+            :hidetracknum="true"
+            :hideproviders="isMobile()"
+            :hidelibrary="isMobile()">
+        </listviewItem>
+      </v-list>
       </section>`,
   props: ['player_id'],
   data() {
@@ -36,8 +23,7 @@ var Queue = Vue.component('Queue', {
     }
   },
   created() {
-    this.$globals.windowtitle = "Queue"
-    //this.getInfo();
+    this.$globals.windowtitle = this.$t('queue')
     this.getQueueTracks();
     this.scroll(this.Queue);
   },
index 2bff1a6c40e686f5d80a1f7c79b2a8191d5e146a..b1a8be2997142d5eb7168d87d8a3cdebee537569 100755 (executable)
@@ -1,22 +1,13 @@
 var Search = Vue.component('Search', {
   template: `
   <section>
-      <v-flex xs12 justify-center>
-        <v-card color="cyan darken-2" class="white--text" img="../images/info_gradient.jpg">
-        
-          <div class="text-xs-center" style="height:40px" id="whitespace_top"/>      
-          <v-card-title class="display-1 justify-center" style="text-shadow: 1px 1px #000000;">
-              {{ $globals.windowtitle }}
-          </v-card-title>
-        </v-card>
-      </v-flex>    
-      <v-text-field
+
+    <v-text-field
         solo
         clearable
-        label="Type here to search..."
+        :label="$t('type_to_search')"
         prepend-inner-icon="search"
-        v-on:input="onSearchBoxInput"
-        v-model="searchquery">
+        v-model="searchQuery">
       </v-text-field>
 
       <v-tabs
@@ -26,7 +17,7 @@ var Search = Vue.component('Search', {
           slider-color="black"
         >
 
-        <v-tab ripple v-if="tracks.length">Tracks</v-tab>
+        <v-tab ripple v-if="tracks.length">{{ $t('tracks') }}</v-tab>
           <v-tab-item v-if="tracks.length">
             <v-card flat>
                 <v-list two-line style="margin-left:15px; margin-right:15px">
@@ -46,7 +37,7 @@ var Search = Vue.component('Search', {
             </v-card>
           </v-tab-item>
 
-          <v-tab ripple v-if="artists.length">Artists</v-tab>
+          <v-tab ripple v-if="artists.length">{{ $t('artists') }}</v-tab>
           <v-tab-item v-if="artists.length">
             <v-card flat>
             <v-list two-line>
@@ -63,7 +54,7 @@ var Search = Vue.component('Search', {
             </v-card>
           </v-tab-item>
 
-          <v-tab ripple v-if="albums.length">Albums</v-tab>
+          <v-tab ripple v-if="albums.length">{{ $t('albums') }}</v-tab>
           <v-tab-item v-if="albums.length">
             <v-card flat>
                 <v-list two-line>
@@ -80,7 +71,7 @@ var Search = Vue.component('Search', {
             </v-card>
           </v-tab-item>
 
-          <v-tab ripple v-if="playlists.length">Playlists</v-tab>
+          <v-tab ripple v-if="playlists.length">{{ $t('playlists') }}</v-tab>
           <v-tab-item v-if="playlists.length">
             <v-card flat>
                 <v-list two-line>
@@ -99,7 +90,7 @@ var Search = Vue.component('Search', {
         </v-tabs>
 
       </section>`,
-  props: [],
+  props: ['searchQuery'],
   data() {
     return {
       selected: [2],
@@ -108,11 +99,16 @@ var Search = Vue.component('Search', {
       tracks: [],
       playlists: [],
       timeout: null,
-      searchquery: ''
     }
   },
   created() {
-    this.$globals.windowtitle = "Search";
+    this.$globals.windowtitle = this.$t('search');
+    this.Search();
+  },
+  watch: {
+    searchQuery () {
+      this.Search();
+    }
   },
   methods: {
     toggle (index) {
@@ -124,26 +120,20 @@ var Search = Vue.component('Search', {
         console.log("selected: "+ this.items[index].name);
       }
     },
-    onSearchBoxInput (index) {
-      clearTimeout(this.timeout);
-      this.timeout = setTimeout(this.Search, 600);
-    },
     Search () {
-      if (!this.searchquery) {
-        this.artists = [];
-        this.albums = [];
-        this.tracks = [];
-        this.playlists = [];
-      }
-      else {
+      this.artists = [];
+      this.albums = [];
+      this.tracks = [];
+      this.playlists = [];
+      if (this.searchQuery) {
         this.$globals.loading = true;
-        console.log(this.searchquery);
+        console.log(this.searchQuery);
         const api_url = '/api/search'
         console.log('loading ' + api_url);
           axios
             .get(api_url, {
               params: {
-                query: this.searchquery,
+                query: this.searchQuery,
                 online: true,
                 limit: 3
               }
diff --git a/music_assistant/web/strings.js b/music_assistant/web/strings.js
new file mode 100644 (file)
index 0000000..051fa7e
--- /dev/null
@@ -0,0 +1,90 @@
+const messages = {
+    
+    
+    en: {
+        // generic strings
+        musicassistant: "Music Assistant",
+        home: "Home",
+        artists: "Artists",
+        albums: "Albums",
+        tracks: "Tracks",
+        playlists: "Playlists",
+        search: "Search",
+        settings: "Settings",
+        queue: "Queue",
+        generic_settings: "Generic settings",
+        music_providers: "Music providers",
+        player_providers: "Player providers",
+        player_settings: "Player settings",
+        type_to_search: "Type here to search...",
+        enabled: "Enabled",
+        // settings strings
+        username: "Username",
+        password: "Password",
+        hostname: "Hostname (or IP)",
+        port: "Port",
+        hass_url: "URL to homeassistant (e.g. https://homeassistant:8123)",
+        hass_token: "Long Lived Access Token",
+        hass_publish: "Publish players to Home Assistant",
+        hass_player_power: "Attach player power to homeassistant entity",
+        hass_player_source: "Source on the homeassistant entity (optional)",
+        hass_player_volume: "Attach player volume to homeassistant entity",
+        web_ssl_cert: "Path to ssl certificate file",
+        web_ssl_key: "Path to ssl keyfile",
+        web_ssl_host: "Hostname (FQDN used in the certificate)",
+        player_enabled: "Enable player",
+        player_name: "Custom name for this player",
+        player_group_with: "Group this player to another (parent)player",
+        player_mute_power: "Use muting as power control",
+        player_disable_vol: "Disable volume controls",
+        player_group_vol: "Apply group volume to childs (for group players only)",
+        player_group_pow: "Apply group power based on childs (for group players only)",
+        player_power_play: "Issue play command on power on",
+        file_prov_music_path: "Path to music files",
+        file_prov_playlists_path: "Path to playlists (.m3u)",
+
+    },
+
+    nl: {
+        // generic strings
+        musicassistant: "Music Assistant",
+        home: "Home",
+        artists: "Artiesten",
+        albums: "Albums",
+        tracks: "Nummers",
+        playlists: "Afspeellijsten",
+        search: "Zoeken",
+        settings: "Instellingen",
+        queue: "Wachtrij",
+        generic_settings: "Algemene instellingen",
+        music_providers: "Muziek providers",
+        player_providers: "Speler providers",
+        player_settings: "Speler instellingen",
+        enabled: "Ingeschakeld",
+        type_to_search: "Type hier om te zoeken...",
+        // settings strings
+        username: "Gebruikersnaam",
+        password: "Wachtwoord",
+        hostname: "Hostnaam (of IP)",
+        port: "Poort",
+        hass_url: "URL naar homeassistant (b.v. https://homeassistant:8123)",
+        hass_token: "Token met lange levensduur",
+        hass_publish: "Publiceer spelers naar Home Assistant",
+        hass_player_power: "Verbind speler aan/uit met homeassistant entity",
+        hass_player_source: "Benodigde bron op de verbonden homeassistant entity (optioneel)",
+        hass_player_volume: "Verbind volume van speler aan een homeassistant entity",
+        web_ssl_cert: "Pad naar ssl certificaat bestand",
+        web_ssl_key: "Pad naar ssl certificaat key bestand",
+        web_ssl_host: "Hostname (FQDN van certificaat)",
+        player_enabled: "Speler inschakelen",
+        player_name: "Aangepaste naam voor deze speler",
+        player_group_with: "Groupeer deze speler met een andere (hoofd)speler",
+        player_mute_power: "Gebruik mute als aan/uit",
+        player_disable_vol: "Schakel volume bediening helemaal uit",
+        player_group_vol: "Pas groep volume toe op onderliggende spelers (alleen groep spelers)",
+        player_group_pow: "Pas groep aan/uit toe op onderliggende spelers (alleen groep spelers)",
+        player_power_play: "Automatisch afspelen bij inschakelen",
+        file_prov_music_path: "Pad naar muziek bestanden",
+        file_prov_playlists_path: "Pad naar playlist bestanden (.m3u)",
+    }
+}
\ No newline at end of file