diff --git a/Settings.ts b/Settings.ts index fefdbdf..a15a466 100644 --- a/Settings.ts +++ b/Settings.ts @@ -8,6 +8,8 @@ export interface SettingsValues { theme: 'dark'|'light'; fontSize: 'eight-pt'|'ten-pt'|'twelve-pt'|'fourteen-pt'|'sixteen-pt'; refreshInterval: number; + translateLyrics: boolean; + translateLang: string; closeToTray: boolean; } @@ -18,6 +20,8 @@ export const defaultSettings: SettingsValues = { theme: 'light', fontSize: 'twelve-pt', refreshInterval: 5000, + translateLyrics: false, + translateLang: 'en-US' closeToTray: false }; diff --git a/render/SongRender.ts b/render/SongRender.ts index a79d7b3..5fe7bb9 100644 --- a/render/SongRender.ts +++ b/render/SongRender.ts @@ -1,5 +1,6 @@ import Component from 'vue-class-component'; import {Searcher} from "./Searcher"; +import {Translaters} from "./Translaters"; import {template} from './template'; import {SpotifyService} from './SpotifyService'; @@ -23,8 +24,10 @@ export class SongRender { protected lastSongSync; protected service:SpotifyService; protected song; + protected translatedSong; protected shell; protected searcher: Searcher; + protected translaters: Translaters; protected showError; protected timer = null; protected settings; @@ -32,12 +35,14 @@ export class SongRender { data() { return { song: null, - lastSongSync: {}, + translatedSong: {}, + lastSongSync: {} } } beforeCompile() { this.searcher = new Searcher(); + this.translaters = new Translaters(); } @@ -68,6 +73,7 @@ export class SongRender { } else { console.log('is not last song searching by title and artist'); song.lyric = 'Loading Lyrics...'; + this.translatedSong = {}; this.displaySong(song); this.saveLastSong(song); this.searcher.search(song.title, song.artist, (err, result) => { @@ -147,6 +153,40 @@ export class SongRender { this.shell.openExternal(url); } + translateLyrics() { + console.log("Language: " + this.settings.translateLang); + this.translatedSong = { + lyrics: 'Searching translated lyrics...', + sourceName: '', + sourceUrl: '' + }; + + this['$nextTick'](() => { + document.getElementById("lyricBox").scrollTop = 0; + }); + + this.translaters.translate(this.song.title, this.song.artist, (err, result) => { + if(!err) { + try{ + result.translated.forEach((translated) => { + if(translated.lang == this.settings.translateLang){ + console.log("Founded translated lyrics"); + this.translatedSong = translated; + this.translatedSong.sourceName = result.sourceName; + this.translatedSong.sourceUrl = result.sourceUrl; + } + }); + } catch(e) { + this.translatedSong = { + lyrics: 'Translated lyrics not founded.', + sourceName: '', + sourceUrl: '' + }; + } + } + }); + } + resizeOnLyricsHide() { if (!this.settings.hideLyrics) { return; diff --git a/render/Translaters.ts b/render/Translaters.ts new file mode 100644 index 0000000..69806d4 --- /dev/null +++ b/render/Translaters.ts @@ -0,0 +1,61 @@ +import {TranslatersLyrics} from './plugins/TranslatersLyrics'; +import {TranslateVagalume} from "./plugins/TranslateVagalume"; + +import {NormalizeTitles} from "./NormalizeTitles"; +const request = require('request').defaults({timeout: 5000}); + +const async = require('async'); +const plugins = [TranslateVagalume]; + +interface Result { + sourceName: string; + sourceUrl: string; + translated: [{ + lang: string, + lyrics: string + }]; +} + +export class Translaters { + + protected plugins:TranslatersLyrics[]; + protected normalizer : NormalizeTitles; + + constructor() { + this.normalizer = new NormalizeTitles(); + this.loadPlugins(); + } + + loadPlugins() { + this.plugins = plugins.map(Plugin => new Plugin(request)); + } + + translate(title:string, artist:string, cb : (error : any, result : Result) => void){ + const from:Result = {sourceName: '', sourceUrl: '', translated: null}; + const normalizedTitle = this.normalizer.normalize(title); + + const tasks = this.plugins.map((plugin : TranslatersLyrics) => { + return (callback) => { + console.log('Translating with', plugin, 'normalizedTitle', normalizedTitle, 'artist', artist); + plugin.search(normalizedTitle, artist, (err, result) => { + console.warn('Result with', plugin, 'normalizedTitle', normalizedTitle, 'artist', artist, 'err', err, 'result', result); + if (err) { + return callback(err); + } + from.translated = result.translated; + from.sourceName = plugin.name; + from.sourceUrl = result.url; + callback(null, from); + }) + } + }); + async.parallel(async.reflectAll(tasks), (err, results) => { + const result = results.find(x => !x.error); + if (result) { + cb(null, result.value); + } else { + cb(err, null); + } + }); + } +} diff --git a/render/less/views/lyric.less b/render/less/views/lyric.less index 3c6659b..d4cd608 100644 --- a/render/less/views/lyric.less +++ b/render/less/views/lyric.less @@ -1,3 +1,7 @@ +body { + overflow: hidden; +} + .eight-pt { font-size: 8pt; } @@ -72,7 +76,7 @@ .header { position: relative; overflow: hidden; - min-height: 8rem; + min-height: 10rem; box-shadow: 0 0.25rem 1rem rgba(0, 0, 0, 0.1); &::before { @@ -126,7 +130,27 @@ } } -.lyrics { +.lyrics-left { + white-space: pre-wrap; + flex: 1; + overflow: auto; + padding: 1rem; + line-height: 1.75; + position: relative; + user-select: all; + float: left; + width: 90%; + + &:first-letter { + text-transform: capitalize; + } +} + +.resized40{ + width: 40%; +} + +.lyrics-right { white-space: pre-wrap; flex: 1; overflow: auto; @@ -134,6 +158,8 @@ line-height: 1.75; position: relative; user-select: all; + float: right; + width: 40%; &:first-letter { text-transform: capitalize; diff --git a/render/plugins/TranslateVagalume.ts b/render/plugins/TranslateVagalume.ts new file mode 100644 index 0000000..0da279a --- /dev/null +++ b/render/plugins/TranslateVagalume.ts @@ -0,0 +1,62 @@ +import {TranslatersLyrics} from './TranslatersLyrics'; + +const API_KEY = ""; +const lang = { + 1: "pt-BR", + 2: "en-US", + 3: "es-ES", + 4: "fr-FR", + 5: "de-DE", + 6: "it-ELE", + 7: "nl-NL", + 8: "ja-JA", + 9: "pt-PT", + 999999: "other" +} + +export class TranslateVagalume extends TranslatersLyrics { + + public name = 'Vagalume'; + + public search(title: string, artist: string, cb: (error?: any, lyrics?) => void) { + let url = `https://api.vagalume.com.br/search.php?art=${encodeURIComponent(artist)}&mus=${encodeURIComponent(title)}&apikey=`; + + this.doReq(url, (err, res, body) => { + if(!err){ + let json = JSON.parse(body); + + return this.parseResult(json, cb); + } else return cb('Vagalume fail'); + }); + } + + protected parseResult(json, cb) { + if(json.mus && json.mus.length > 0){ + let mus = json.mus[0]; + + if(mus.translate && mus.translate.length > 0){ + let translate = mus.translate; + let translated = []; + + for(let i = 0; i < translate.length; i++){ + translated.push({ + lang: this.checkLang(translate[i].lang), + lyrics: translate[i].text + }); + } + + return cb(null, { + url: mus.url, + translated: translated + }); + } + } + return cb('Lyrics not found'); + } + + protected checkLang(langId: number){ + if(lang[langId]) return lang[langId]; + + return "other"; + } +} diff --git a/render/plugins/TranslatersLyrics.ts b/render/plugins/TranslatersLyrics.ts new file mode 100644 index 0000000..52214ee --- /dev/null +++ b/render/plugins/TranslatersLyrics.ts @@ -0,0 +1,40 @@ + +interface TranslateResult { + url: string; + translated: [{ + lang: string, + lyrics: string + }]; +} + +export class TranslatersLyrics { + protected req; + public name = 'Generic'; + + public constructor(req = null) { + this.req = req; + } + + protected doReq(url, cb) { + let opt = { + jar: true, + method: 'GET', + uri: url, + gzip: true, + followRedirect: true, + maxRedirects: 10, + resolveWithFullResponse: true, + headers: { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36', + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'Accept-Encoding': 'gzip, deflate, sdch', + 'Accept-Language': 'es,en-US;q=0.8,en;q=0.6,pt;q=0.4', + } + }; + return this.req(opt, cb); + } + + public search(title: string, artist: string, cb: (error?: any, result?: TranslateResult) => void) { + + } +} diff --git a/render/views/Settings.html b/render/views/Settings.html index bc1e875..a5b8049 100644 --- a/render/views/Settings.html +++ b/render/views/Settings.html @@ -65,6 +65,28 @@