From 839028909cf11bf3498f1abf0e8045b9c5cb83b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renato=20N=2E=20Louren=C3=A7o?= Date: Wed, 30 Aug 2017 23:29:15 -0300 Subject: [PATCH 1/5] Added translate function to lyrics --- Settings.ts | 6 ++- render/SongRender.ts | 36 +++++++++++++++++ render/Translaters.ts | 52 ++++++++++++++++++++++++ render/less/views/lyric.less | 29 +++++++++++++- render/plugins/TranslateVagalume.ts | 62 +++++++++++++++++++++++++++++ render/plugins/TranslatersLyrics.ts | 40 +++++++++++++++++++ render/views/Settings.html | 26 ++++++++++++ render/views/Song.html | 12 +++++- 8 files changed, 259 insertions(+), 4 deletions(-) create mode 100644 render/Translaters.ts create mode 100644 render/plugins/TranslateVagalume.ts create mode 100644 render/plugins/TranslatersLyrics.ts diff --git a/Settings.ts b/Settings.ts index 4ddffec..bb02ad3 100644 --- a/Settings.ts +++ b/Settings.ts @@ -7,6 +7,8 @@ export interface SettingsValues { theme: 'dark'|'light'; fontSize: 'eight-pt'|'ten-pt'|'twelve-pt'|'fourteen-pt'|'sixteen-pt'; refreshInterval: number; + translateLyrics: boolean; + translateLang: string; } export const defaultSettings: SettingsValues = { @@ -14,7 +16,9 @@ export const defaultSettings: SettingsValues = { alwaysOnTop: false, theme: 'light', fontSize: 'twelve-pt', - refreshInterval: 5000 + refreshInterval: 5000, + translateLyrics: false, + translateLang: 'en-US' }; export class Settings { diff --git a/render/SongRender.ts b/render/SongRender.ts index 3c0a68c..5e9c10a 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,8 +35,10 @@ export class SongRender { data() { return { song: null, + translatedSong: {}, lastSongSync: {}, searcher: new Searcher(), + translaters: new Translaters(), } } @@ -71,6 +76,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) => { @@ -131,6 +137,36 @@ export class SongRender { this.shell.openExternal(url); } + translateLyrics() { + console.log("Language: " + this.settings.translateLang); + this.translatedSong = { + lyrics: 'Searching translated lyrics...', + sourceName: '', + sourceUrl: '' + }; + + 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..3dc57ee --- /dev/null +++ b/render/Translaters.ts @@ -0,0 +1,52 @@ +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) => { + return 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); + + async.detectSeries(this.plugins, (plugin : TranslatersLyrics, callback) => { + plugin.search(normalizedTitle, artist, (err, result) => { + if (!err) { + from.translated = result.translated; + from.sourceName = plugin.name; + from.sourceUrl = result.url; + } + callback(null, from); + }) + }, (err) => { + cb(err, from); + }); + } +} diff --git a/render/less/views/lyric.less b/render/less/views/lyric.less index 5e1b6c1..31a1f92 100644 --- a/render/less/views/lyric.less +++ b/render/less/views/lyric.less @@ -119,7 +119,32 @@ } } -.lyrics { +.lyrics-right { + width: 100%; + position: relative; +} + +.lyrics-left { + white-space: pre-wrap; + flex: 1; + overflow: auto; + padding: 1rem; + line-height: 1.75; + position: relative; + user-select: all; + float: left; + width: 100%; + + &:first-letter { + text-transform: capitalize; + } +} + +.resized40{ + width: 40%; +} + +.lyrics-right { white-space: pre-wrap; flex: 1; overflow: auto; @@ -127,6 +152,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 f13117f..384be1a 100644 --- a/render/views/Settings.html +++ b/render/views/Settings.html @@ -56,6 +56,32 @@

+
+ Translate +
    +
  1. + +
  2. +
  3. + +
  4. +
+
diff --git a/render/views/Song.html b/render/views/Song.html index 85c8c56..c2849c6 100644 --- a/render/views/Song.html +++ b/render/views/Song.html @@ -20,9 +20,17 @@

lyricfier

{{ song.title }}

{{ song.artist }}

-
{{ song.lyric }}
-
Source: {{song.sourceName}}
+
+
{{ song.lyric }}
+
{{ translatedSong.lyrics }}
+
+ +
+ Source: {{song.sourceName}} + | Translate lyrics + {{translatedSong.sourceName}} +
From adb7e7074163df640da69d1293f3cd05281b16dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renato=20N=2E=20Louren=C3=A7o?= Date: Thu, 5 Oct 2017 09:15:28 -0300 Subject: [PATCH 2/5] Updated detectSeries to the same logic used in lyric search. --- render/Translaters.ts | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/render/Translaters.ts b/render/Translaters.ts index 3dc57ee..69806d4 100644 --- a/render/Translaters.ts +++ b/render/Translaters.ts @@ -27,26 +27,35 @@ export class Translaters { } loadPlugins() { - this.plugins = plugins.map((Plugin) => { - return new Plugin(request); - }); + 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 from:Result = {sourceName: '', sourceUrl: '', translated: null}; const normalizedTitle = this.normalizer.normalize(title); - async.detectSeries(this.plugins, (plugin : TranslatersLyrics, callback) => { - plugin.search(normalizedTitle, artist, (err, result) => { - if (!err) { + 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); - }) - }, (err) => { - cb(err, from); + 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); + } }); } } From 25f917c5a27f7195515cf3e2e4a9e9534959a5b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renato=20N=2E=20Louren=C3=A7o?= Date: Thu, 5 Oct 2017 09:45:19 -0300 Subject: [PATCH 3/5] Fixed to setting default as disabled on first launch to Enable Translate to lyrics. Updated to use checkbox instead select to keep in the same design from Keep Lyricfier always on top. --- render/views/Settings.html | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/render/views/Settings.html b/render/views/Settings.html index 384be1a..15664c5 100644 --- a/render/views/Settings.html +++ b/render/views/Settings.html @@ -61,11 +61,7 @@

  1. From 552d6db6d5d807ac5823ef6db4a633582a44d61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renato=20N=2E=20Louren=C3=A7o?= Date: Thu, 5 Oct 2017 10:40:43 -0300 Subject: [PATCH 4/5] Fixed wrong scrollbar (on body). Updated to scroll to top when click on Translate lyrics button. --- render/SongRender.ts | 4 ++++ render/less/views/lyric.less | 13 ++++++------- render/views/Song.html | 6 +++--- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/render/SongRender.ts b/render/SongRender.ts index 1949815..bb32c22 100644 --- a/render/SongRender.ts +++ b/render/SongRender.ts @@ -138,6 +138,10 @@ export class SongRender { sourceUrl: '' }; + this['$nextTick'](() => { + document.getElementById("lyricBox").scrollTop = 0; + }); + this.translaters.translate(this.song.title, this.song.artist, (err, result) => { if(!err) { try{ diff --git a/render/less/views/lyric.less b/render/less/views/lyric.less index 31a1f92..03419a7 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; } @@ -65,7 +69,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 { @@ -119,11 +123,6 @@ } } -.lyrics-right { - width: 100%; - position: relative; -} - .lyrics-left { white-space: pre-wrap; flex: 1; @@ -133,7 +132,7 @@ position: relative; user-select: all; float: left; - width: 100%; + width: 90%; &:first-letter { text-transform: capitalize; diff --git a/render/views/Song.html b/render/views/Song.html index c2849c6..9470234 100644 --- a/render/views/Song.html +++ b/render/views/Song.html @@ -21,9 +21,9 @@

    {{ song.title }}

    {{ song.artist }}

    -
    -
    {{ song.lyric }}
    -
    {{ translatedSong.lyrics }}
    +
    +
    {{ song.lyric }}
    +
    {{ translatedSong.lyrics }}
    From 27c046892a6a618b6cd1735c51f558f6513c9063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renato=20N=2E=20Louren=C3=A7o?= Date: Thu, 5 Oct 2017 10:50:29 -0300 Subject: [PATCH 5/5] Updated to dont appear Translate lyric button when there is no song showing. --- render/views/Song.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render/views/Song.html b/render/views/Song.html index 9470234..c7d4923 100644 --- a/render/views/Song.html +++ b/render/views/Song.html @@ -28,7 +28,7 @@

    {{ song.artist }}

    Source: {{song.sourceName}} - | Translate lyrics + | Translate lyrics {{translatedSong.sourceName}}