├── .gitignore
├── LICENSE
├── README.md
├── app
├── assets
│ ├── css
│ │ ├── base.css
│ │ ├── config.css
│ │ ├── main.css
│ │ └── unsemantic.css
│ ├── files
│ │ └── Hind-Regular.ttf
│ ├── img
│ │ ├── config.svg
│ │ ├── equalizer.svg
│ │ ├── folder.svg
│ │ ├── icon.png
│ │ ├── icon@1.25x.png
│ │ ├── icon@1.33x.png
│ │ ├── icon@1.4x.png
│ │ ├── icon@1.8x.png
│ │ ├── lang.svg
│ │ ├── legal.svg
│ │ ├── next.svg
│ │ ├── play.png
│ │ ├── play.svg
│ │ ├── prev.svg
│ │ ├── search.svg
│ │ ├── soube-icon.svg
│ │ ├── thumb-next.png
│ │ ├── thumb-pause.png
│ │ ├── thumb-play.png
│ │ └── thumb-prev.png
│ └── js
│ │ ├── config
│ │ ├── index.js
│ │ └── lang.json
│ │ ├── configMain.js
│ │ ├── dom
│ │ └── index.js
│ │ ├── factory
│ │ └── index.js
│ │ ├── main.js
│ │ ├── player
│ │ ├── addSongFolder
│ │ │ └── index.js
│ │ ├── controls
│ │ │ └── index.js
│ │ ├── createView
│ │ │ └── index.js
│ │ ├── equalizer
│ │ │ └── index.js
│ │ └── index.js
│ │ ├── thumbar
│ │ └── index.js
│ │ └── version
│ │ └── index.js
├── index.js
├── package.json
└── views
│ ├── config-panel
│ └── config.html
│ └── main
│ └── index.html
├── build
├── icon.icns
├── icon.ico
└── icons
│ ├── 144x144.png
│ ├── 192x192.png
│ ├── 192x292.png
│ ├── 48x48.png
│ ├── 72x72.png
│ └── 96x96.png
├── jsconfig.json
├── package.json
└── soube.desktop
/.gitignore:
--------------------------------------------------------------------------------
1 | .vscode
2 | node_modules
3 | dist
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Diego Molina Vera
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Soube
4 |
5 | ### This project is not longer maintained
6 |
7 | Thanks to all the people who downloaded the application and gave me feedback to improve it. Also, thanks to those who forked the project and those who gave me an start.
8 |
9 | Soube was a nice project from where I learned a lot from my mistakes, but for others reasons I stopped working on it for years. I wanted to improve the code, have a better folder structure, following JS best practices, and coding in a clean way (I think you can see that intention on the development branch). But this is not the end. The past years that I didn't work on Soube I was growing up as a developer, specially with JavaScript and some other importants Knowledge like Data Structure. Now I have a little bit of more knowledge that it will help me to bring Soube again but powerful and not only focused on local music file. This new Soube version won't be public until I decide it is stable and good enough to be share it with you all. I won't close this repository, so you are free to use it. Thanks you all!
10 |
11 | Soube is a simple and minimalist music player based on Electronjs.
12 |
13 | #### Install it on Windows, Mac & Linux
14 |
15 | [Soube website](http://soube.diegomolina.cl)
16 |
17 | ## Features
18 | * Notifications showing what song is played.
19 | * Auto detection of new songs.
20 | * Idiom. You can change the idiom of the config panel (Do this before anything).
21 | * Equalizer.
22 | * Responsive design.
23 | * Shorcuts to set play/pause,next, prev and disalbe/enable shuffle.
24 | * Searching by song.
25 |
26 | ## Linux users
27 | * For Distributions that don't use **rpm** or **deb** extensions, you have to follow the steps below.
28 |
29 | #### Step 1
30 |
31 | ```
32 | Extract the files and you will see the next folder:
33 | * soube-linux-ia32
34 | or
35 | * soube-linux-x64
36 | ```
37 |
38 | #### Step 2
39 | * Move the folder using the next command line.
40 |
41 | ```
42 | sudo mv [Place_where_is_soube_folder]/[soube-linux-ia32 or soube-linux-x64] /opt/soube
43 | ```
44 |
45 | #### step 3
46 | * Download the [soube.desktop](https://github.com/DracotMolver/Soube/blob/master/soube.desktop) file for an icon launcher and move it to this location (or your prefer one):
47 |
48 | ```
49 | sudo [Place_where_is_the_file]/soube.desktop /usr/share/applications
50 |
51 | ```
52 |
53 | * Done!. You should be ready to use Soube.
54 |
55 | ## Shortcuts
56 |
57 | * Ctrl + F // Display the searching option
58 | * Ctrl + Up // Set Play/Pause the song
59 | * Ctrl + Left // Prev song
60 | * Ctrl + Right // Next song
61 | * Ctrl + Down // Switch shuffle
62 |
--------------------------------------------------------------------------------
/app/assets/css/base.css:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Diego Alberto Molina Vera
3 | * @copyright 2016- 2017
4 | */
5 | /* ------------------------ GENERALES ------------------------ */
6 | @font-face {
7 | font-family: 'Hind';
8 | src: url(../files/Hind-Regular.ttf)
9 | }
10 |
11 | /**
12 | * Se realizan reset pero de los atributos más importantes
13 | * que vamos a usar en Chromium.
14 | * Al no ser una aplicación web "online", no debemos de preocuperanos, en parte,
15 | * por usar el comodín [*] para formatear todos los elementos HTML
16 | */
17 | h1,
18 | h2,
19 | h3,
20 | h4,
21 | h5,
22 | h6,
23 | span,
24 | div,
25 | p {
26 | -webkit-text-size-adjust: 100%;
27 | -webkit-font-smooth: antialiased;
28 | font-variant-ligatures: common-ligatures no-discretionary-ligatures no-historical-ligatures;
29 | font-stretch: condensed;
30 | font-family: 'Hind', sans-serif;
31 | font-weight: normal;
32 | line-height: 1.35em;
33 | font-size: 100%;
34 | padding: 0;
35 | margin: 0;
36 | cursor: default;
37 | color: var(--blackColor)
38 | }
39 |
40 | input,
41 | button {
42 | border: none
43 | }
44 |
45 | input:focus,
46 | button:focus {
47 | outline-width: 0
48 | }
49 |
50 | ul {
51 | list-style: none
52 | }
53 |
54 | body,
55 | html {
56 | overflow: hidden;
57 | padding: 0;
58 | margin: 0;
59 | bottom: 0;
60 | height: 100%;
61 | width: 100%;
62 | right: 0;
63 | left: 0;
64 | top: 0
65 | }
66 |
67 | img,
68 | svg,
69 | a {
70 | cursor: pointer
71 | }
72 |
73 | .hide {
74 | display: none
75 | }
76 |
77 | /* ------------------------ VARIABLES ------------------------ */
78 | :root {
79 | --lightGreyColor: #F4F6F6;
80 | --darkGreyColor: #979A9A;
81 | --darkPinkColor: #d81b60;
82 | --lightPinkColor: #f06292;
83 | --whiteColor: #FBFCFC;
84 | --blackColor: #424949;
85 | --pinkColor: #e91e63
86 | }
87 |
88 | /* ------------------------ GENERALES ------------------------ */
89 | .border-bottom,
90 | .border-left,
91 | .border-right {
92 | border-width: 0;
93 | border-style: solid;
94 | box-sizing: border-box;
95 | }
96 |
97 | .border-bottom {
98 | border-bottom-width: .01em
99 | }
100 |
101 | .border-left {
102 | border-left-width: .01em
103 | }
104 |
105 | .border-right {
106 | border-right-width: .01em
107 | }
108 |
109 | .border-color-dark-pink {
110 | border-color: var(--darkPinkColor)
111 | }
112 |
113 | .border-color-light-grey {
114 | border-color: var(--lightGreyColor)
115 | }
116 |
117 | .border-color-dark-grey {
118 | border-color: var(--darkGreyColor)
119 | }
120 |
121 | .border-color-white {
122 | border-color: var(--whiteColor)
123 | }
124 |
125 | .border-color-black {
126 | border-color: var(--blackColor)
127 | }
128 |
129 | .border-color-pink {
130 | border-color: var(--pinkColor)
131 | }
132 |
133 | .border-color-light-pink {
134 | border-color: var(--lightPinkColor)
135 | }
136 |
137 | /* ------------------------ SCROLLBAR ------------------------ */
138 | ::-webkit-scrollbar {
139 | background-color: var(--whiteColor);
140 | width: .8em
141 | }
142 |
143 | ::-webkit-scrollbar-thumb:window-inactive,
144 | ::-webkit-scrollbar-thumb {
145 | background: var(--blackColor)
146 | }
--------------------------------------------------------------------------------
/app/assets/css/config.css:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Diego Alberto Molina Vera
3 | * @copyright 2016- 2017
4 | */
5 | /* ------------------------ GENERALES ------------------------*/
6 | h1 {
7 | font-size: 1.1em
8 | }
9 |
10 | h4 {
11 | font-size: .9em
12 | }
13 |
14 | h5 {
15 | font-size: .5625em
16 | }
17 |
18 | #nav {
19 | background: var(--pinkColor)
20 | }
21 |
22 | #nav > h1 {
23 | font-weight: 400;
24 | padding: .2em .34em;
25 | float: left;
26 | color: var(--whiteColor)
27 | }
28 |
29 | #_titleconfig,
30 | .range-circle:hover,
31 | .config-icons > img:hover,
32 | #legal > h5,
33 | .lang-option:hover,
34 | a,
35 | #_neweq {
36 | cursor: pointer
37 | }
38 |
39 | .config-icons {
40 | position: relative;
41 | margin: 1.6em auto auto 0;
42 | height: 4.6em
43 | }
44 |
45 | .config-option p {
46 | margin-bottom: .5em;
47 | font-weight: 500;
48 | margin-top: .5em;
49 | text-align: center;
50 | font-size: .95em
51 | }
52 |
53 | .config-option h4 {
54 | font-weight: 300;
55 | text-align: center;
56 | color: var(--darkGreyColor)
57 | }
58 |
59 | .config-opt-anim {
60 | animation: confSlide 1s ease-in-out forwards
61 | }
62 |
63 | .range-circle::before,
64 | #hertz::before,
65 | #k-hertz::before{
66 | position: absolute;
67 | content: ''
68 | }
69 |
70 | /* ----------------------------- LEGAL ---------------------------- */
71 | #legal {
72 | background: var(--whiteColor);
73 | padding: 1em;
74 | border: .01em solid var(--lightGreyColor);
75 | margin: 2em auto;
76 | height: auto;
77 | width: 60em;
78 | }
79 |
80 | #legal > h5 {
81 | color: var(--darkGreyColor)
82 | }
83 |
84 | /* ------------------------ CONFIGURACIÓN CARPETA DE MÚSICA ------------------------*/
85 | #loading {
86 | background: rgba(245, 245, 245, .3);
87 | position: fixed;
88 | z-index: 3;
89 | height: 100%;
90 | width: 100%;
91 | cursor: wait;
92 | top: 0
93 | }
94 |
95 | #loading-info {
96 | margin-top: 18%;
97 | text-align: center;
98 | width: 100%;
99 | color: var(--blackColor);
100 | float: left
101 | }
102 |
103 | /* ------------------------ CONFIGURACIÓN DEL IDIOMA ------------------------*/
104 | #lang {
105 | box-sizing: border-box
106 | }
107 |
108 | .lang-option:hover {
109 | border-bottom: .1em solid #0277bd
110 | }
111 |
112 | .lang-option {
113 | margin-top: .6em;
114 | text-align: center;
115 | font-size: 2em
116 | }
117 |
118 | /* ----------------------------- EQUALIZADOR ---------------------------- */
119 | .range-container {
120 | border-radius: 5px;
121 | background: var(--darkGreyColor);
122 | padding: 0;
123 | margin: .5em 1.225em 1.4em;
124 | opacity: .5;
125 | height: 18em;
126 | width: .6em;
127 | float: left
128 | }
129 |
130 | .range-container:hover{
131 | opacity: 1
132 | }
133 |
134 | .range-circle {
135 | background: rgba(233,30,99, .14);
136 | border-radius: 50%;
137 | position: relative;
138 | border: .1em solid var(--lightPinkColor);
139 | height: 1.5em;
140 | width: 1.5em;
141 | left: -9px;
142 | top: 130px
143 | }
144 |
145 | .range-circle::before {
146 | border-radius: 50%;
147 | box-shadow: 0 1px 2px 1px rgba(0,0,0,.14);
148 | background: var(--darkPinkColor);
149 | border: .1em solid var(--pinkColor);
150 | height: .7em;
151 | width: .7em;
152 | left: 5px;
153 | top: 5px
154 | }
155 |
156 | #equalizer-container {
157 | margin-top: 1em;
158 | height: 100%;
159 | width: 100%;
160 | float: left
161 | }
162 |
163 | #equalizer-container ul {
164 | padding: 0;
165 | margin: .6em .2em;
166 | width: 100%;
167 | float: left
168 | }
169 |
170 | #equalizer-container ul > li {
171 | text-align: center;
172 | display: inline-block;
173 | width: 2.8em
174 | }
175 |
176 | #hertz {
177 | width: 35em
178 | }
179 |
180 | #k-hertz {
181 | width: 32.2em
182 | }
183 |
184 | #hertz,
185 | #k-hertz {
186 | padding-right: .5em;
187 | padding-left: .5em;
188 | text-align: center;
189 | float: left
190 | }
191 |
192 | #hertz > div,
193 | #k-hertz > div{
194 | background: white;
195 | margin: 0 auto;
196 | width: 3em;
197 | }
198 |
199 | #hertz::before,
200 | #k-hertz::before {
201 | background: var(--lightGreyColor);
202 | z-index: -1;
203 | height: 2px;
204 | top: 112px
205 | }
206 |
207 | #hertz::before {
208 | width: 35em;
209 | left: 13px
210 | }
211 |
212 | #k-hertz::before {
213 | width: 31em;
214 | left: 609px
215 | }
216 |
217 | #eq-buttons {
218 | background: var(--whiteColor)
219 | }
220 |
221 | #name-new-eq,
222 | #eq-buttons,
223 | #_neweq {
224 | border: 1px solid rgba(0,0,0,.14);
225 | border-radius: 4px;
226 | margin: 1em 0 .5em 1em
227 | }
228 |
229 | #name-new-eq {
230 | height: 28px;
231 | width: 58%
232 | }
233 |
234 | #_neweq {
235 | padding-right: .6em;
236 | padding-left: .6em;
237 | line-height: 1.9em;
238 | background: var(--darkGreyColor);
239 | color: var(--whiteColor)
240 | }
241 |
242 | #_neweq[disabled]{
243 | background: var(--whiteColor);
244 | color: #d4d4d4
245 | }
246 |
247 | /*---------------------------------- ANIMACIONES ----------------------------------*/
248 | @keyframes confSlide {
249 | 100% {
250 | transform: translateX(-100%);
251 | opacity: 0
252 | }
253 | }
--------------------------------------------------------------------------------
/app/assets/css/main.css:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Diego Alberto Molina Vera
3 | * @copyright 2016- 2017
4 | */
5 | /* ------------------------ GENERAL ------------------------*/
6 | .btn-controls::after,
7 | .arrow::after,
8 | .arrow::before,
9 | .list-song-container::after,
10 | .tooltip::after {
11 | position: absolute;
12 | content: ''
13 | }
14 |
15 | /* ------- CONTROL PARA MOVER LA LISTA DE CANCIONES --------*/
16 | #updown {
17 | border-radius: 5px;
18 | text-align: center;
19 | background: var(--whiteColor);
20 | box-shadow: 0 6px 10px 0 rgba(0, 0, 0, .3);
21 | position: absolute;
22 | z-index: 2;
23 | border: 1px solid var(--lightGreyColor);
24 | height: 30px;
25 | bottom: 10px;
26 | width: 66px;
27 | right: 22px
28 | }
29 |
30 | #updown:hover {
31 | cursor: pointer
32 | }
33 |
34 | #updown .arrow-updown:first-child {
35 | border-right: 1px solid var(--lightGreyColor)
36 | }
37 |
38 | #updown .arrow-updown{
39 | line-height: 31px
40 | }
41 |
42 | /* ------------------------ TOP NAV ------------------------*/
43 | #top-nav {
44 | background: var(--pinkColor)
45 | }
46 |
47 | #config {
48 | margin-top: 1.9em;
49 | padding-right: 35%;
50 | padding-left: 35%;
51 | height: 2em
52 | }
53 |
54 | /* ------------------------ CONTROLES DEL REPRODUCTOR ------------------------ */
55 | .btn-controls {
56 | text-align: center;
57 | margin: .3em 0;
58 | height: 1.2em;
59 | float: left
60 | }
61 |
62 | .btn-controls::after {
63 | border-radius: 50%;
64 | background: var(--darkPinkColor);
65 | opacity: 0;
66 | height: 1.7em;
67 | width: 1.7em;
68 | left: 104px
69 | }
70 |
71 | #play-pause {
72 | text-align: center;
73 | margin: 1.25em .9em;
74 | height: 3em
75 | }
76 |
77 | #play-pause::after {
78 | height: 4em;
79 | width: 4em;
80 | left: 14px;
81 | top: 12px
82 | }
83 |
84 | #shuffle-icon {
85 | fill: var(--lightPinkColor)
86 | }
87 |
88 | #next::after {
89 | top: 0
90 | }
91 |
92 | #shuffle::after {
93 | top: 61px
94 | }
95 |
96 | #prev::after {
97 | top: 31px
98 | }
99 |
100 | .click-controls::after {
101 | animation: clickControlsEffect .3s ease-out forwards
102 | }
103 |
104 | #total-progress-bar,
105 | #progress-bar {
106 | height: .36em;
107 | cursor: e-resize
108 | }
109 |
110 | #total-progress-bar {
111 | background: var(--lightPinkColor)
112 | }
113 |
114 | #progress-bar {
115 | background: var(--whiteColor);
116 | box-shadow: 1px 0 3px 0 #fff;
117 | float: left;
118 | width: 0
119 | }
120 |
121 | /* ------------------------ DETALLES DE LA CANCIÓN ------------------------ */
122 | #info-song {
123 | text-align: left;
124 | overflow: hidden;
125 | margin: .1em 1.2em;
126 | width: 85%;
127 | float: left
128 | }
129 |
130 | #info-song {
131 | -webkit-user-select: none;
132 | }
133 |
134 | #song-title > h1 {
135 | font-weight: 500;
136 | font-size: 1.6em
137 | }
138 |
139 | #artist > h2 {
140 | font-size: 1.3em
141 | }
142 |
143 | #album > h3 {
144 | font-weight: 300;
145 | font-size: 1em
146 | }
147 |
148 | #song-title > h1,
149 | #artist > h2,
150 | #album > h3,
151 | .search-results {
152 | text-overflow: ellipsis;
153 | overflow: hidden
154 | }
155 |
156 | #time {
157 | text-align: center;
158 | font-size: 1.16em;
159 | margin: .5em 0
160 | }
161 |
162 | #time-start,
163 | #time-end,
164 | #artist > h2 {
165 | font-weight: 400
166 | }
167 |
168 | #song-title > h1,
169 | #album > h3,
170 | #artist > h2,
171 | #time-end,
172 | #time-start
173 | /*#searchBy > option */
174 | {
175 | color: var(--whiteColor);
176 | }
177 |
178 | /* ------------------------ LISTADO DE CANCIONES ------------------------ */
179 | #list-songs {
180 | background: linear-gradient(var(--whiteColor) 30%, transparent),
181 | radial-gradient(at 50% 0, rgba(66,73,73, .3), transparent 80%);
182 | background-repeat: no-repeat;
183 | background-size: 100% 50px, 100% 15px;
184 | background-attachment: local, scroll;
185 | overflow: hidden;
186 | overflow-y: scroll;
187 | height: calc(100% - 95px);
188 | width: 100%;
189 | float: left
190 | }
191 |
192 | .list-song-container:nth-child(odd) {
193 | background: var(--lightGreyColor)
194 | }
195 |
196 | .list-song-container:nth-child(even) {
197 | background: var(--whiteColor)
198 | }
199 |
200 | .song-info {
201 | text-overflow: ellipsis;
202 | padding-left: .6em;
203 | font-weight: 300;
204 | line-height: 2em;
205 | font-size: .906em;
206 | overflow: hidden
207 | }
208 |
209 | .miscelaneo {
210 | margin-right: .6em;
211 | color: var(--darkGreyColor)
212 | }
213 |
214 | /* ------------------------ BUSQUEDA DE CANCIONES ------------------------ */
215 | .search-results {
216 | margin: 1em
217 | }
218 |
219 | #search-container {
220 | background: rgba(245, 245, 245, .3);
221 | position: fixed;
222 | z-index: 10;
223 | height: 100%;
224 | width: 100%;
225 | top: 0
226 | }
227 |
228 | #search-wrapper {
229 | position: relative;
230 | padding: 6em 0 3.5em 0;
231 | margin: auto;
232 | height: auto;
233 | width: 11%
234 | }
235 |
236 | #search {
237 | background-image: url(../../assets/img/search.svg);
238 | background-repeat: no-repeat;
239 | background-color: transparent;
240 | background-size: 1.6em;
241 | border-bottom: .1em solid var(--lightPinkColor);
242 | padding-left: 1.6em;
243 | line-height: 0;
244 | transform: scale(0);
245 | margin: 0;
246 | cursor: text;
247 | width: 94%;
248 | color: var(--blackColor)
249 | }
250 |
251 | #search,
252 | #search-result {
253 | font-weight: 300;
254 | font-size: 1.6em
255 | }
256 |
257 | #search-result {
258 | padding-left: 1.6em;
259 | position: absolute;
260 | z-index: -1;
261 | color: rgba(182, 182, 182, .4);
262 | top: 3.9em
263 | }
264 |
265 | /*#searchBy {
266 | border-radius: 4px;
267 | padding-left: 7px;
268 | background: var(--darkGreyColor);
269 | height: 42px;
270 | border: none;
271 | width: 75%;
272 | color: var(--whiteColor);
273 | }*/
274 |
275 | .search-anim {
276 | animation: showInputUp .5s ease-in forwards
277 | }
278 |
279 | .search-wrapper-anim {
280 | animation: displayInputSearch .5s .6s ease-in-out forwards
281 | }
282 |
283 | #container-results {
284 | overflow: hidden;
285 | height: 370px
286 | }
287 |
288 | .results,
289 | #wrapper-results{
290 | height: 100%;
291 | float: left
292 | }
293 |
294 | .no-searching-found {
295 | text-align: center;
296 | font-size: 2.6em
297 | }
298 |
299 | /* ------------------------ PAGINACIÓN DEL INPUT SEARCH ------------------------ */
300 | #pagination {
301 | position: relative;
302 | height: 30px;
303 | margin: 0 auto;
304 | width: 400px;
305 | top: 1em
306 | }
307 |
308 | .arrow::after,
309 | .arrow::before {
310 | border-radius: 25px;
311 | background: var(--blackColor);
312 | opacity: .2;
313 | height:.3em;
314 | width: 10%
315 | }
316 |
317 | .arrow-open-anim::after {
318 | animation: openArrowDown .6s ease-out forwards
319 | }
320 |
321 | .arrow-open-anim::before {
322 | animation: openArrowUp .6s ease-out forwards
323 | }
324 |
325 | #left-arrow,
326 | #right-arrow {
327 | cursor: pointer;
328 | }
329 |
330 | #left-arrow {
331 | float: left
332 | }
333 |
334 | #left-arrow::before,
335 | #left-arrow::after {
336 | transform-origin: left
337 | }
338 |
339 | #right-arrow::before,
340 | #right-arrow::after {
341 | transform-origin: right
342 | }
343 |
344 | /* -------------------------------------- */
345 | #right-arrow::after,
346 | #left-arrow::before { /* Para obtener una mejor simetría en los ejes que se unen */
347 | top: 2px
348 | }
349 |
350 | #right-arrow::before,
351 | #left-arrow::after { /* Para obtener una mejor simetría en los ejes que se unen */
352 | top: 0
353 | }
354 |
355 | /* -------------------------------------- */
356 | #right-arrow {
357 | float: right
358 | }
359 |
360 | /* ------------------------ MENSAJE DE BIENVENIDA ------------------------ */
361 | #init-message {
362 | background: white !important;
363 | margin-top: 4em;
364 | text-align: center;
365 | font-size: 1.6em;
366 | color: var(--darkGreyColor)
367 | }
368 |
369 | /* ------------------------ MENSAJE DE CANCIONES NUEVAS ------------------------ */
370 | #pop-up-container {
371 | position: absolute;
372 | height: auto;
373 | margin: 0 2%;
374 | width: auto;
375 | right: 0;
376 | left: 0;
377 | top: 6.4em
378 | }
379 |
380 | #pop-up,
381 | .search-results {
382 | border-radius: 2px;
383 | border-bottom: 2px solid var(--pinkColor);
384 | background: var(--darkGreyColor);
385 | text-align: center;
386 | font-size: .94em;
387 | padding: .6em;
388 | color: var(--whiteColor)
389 | }
390 |
391 | #pop-up {
392 | transform: scale(0)
393 | }
394 |
395 | #pop-up > a {
396 | text-decoration: none;
397 | color: currentColor
398 | }
399 |
400 | .pop-up-anim {
401 | animation: showPopUp .3s ease-in-out forwards
402 | }
403 |
404 | /* ------------------------ TOOLTIPS ------------------------ */
405 | .tooltip {
406 | transform-origin: center;
407 | letter-spacing: .14em;
408 | border-radius: 10px;
409 | line-height: 2em;
410 | text-align: center;
411 | background: hsla(180, 5%, 27%, .8);
412 | font-size: .8em;
413 | transform: scale(1, 0);
414 | position: absolute;
415 | opacity: 0;
416 | padding: 0 1em;
417 | width: auto;
418 | color: var(--whiteColor);
419 | }
420 |
421 | .tooltip::after {
422 | border-style: solid;
423 | border-width: 0 8px 11px 8px;
424 | border-color: transparent transparent hsla(180, 5%, 27%, .8) transparent;
425 | height: 0;
426 | margin: 0 auto;
427 | width: 0;
428 | right: 0;
429 | left: 0;
430 | top: -11px
431 | }
432 |
433 | .text-container:hover > .tooltip {
434 | animation: showToolTip .3s ease-in forwards
435 | }
436 |
437 | /* ------------------------ CANCIÓN SELECCIONADA ------------------------ */
438 | .list-song-container {
439 | width: 100%;
440 | float: left;
441 | cursor: pointer
442 | }
443 |
444 | .list-song-container::after {
445 | border-radius: 25px;
446 | background: rgba(66, 73, 73, 0);
447 | height: 28px;
448 | margin: 0 auto;
449 | width: 0;
450 | right: 0;
451 | left: 0;
452 | top: 188px
453 | }
454 |
455 | /* ------------------------ ANIMACIONES ------------------------ */
456 | @keyframes showToolTip {
457 | 100% {
458 | transform: scale(1);
459 | opacity: 1
460 | }
461 | }
462 |
463 | @keyframes showPopUp {
464 | 100% {
465 | transform: scale(1)
466 | }
467 | }
468 |
469 | @keyframes displayInputSearch {
470 | 100% {
471 | width: 75%
472 | }
473 | }
474 |
475 | @keyframes showInputUp {
476 | 100% {
477 | transform: scale(1)
478 | }
479 | }
480 |
481 | @keyframes clickControlsEffect {
482 | 0% {
483 | transform: scale3d(.3, .3, 1)
484 | }
485 | 25%, 50% {
486 | opacity: .8
487 | }
488 | 100% {
489 | transform: scale3d(1.2, 1.2, 1);
490 | opacity: 0
491 | }
492 | }
493 |
494 | @keyframes openArrowDown {
495 | 0% {
496 | transform: rotate(0)
497 | }
498 | 100% {
499 | transform: rotate(40deg);
500 | opacity: 1
501 | }
502 | }
503 |
504 | @keyframes openArrowUp {
505 | 0% {
506 | transform: rotate(0)
507 | }
508 | 100% {
509 | transform: rotate(-40deg);
510 | opacity: 1
511 | }
512 | }
513 |
514 | /* ------------------------ MEDIA QUERIES ------------------------ */
515 | @media only screen and (max-width: 750px) {
516 | /* ------------------------ BUSQUEDA DE CANCIONES ------------------------ */
517 | .no-searching-found {
518 | font-size: 2em
519 | }
520 |
521 | /* ------------------------ CONTROLES DEL REPRODUCTOR ------------------------ */
522 | #total-progress-bar{
523 | margin: .1em 1.3em .5em
524 | }
525 |
526 | .prev-next::after {
527 | left: 14.7%
528 | }
529 |
530 | /* ------------------------ TOP NAV ------------------------*/
531 | #config-container {
532 | border-radius: 50%;
533 | background: var(--pinkColor);
534 | position: absolute;
535 | padding: .3em;
536 | bottom: 7px;
537 | z-index: 4;
538 | height: 2.6em;
539 | width: 2.6em;
540 | right: 16px
541 | }
542 |
543 | #config {
544 | padding: 0;
545 | margin: 0;
546 | height: 100%;
547 | width: 100%;
548 | float: left
549 | }
550 |
551 | #updown {
552 | right: 70px
553 | }
554 | /* ------------------------ DETALLES DE LA CANCIÓN ------------------------ */
555 | #info-song {
556 | text-align: left;
557 | margin: .3em 1.2em;
558 | width: 93%
559 | }
560 |
561 | #info-song > div {
562 | overflow: hidden
563 | }
564 |
565 | #song-title > h1 {
566 | font-size: 1.16em
567 | }
568 |
569 | #artist > h2,
570 | #album > h3 {
571 | font-size: 1em
572 | }
573 |
574 | #time {
575 | font-size: 1em;
576 | margin: 0
577 | }
578 |
579 | /* ------------------------ LISTADO DE CANCIONES ------------------------ */
580 | .list-song-container::after {
581 | top: 193px
582 | }
583 | }
--------------------------------------------------------------------------------
/app/assets/css/unsemantic.css:
--------------------------------------------------------------------------------
1 | .clear{clear:both;display:block;overflow:hidden;visibility:hidden;width:0;height:0}.grid-container:before,.mobile-grid-5:before,.mobile-grid-10:before,.mobile-grid-15:before,.mobile-grid-20:before,.mobile-grid-25:before,.mobile-grid-30:before,.mobile-grid-35:before,.mobile-grid-40:before,.mobile-grid-45:before,.mobile-grid-50:before,.mobile-grid-55:before,.mobile-grid-60:before,.mobile-grid-65:before,.mobile-grid-70:before,.mobile-grid-75:before,.mobile-grid-80:before,.mobile-grid-85:before,.mobile-grid-90:before,.mobile-grid-95:before,.mobile-grid-100:before,.mobile-grid-33:before,.mobile-grid-66:before,.grid-5:before,.grid-10:before,.grid-15:before,.grid-20:before,.grid-25:before,.grid-30:before,.grid-35:before,.grid-40:before,.grid-45:before,.grid-50:before,.grid-55:before,.grid-60:before,.grid-65:before,.grid-70:before,.grid-75:before,.grid-80:before,.grid-85:before,.grid-90:before,.grid-95:before,.grid-100:before,.grid-33:before,.grid-66:before,.grid-offset:before,.clearfix:before,.grid-container:after,.mobile-grid-5:after,.mobile-grid-10:after,.mobile-grid-15:after,.mobile-grid-20:after,.mobile-grid-25:after,.mobile-grid-30:after,.mobile-grid-35:after,.mobile-grid-40:after,.mobile-grid-45:after,.mobile-grid-50:after,.mobile-grid-55:after,.mobile-grid-60:after,.mobile-grid-65:after,.mobile-grid-70:after,.mobile-grid-75:after,.mobile-grid-80:after,.mobile-grid-85:after,.mobile-grid-90:after,.mobile-grid-95:after,.mobile-grid-100:after,.mobile-grid-33:after,.mobile-grid-66:after,.grid-5:after,.grid-10:after,.grid-15:after,.grid-20:after,.grid-25:after,.grid-30:after,.grid-35:after,.grid-40:after,.grid-45:after,.grid-50:after,.grid-55:after,.grid-60:after,.grid-65:after,.grid-70:after,.grid-75:after,.grid-80:after,.grid-85:after,.grid-90:after,.grid-95:after,.grid-100:after,.grid-33:after,.grid-66:after,.grid-offset:after,.clearfix:after{content:".";display:block;overflow:hidden;visibility:hidden;font-size:0;line-height:0;width:0;height:0}.grid-container:after,.mobile-grid-5:after,.mobile-grid-10:after,.mobile-grid-15:after,.mobile-grid-20:after,.mobile-grid-25:after,.mobile-grid-30:after,.mobile-grid-35:after,.mobile-grid-40:after,.mobile-grid-45:after,.mobile-grid-50:after,.mobile-grid-55:after,.mobile-grid-60:after,.mobile-grid-65:after,.mobile-grid-70:after,.mobile-grid-75:after,.mobile-grid-80:after,.mobile-grid-85:after,.mobile-grid-90:after,.mobile-grid-95:after,.mobile-grid-100:after,.mobile-grid-33:after,.mobile-grid-66:after,.grid-5:after,.grid-10:after,.grid-15:after,.grid-20:after,.grid-25:after,.grid-30:after,.grid-35:after,.grid-40:after,.grid-45:after,.grid-50:after,.grid-55:after,.grid-60:after,.grid-65:after,.grid-70:after,.grid-75:after,.grid-80:after,.grid-85:after,.grid-90:after,.grid-95:after,.grid-100:after,.grid-33:after,.grid-66:after,.grid-offset:after,.clearfix:after{clear:both}.grid-container{margin-left:auto;margin-right:auto;max-width:100%;height:100%}.mobile-grid-5,.mobile-grid-10,.mobile-grid-15,.mobile-grid-20,.mobile-grid-25,.mobile-grid-30,.mobile-grid-35,.mobile-grid-40,.mobile-grid-45,.mobile-grid-50,.mobile-grid-55,.mobile-grid-60,.mobile-grid-65,.mobile-grid-70,.mobile-grid-75,.mobile-grid-80,.mobile-grid-85,.mobile-grid-90,.mobile-grid-95,.mobile-grid-100,.mobile-grid-33,.mobile-grid-66,.grid-5,.grid-10,.grid-15,.grid-20,.grid-25,.grid-30,.grid-35,.grid-40,.grid-45,.grid-50,.grid-55,.grid-60,.grid-65,.grid-70,.grid-75,.grid-80,.grid-85,.grid-90,.grid-95,.grid-100,.grid-33,.grid-66{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.grid-parent{padding-left:0;padding-right:0}.grid-offset{clear:both;margin-left:-10px;margin-right:-10px}@media (max-width: 767px){.hide-on-mobile{display:none!important}.mobile-grid-5{float:left;width:5%}.mobile-grid-10{float:left;width:10%}.mobile-grid-15{float:left;width:15%}.mobile-grid-20{float:left;width:20%}.mobile-grid-25{float:left;width:25%}.mobile-grid-30{float:left;width:30%}.mobile-grid-35{float:left;width:35%}.mobile-grid-40{float:left;width:40%}.mobile-grid-45{float:left;width:45%}.mobile-grid-50{float:left;width:50%}.mobile-grid-55{float:left;width:55%}.mobile-grid-60{float:left;width:60%}.mobile-grid-65{float:left;width:65%}.mobile-grid-70{float:left;width:70%}.mobile-grid-75{float:left;width:75%}.mobile-grid-80{float:left;width:80%}.mobile-grid-85{float:left;width:85%}.mobile-grid-90{float:left;width:90%}.mobile-grid-95{float:left;width:95%}.mobile-grid-33{float:left;width:33.33333%}.mobile-grid-66{float:left;width:66.66667%}.mobile-grid-100{clear:both;width:100%}}@media (min-width: 768px){.hide-on-desktop{display:none!important}.grid-5{float:left;width:5%}.grid-10{float:left;width:10%}.grid-15{float:left;width:15%}.grid-20{float:left;width:20%}.grid-25{float:left;width:25%}.grid-30{float:left;width:30%}.grid-35{float:left;width:35%}.grid-40{float:left;width:40%}.grid-45{float:left;width:45%}.grid-50{float:left;width:50%}.grid-55{float:left;width:55%}.grid-60{float:left;width:60%}.grid-65{float:left;width:65%}.grid-70{float:left;width:70%}.grid-75{float:left;width:75%}.grid-80{float:left;width:80%}.grid-85{float:left;width:85%}.grid-90{float:left;width:90%}.grid-95{float:left;width:95%}.grid-33{float:left;width:33.33333%}.grid-66{float:left;width:66.66667%}.grid-100{clear:both;width:100%}}
--------------------------------------------------------------------------------
/app/assets/files/Hind-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DracotMolver/soube/8e801be2b8318aab4078790e67aaccb3a402c336/app/assets/files/Hind-Regular.ttf
--------------------------------------------------------------------------------
/app/assets/img/config.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/img/equalizer.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/img/folder.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/img/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DracotMolver/soube/8e801be2b8318aab4078790e67aaccb3a402c336/app/assets/img/icon.png
--------------------------------------------------------------------------------
/app/assets/img/icon@1.25x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DracotMolver/soube/8e801be2b8318aab4078790e67aaccb3a402c336/app/assets/img/icon@1.25x.png
--------------------------------------------------------------------------------
/app/assets/img/icon@1.33x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DracotMolver/soube/8e801be2b8318aab4078790e67aaccb3a402c336/app/assets/img/icon@1.33x.png
--------------------------------------------------------------------------------
/app/assets/img/icon@1.4x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DracotMolver/soube/8e801be2b8318aab4078790e67aaccb3a402c336/app/assets/img/icon@1.4x.png
--------------------------------------------------------------------------------
/app/assets/img/icon@1.8x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DracotMolver/soube/8e801be2b8318aab4078790e67aaccb3a402c336/app/assets/img/icon@1.8x.png
--------------------------------------------------------------------------------
/app/assets/img/lang.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/img/legal.svg:
--------------------------------------------------------------------------------
1 |
44 |
--------------------------------------------------------------------------------
/app/assets/img/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/img/play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DracotMolver/soube/8e801be2b8318aab4078790e67aaccb3a402c336/app/assets/img/play.png
--------------------------------------------------------------------------------
/app/assets/img/play.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/assets/img/prev.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/img/search.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/img/soube-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
165 |
--------------------------------------------------------------------------------
/app/assets/img/thumb-next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DracotMolver/soube/8e801be2b8318aab4078790e67aaccb3a402c336/app/assets/img/thumb-next.png
--------------------------------------------------------------------------------
/app/assets/img/thumb-pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DracotMolver/soube/8e801be2b8318aab4078790e67aaccb3a402c336/app/assets/img/thumb-pause.png
--------------------------------------------------------------------------------
/app/assets/img/thumb-play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DracotMolver/soube/8e801be2b8318aab4078790e67aaccb3a402c336/app/assets/img/thumb-play.png
--------------------------------------------------------------------------------
/app/assets/img/thumb-prev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DracotMolver/soube/8e801be2b8318aab4078790e67aaccb3a402c336/app/assets/img/thumb-prev.png
--------------------------------------------------------------------------------
/app/assets/js/config/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Diego Alberto Molina Vera
3 | * @copyright 2016 - 2017
4 | */
5 | /* --------------------------------- Modules --------------------------------- */
6 | //---- nodejs ----
7 | const fs = require('fs');
8 |
9 | //---- electron ----
10 | const {
11 | remote,
12 | net
13 | } = require('electron');
14 |
15 | //---- own ----
16 | const version = require('./../version');
17 |
18 | /* --------------------------------- Functions --------------------------------- */
19 | // Will create all the files needed by the music player.
20 | // Some old files (old soubes versions) will be overwritens.
21 | // This function will checks for two files:
22 | // - config.json
23 | // - listSong.json
24 | function createFiles(app) {
25 | /* --------------------------------- Configuration --------------------------------- */
26 | //---- constants ----
27 | const PATH = app.getPath('userData');
28 | const CONFIG_PATH = `${PATH}/config.json`;
29 | const LIST_SONG_PATH = `${PATH}/listSong.json`;
30 |
31 | if (!fs.existsSync(CONFIG_PATH)) {
32 | // Values by default
33 | const CONFIG = {
34 | lang: 'us',
35 | shuffle: true,
36 | musicFolder: '',
37 | equalizer: {
38 | reset: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
39 | rock: [80, 103, 105, 121, 145, 128, 125, 123, 122, 143, 163, 134, 135, 129, 139, 146, 144, 153, 152, 149, 124, 102, 103],
40 | electro: [99, 133, 102, 122, 100, 139, 125, 151, 158, 152, 124, 116, 116, 117, 147, 100, 139, 173, 112, 135, 165, 85, 121],
41 | acustic: [104, 124, 141, 0, 0, 104, 0, 104, 117, 0, 0, 0, 107, 104, 109, 123, 92, 107, 0, 154, 113, 84, 90]
42 | },
43 | equalizerConfig: 'reset'
44 | };
45 |
46 | fs.openSync(CONFIG_PATH, 'w');
47 | fs.writeFileSync(CONFIG_PATH, JSON.stringify(CONFIG, null), { flag: 'w' });
48 | }
49 | // else {
50 | // // ONLY TO UPDATE THE CONFIG FILE
51 | // var actualVersion = app.getVersion().toString();
52 | // version(net, actualVersion, response => {
53 | // if (response === 'major' && actualVersion === '1.3.2') {
54 | // let config = JSON.parse(fs.readFileSync(CONFIG_PATH).toString());
55 | // config.equalizer = {
56 | // reset: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
57 | // rock: [80, 103, 105, 121, 145, 128, 125, 123, 122, 143, 163, 134, 135, 129, 139, 146, 144, 153, 152, 149, 124, 102, 103],
58 | // electro: [99, 133, 102, 122, 100, 139, 125, 151, 158, 152, 124, 116, 116, 117, 147, 100, 139, 173, 112, 135, 165, 85, 121],
59 | // acustic: [104, 124, 141, 0, 0, 104, 0, 104, 117, 0, 0, 0, 107, 104, 109, 123, 92, 107, 0, 154, 113, 84, 90]
60 | // };
61 | // config.equalizerConfig = 'reset';
62 | // fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null), { flag: 'w' });
63 | // }
64 | // });
65 | // }
66 |
67 | /* --------------------------------- File of songs --------------------------------- */
68 | if (!fs.existsSync(LIST_SONG_PATH)) {
69 | fs.openSync(LIST_SONG_PATH, 'w');
70 | fs.writeFileSync(LIST_SONG_PATH, JSON.stringify({}, null), { flag: 'w' });
71 | }
72 | }
73 |
74 | // Will save the files config.json and listSong.json if needed.
75 | function editFile(fileName, data) {
76 | //---- constants ----
77 | fs.writeFile(`${remote.app.getPath('userData')}/${fileName}.json`, JSON.stringify(data, null), err => { });
78 | }
79 |
80 | // Will get all the config files.
81 | // config.json [.confg] path
82 | // lang.json [local project] path
83 | // listSong.json [.config] path
84 | function init() {
85 | return {
86 | editFile,
87 | configFile: require(`${remote.app.getPath('userData')}/config.json`),
88 | listSongs: require(`${remote.app.getPath('userData')}/listSong.json`),
89 | langFile: require('./lang.json')
90 | }
91 | }
92 |
93 | module.exports = Object.freeze({
94 | createFiles,
95 | init
96 | });
--------------------------------------------------------------------------------
/app/assets/js/config/lang.json:
--------------------------------------------------------------------------------
1 | {
2 | "es": {
3 | "title": "Desconocida",
4 | "artist": "Desconocido",
5 | "album": "Desconocido",
6 | "config": {
7 | "addSongFolder": "Carpeta música",
8 | "changeLanguage": "Cambiar idioma",
9 | "statusLanguage": "Español",
10 | "titleConfig": "Configuración",
11 | "addAccounts": "Cuentas",
12 | "equalizerSetting": "Ecualizador",
13 | "statusSongFolder": "No hay carpeta de música",
14 | "loadingSongFolder": "Cargando canciones: %d / %d
Paciencia, se están extrayendo los metadatos :)",
15 | "legal": "Términos legales",
16 | "newEQ": "Nuevo"
17 | },
18 | "alerts": {
19 | "newVersion": "Tenemos una nueva versión!. Click para ir descargarlo.",
20 | "newSongsFound": "Agregando nuevas canciones",
21 | "welcome": "No hay una carpeta de música agregada.
Por favor, agrega tu carpeta de música dando click en esta ventana blanca o en el icono de configuración",
22 | "error_001": "Falta un archivo necesario para que funcione el reproductor. Ir a: https:\/\/github.com\/DracotMolver\/Soube y reportar el error. Gracias",
23 | "error_002": "No hay archivos para reproducir. Por favor, ir al icono de configuración y agregar tu carpeta de música",
24 | "error_003": "Se ha producido un error al intentar leer la carpeta: "
25 | },
26 | "searchBy": [
27 | "Canción",
28 | "Artista",
29 | "Álbum"
30 | ]
31 | },
32 | "us": {
33 | "title": "Unknown",
34 | "artist": "Unknown",
35 | "album": "Unknown",
36 | "config": {
37 | "addSongFolder": "Music folder",
38 | "changeLanguage": "Change language",
39 | "statusLanguage": "English",
40 | "titleConfig": "Configuration",
41 | "addAccounts": "Accounts",
42 | "equalizerSetting": "Equalizer",
43 | "statusSongFolder": "No music folder",
44 | "loadingSongFolder": "Loading songs: %d1 / %d2
Patience, extracting metadata",
45 | "legal": "Legal terms",
46 | "newEQ": "New"
47 | },
48 | "alerts": {
49 | "newVersion": "We've got a new version!. Click to download it.",
50 | "newSongsFound": "Adding new songs ",
51 | "welcome": "There is no music folder added.
Please, add your music folder doing click on this white frame or in the config icon",
52 | "error_001": "Missed a file needed to use the music player. Go to: https:\/\/github.com\/DracotMolver\/Soube and report the error. Thanks.",
53 | "error_002": "There are no files to play. Please, click on the config icon to add your music folder.",
54 | "error_003": "An error occured trying to read the folder: "
55 | },
56 | "searchBy": [
57 | "Song",
58 | "Artist",
59 | "Album"
60 | ]
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/app/assets/js/configMain.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Diego Alberto Molina Vera
3 | * @copyright 2016 - 2017
4 | */
5 | /* --------------------------------- Modules --------------------------------- */
6 | //---- electron ----
7 | const {
8 | shell,
9 | ipcRenderer,
10 | remote
11 | } = require('electron');
12 |
13 | //---- own ----
14 | const factory = require('./factory');
15 | const player = factory('player');
16 | const EQ = factory('equilizer');
17 | const {
18 | configFile,
19 | langFile,
20 | editFile
21 | } = require('./config').init();
22 | require('./dom');
23 |
24 | /* --------------------------------- Variables --------------------------------- */
25 | let lang = langFile[configFile.lang];
26 | let eqHrz = [];
27 | let newEQHrz = [];
28 | let actualPanel = null;
29 |
30 | /* --------------------------------- Functions --------------------------------- */
31 | // Change the text in the config window
32 | (function updateTextContet() {
33 | $('#_addsongfolder').text(lang.config.addSongFolder);
34 | $('#_statussongfolder').text(configFile.musicFolder === '' ? lang.config.statusSongFolder : configFile.musicFolder);
35 | $('#_changelanguage').text(lang.config.changeLanguage);
36 | $('#_statuslanguage').text(lang.config.statusLanguage);
37 | $('#_titleconfig').text(lang.config.titleConfig);
38 | $('#_equalizersetting').text(lang.config.equalizerSetting);
39 | $('#_legal').text(lang.config.legal);
40 | $('#_neweq').text(lang.config.newEQ);
41 | })();
42 |
43 | // Animation of the panel when select an option
44 | function animConfigPanel(e, text) {
45 | actualPanel = $(`#${$(e).data('action')}`).removeClass('hide');
46 |
47 | $('#config-container-options')
48 | .addClass('config-opt-anim')
49 | .on({
50 | 'animationend': function () {
51 | $('#config-container-values').removeClass('hide');
52 | $(this).addClass('hide');
53 | }
54 | });
55 |
56 | $('#_titlesubconfig').text(` > ${text}`);
57 | }
58 |
59 | // Change the lang of the music player
60 | function onClickChangeLang() {
61 | animConfigPanel(this, lang.config.changeLanguage);
62 |
63 | $('.lang-option').on({
64 | 'click': function () {
65 | configFile.lang = $(this).data('lang');
66 | editFile('config', configFile);
67 | remote.getCurrentWindow().reload();
68 | }
69 | });
70 | }
71 |
72 | // Get the path song
73 | function saveSongList(parentFolder = '') {
74 | configFile.musicFolder = parentFolder;
75 | editFile('listSong', {});
76 | editFile('config', configFile);
77 |
78 | $('#folder-status').child(0).text(parentFolder);
79 |
80 | // Show a loading
81 | // Read the content of the parent folder
82 | player.addSongFolder(parentFolder, () => {
83 | $('#loading').removeClass('hide');
84 | $($('.grid-container').get(0))
85 | .css('-webkit-filter:blur(2px)');
86 | }, (i, maxLength) => { // Iterator function
87 | $('#_loading-info').text(`${lang.config.loadingSongFolder.replace('%d1', i).replace('%d2', maxLength)}`);
88 |
89 | if (i === maxLength) {
90 | // Ocultar loading
91 | $('#loading').addClass('hide');
92 | $($('.grid-container').get(0)).rmAttr('style');
93 | remote.BrowserWindow.getAllWindows()[0].reload()
94 | // ipcRenderer.send('display-list');
95 | // remote.webContents.getAllWebContents
96 | }
97 | });
98 | }
99 |
100 | // Animation over the buttons in the EQ panel
101 | function onEqualizerPanel(e) {
102 | animConfigPanel(this, lang.config.equalizerSetting);
103 |
104 | EQ.onDragMove(data => {
105 | ipcRenderer.send('equalizer-filter', data);
106 | });
107 |
108 | EQ.onDragEnd((pos, db) => {
109 | newEQHrz[pos] = db;
110 | });
111 |
112 | // Set the EQ config choosen
113 | newEQHrz = eqHrz = configFile.equalizer[configFile.equalizerConfig];
114 | $('.range-circle').each((v, i) => {
115 | $(v).css(`top:${eqHrz[i] === 0 ? 130 : eqHrz[i]}px`);
116 | }).on({
117 | mousedown: function () {
118 | EQ.onDragStart(this);
119 | }
120 | });
121 | }
122 |
123 | // Options to config the EQ
124 | function setEQ () {
125 | configFile.equalizerConfig = this.value;
126 | editFile('config', configFile);
127 |
128 | eqHrz = configFile.equalizer[configFile.equalizerConfig];
129 | $('.range-circle').each((v, i) => {
130 | $(v).css(`top:${eqHrz[i] === 0 ? 130 : eqHrz[i]}px`);
131 |
132 | ipcRenderer.send('equalizer-filter', [i,
133 | eqHrz[i] !== 0 ? parseFloat((eqHrz[i] < 130 ? 121 - eqHrz[i] : -eqHrz[i] + 140) / 10) : 0
134 | ]);
135 | });
136 | }
137 |
138 | /** --------------------------------------- Events --------------------------------------- **/
139 | // Refresh the window
140 | $('#_titleconfig').on({
141 | click: () => {
142 | if (actualPanel !== null) {
143 | actualPanel.addClass('hide');
144 |
145 | $('#config-container-options')
146 | .removeClass('config-opt-anim')
147 | .removeClass('hide');
148 |
149 | $('#config-container-values').addClass('hide');
150 | $('#_titlesubconfig').text('');
151 | }
152 | }
153 | });
154 |
155 | // Change the language
156 | $('#change-lang').on({ click: onClickChangeLang });
157 |
158 | // Action to add the songs
159 | $('#add-songs').on({
160 | click: () => {
161 | remote.dialog.showOpenDialog({
162 | title: 'Add music folder',
163 | properties: ['openDirectory']
164 | }, parentFolder => {
165 | if (parentFolder !== undefined) saveSongList(parentFolder[0]);
166 | });
167 | }
168 | });
169 |
170 | // Show the EQ panel
171 | $('#equalizer-panel').on({ click: onEqualizerPanel });
172 |
173 | // EQ settings options
174 | Object.keys(configFile.equalizer).forEach(v => {
175 | $('#eq-buttons').insert(
176 | $('option').clone(true).val(v).text(v)
177 | .attr(configFile.equalizerConfig === v.toLowerCase() ? { selected:'selected' } : '')
178 | );
179 | });
180 | $('#eq-buttons').on({ change: setEQ });
181 |
182 | // Check if the person want to add a new EQ setting
183 | $('#name-new-eq').on({
184 | keyup: function() {
185 | if (this.value.trim().length > 3) $('#_neweq').rmAttr('disabled');
186 | else $('#_neweq').attr({ disabled: 'disabled' });
187 | }
188 | });
189 |
190 | // Action to add a new EQ setting
191 | $('#_neweq').on({
192 | click: function(e) {
193 | e.preventDefault();
194 | const eqStylesNames = Object.keys(configFile.equalizer);
195 |
196 | if(eqStylesNames.indexOf($('#name-new-eq').val()) === -1) {
197 | configFile.equalizer[$('#name-new-eq').val()] = newEQHrz;
198 | editFile('config', configFile);
199 | } else {
200 | console.log('El nombres ya exíste o no es un nombre válido');
201 | }
202 | }
203 | });
204 |
205 | // Open the default browser of the OS
206 | $(':a').on({
207 | click: function (e) {
208 | e.preventDefault();
209 | shell.openExternal(this.href);
210 | }
211 | });
212 |
213 | // Show legal terms
214 | $('#terms').on({
215 | click: function() {
216 | animConfigPanel(this, lang.config.legal);
217 | }
218 | });
--------------------------------------------------------------------------------
/app/assets/js/dom/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Diego Alberto Molina Vera
3 | * @copyright 2016 - 2017
4 | */
5 | module.exports = (_ => {
6 | /* --------------------------------- Variables --------------------------------- */
7 | //---- normals ----
8 | var createdElements = {};
9 | var poolOfElements = {};
10 | var event = {};
11 |
12 | /* --------------------------------- Events --------------------------------- */
13 | const EVENT = {
14 | element: null,
15 | addClass: function(str) {
16 | const CLASS_NAME = this.element.className;
17 |
18 | if (CLASS_NAME.indexOf(str) === -1)
19 | this.element.className += CLASS_NAME === '' ? `${str}` : ` ${str}`;
20 |
21 | return this;
22 | },
23 | empty: function() {
24 | let el = this.element;
25 | while(el.firstChild) el.removeChild(el.firstChild);
26 |
27 | return this;
28 | },
29 | text: function(str = null) {
30 | if (str === null) return this.element.textContent;
31 |
32 | return this.element.innerHTML = `${str}`, this;
33 | },
34 | removeClass: function(_class) {
35 | return this.element.className = this.element.className.replace(_class, ''), this;
36 | },
37 | child: function(pos = -1) {
38 | return this.element = pos !== -1 ? this.element.children[pos] : Array.from(this.element.children),
39 | this;
40 | },
41 | on: function(fn) {
42 | // Select element is like an array because of the options elements inside
43 | if (this.element.length !== undefined && this.element.nodeName !== 'SELECT') {
44 | this.element.forEach(e => onFunction(e, fn));
45 | } else {
46 | onFunction(this.element, fn);
47 | }
48 |
49 | return this;
50 | },
51 | data: function(data = null) {
52 | if (typeof data === 'string') {
53 | let d = this.element.dataset[data];
54 | if (/^\d+$/.test(d)) return parseInt(d);
55 | else if (/^\d+(\.+)\d+$/.test(d)) return parseFloat(d);
56 | else if (/^(\w|\s)+$/.test(d)) return d.toString();
57 | } else {
58 | Object.keys(data).forEach(v => {
59 | this.element.dataset[v] = data[v];
60 | });
61 | }
62 |
63 | return this;
64 | },
65 | each: function(fn) {
66 | this.element.forEach((v, i) => {
67 | fn.length === 1 ? fn(v) : fn(v, i);
68 | });
69 |
70 | return this;
71 | },
72 | css: function(str) {
73 | let el = this.element;
74 |
75 | if (el.length !== undefined) {
76 | el.forEach(e => {
77 | if (e.style.cssText.indexOf(str))
78 | e.style.cssText += e.style.cssText === '' ? `${str};` : ` ${str};`;
79 | });
80 | } else {
81 | if (el.style.cssText.indexOf(str))
82 | el.style.cssText += el.style.cssText === '' ? `${str};` : ` ${str};`;
83 | }
84 |
85 | return this;
86 | },
87 | clone: function(isCloned) {
88 | if (typeof this.element === 'string') {
89 | return this.element = getCreatedElement(this.element).cloneNode(isCloned), this;
90 | } else {
91 | return event = Object.assign({}, EVENT),
92 | event.element = this.element.cloneNode(isCloned), event;
93 | }
94 | },
95 | get: function(pos = -1) {
96 | return pos === -1 ? this.element : this.element[pos];
97 | },
98 | rmAttr: function(attr) {
99 | return this.element.removeAttribute(attr), this;
100 | },
101 | attr: function (attr) {
102 | if (typeof attr === 'object')
103 | Object.keys(attr).forEach(v => { this.element.setAttribute(v, attr[v]); });
104 | else if (attr !== '')
105 | return this.element.getAttribute(attr);
106 |
107 | return this;
108 | },
109 | insert: function (...a) {
110 | a.forEach(v => { this.element.appendChild('element' in v ? v.element : v); });
111 | return this;
112 | },
113 | val: function (v = null) {
114 | return v === null ? this.element.value : this.element.value = v, this;
115 | },
116 | has: function (s) {
117 | return this.element.className.indexOf(s) !== -1;
118 | }
119 | };
120 |
121 | /* --------------------------------- Functions --------------------------------- */
122 | function onFunction (el, fn) {
123 | Object.keys(fn).forEach(v => {
124 | /animation/.test(v) ?
125 | el.addEventListener(v.toLowerCase(), fn[v]) :
126 | el[`on${v.toLowerCase()}`] = fn[v];
127 | })
128 | }
129 |
130 | function saveCreatedElement(name) {
131 | if (!createdElements[name]) createdElements[name] = document.createElement(name);
132 | }
133 |
134 | function getCreatedElement(name) {
135 | return createdElements[name];
136 | }
137 |
138 | function saveElementInPool(name, element) {
139 | poolOfElements[name] = element;
140 | }
141 |
142 | function inPool(name) {
143 | return poolOfElements[name] === EVENT.element;
144 | }
145 |
146 | function getElementInPool(name) {
147 | return poolOfElements[name];
148 | }
149 |
150 | /* --------------------------------- Main Function --------------------------------- */
151 | // Get an string to search for an element into the DOM and its return an Object
152 | // with all the needed functions
153 | const DOM = e => {
154 | event = Object.assign({}, EVENT);
155 |
156 | if (inPool(e)) {
157 | event.element = getElementInPool(e);
158 | } else {
159 | if ((r = /^(\.|#|:)/.exec(e))) {
160 | switch(r[0]) {
161 | case '.': e = Array.from(document.getElementsByClassName(e.slice(1, e.length))); break;
162 | case '#': e = document.getElementById(e.slice(1, e.length)); break;
163 | case ':': e = Array.from(document.getElementsByTagName(e.slice(1, e.length))); break;
164 | }
165 |
166 | saveElementInPool(e);
167 | } else if (typeof e === 'string') {
168 | saveCreatedElement(e);
169 | }
170 | }
171 |
172 | return event.element = e, event;
173 | }
174 |
175 | _.$ = DOM;
176 | })(global);
--------------------------------------------------------------------------------
/app/assets/js/factory/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Diego Alberto Molina Vera
3 | * @copyright 2016 - 2017
4 | */
5 | function factory(nameClass) {
6 | switch (nameClass.toLowerCase()) {
7 | case 'equilizer': return require('./../player/equalizer');
8 | case 'player': return require('./../player');
9 | }
10 | }
11 |
12 | module.exports = factory;
--------------------------------------------------------------------------------
/app/assets/js/main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Diego Alberto Molina Vera
3 | * @copyright 2016 - 2017
4 | */
5 | /** --------------------------------------- Modules --------------------------------------- **/
6 | //---- Electron ----
7 | const {
8 | ipcRenderer,
9 | remote
10 | } = require('electron');
11 |
12 | //---- own ----
13 | const PLAYER = require('./factory')('player');
14 | const version = require('./version');
15 | const config = require('./config');
16 | const {
17 | configFile,
18 | langFile,
19 | listSongs
20 | } = config.init();
21 | require('./dom');
22 |
23 | /** --------------------------------------- Variables --------------------------------------- **/
24 | //---- constants ----
25 | const TIME_SCROLLING = 3.2; // Pixels per frame
26 | const LAPSE_POPUP = 4500; // Duration of info popups
27 | const LAPSE_SCROLLING = 60; // Lapse before do scrolling
28 | const MAX_ELEMENTS = 20; // Max of elementos to display when is filtering a song [searching bar]
29 | const BTN_FILTER_SONGS = [ // Elements to use as a items into the slide
30 | $('div').clone(false).addClass('grid-25 mobile-grid-25'),
31 | $('div').clone(false).addClass('search-results'),
32 | $('div').clone(false).addClass('results')
33 | ];
34 |
35 | //---- normals ----
36 | let lang = langFile[configFile.lang];
37 | let clickedElement = null; // When you do click on the name of the song
38 | let positionElement = null; // Where is the song that you clicked on.
39 | let isSearchDisplayed = false; // Checks if it was launched the searching bar
40 | let totalResults = 0; // Amount of songs filtered
41 | let searchValue = ''; // The input text to search for
42 | let tempSlide = 0; // To create the pagination
43 | let countSlide = 0;
44 | let searchBy = 'title';
45 | let fragmentSlide = null; // DocumentFragment() slide container
46 | let fragmentItems = null; // DocumentFragment() button container
47 | let slide = 0; // Amount of slides to make
48 | let regex = null; // The name of the song to searching for as a regular expression
49 | let list = []; // Filtered songs.
50 | let interval = 0;
51 |
52 | /** --------------------------------------- Functions --------------------------------------- **/
53 | // Check if there's a new version to download
54 | function getActualVersion() {
55 | version(remote.net, remote.app.getVersion(), response => {
56 | if (response === 'major') {
57 | $('#pop-up-container')
58 | .removeClass('hide')
59 | .child(0)
60 | .addClass('pop-up-anim')
61 | .text(
62 | `${lang.alerts.newVersion}`
63 | );
64 |
65 | $(':a').on({
66 | click: function (e) {
67 | e.preventDefault();
68 | shell.openExternal(this.href);
69 | }
70 | });
71 |
72 | let tout = setTimeout(() => {
73 | $('#pop-up-container')
74 | .addClass('hide')
75 | .child(0)
76 | .removeClass('pop-up-anim');
77 |
78 | clearTimeout(tout);
79 | }, LAPSE_POPUP);
80 | }
81 | });
82 | }
83 |
84 | // Main function!!
85 | function loadSongs() {
86 | // Enable shuffle
87 | if (configFile.shuffle) $('#shuffle-icon').css('fill:#FBFCFC');
88 |
89 | getActualVersion();
90 | if (Object.keys(listSongs).length === 0) {
91 | $('#list-songs').text(
92 | `