From 510615a520f4e3b51f8e52ba4a3b867ccb47ace3 Mon Sep 17 00:00:00 2001
From: Nicolas Guichard
Date: Sat, 1 Apr 2017 17:55:36 +0200
Subject: [PATCH 01/25] Make Instant-Lyrics app-agnostic
---
src/appIndicator.py | 62 +++++++++++++++++++++++++++++----------------
src/windows.py | 24 ++++++++----------
2 files changed, 51 insertions(+), 35 deletions(-)
diff --git a/src/appIndicator.py b/src/appIndicator.py
index 632c05b..6f6853c 100644
--- a/src/appIndicator.py
+++ b/src/appIndicator.py
@@ -11,48 +11,66 @@
from . import utils
from src.settings import APPINDICATOR_ID, CONFIG_PATH
+import dbus
+from dbus.mainloop.glib import DBusGMainLoop
+DBusGMainLoop(set_as_default=True)
class AppIndicator():
-
def __init__(self):
signal.signal(signal.SIGINT, signal.SIG_DFL)
-
- indicator = appindicator.Indicator.new(APPINDICATOR_ID, utils.get_icon_path(
- '../icons/instant-lyrics-24.png'), appindicator.IndicatorCategory.SYSTEM_SERVICES)
- indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
- indicator.set_menu(self.build_menu())
-
+
+ bus = dbus.SessionBus()
+
+ bus.add_signal_receiver(self.build_menu, path="/org/mpris/MediaPlayer2")
+
+ self.indicator = appindicator.Indicator.new(APPINDICATOR_ID, utils.get_icon_path(
+'../icons/instant-lyrics-24.png'), appindicator.IndicatorCategory.SYSTEM_SERVICES)
+ self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
+ self.build_menu()
+
self.Config = utils.get_config()
+
Gtk.main()
- def build_menu(self):
- menu = Gtk.Menu()
+ def list_apps(self):
+ apps = []
+ session_bus = dbus.SessionBus()
+ for service in session_bus.list_names():
+ if service[:22] == "org.mpris.MediaPlayer2":
+ apps.append(service[23:])
+
+ return apps
- get_lyrics = Gtk.MenuItem('Get Lyrics')
- get_lyrics.connect('activate', self.fetch_lyrics)
+ def build_menu(self, *args):
+ menu = Gtk.Menu()
- spotify_lyrics = Gtk.MenuItem('Spotify Lyrics')
- spotify_lyrics.connect('activate', self.spotify_lyrics)
+ item_lyrics = Gtk.MenuItem('Custom Lyrics')
+ item_lyrics.connect('activate', self.fetch_lyrics)
+ menu.append(item_lyrics)
+ apps = self.list_apps()
+ for app in apps:
+ current = Gtk.MenuItem(app.capitalize()+" lyrics")
+ current.connect('activate', self.app_lyrics, app)
+ menu.insert(current, 1)
+
preferences = Gtk.MenuItem('Preferences')
preferences.connect('activate', self.preferences)
+ menu.append(preferences)
item_quit = Gtk.MenuItem('Quit')
item_quit.connect('activate', self.quit)
-
- menu.append(get_lyrics)
- menu.append(spotify_lyrics)
- menu.append(preferences)
menu.append(item_quit)
+
menu.show_all()
- return menu
+ self.indicator.set_menu(menu)
def fetch_lyrics(self, source):
win = LyricsWindow("get", self)
- def spotify_lyrics(self, source):
- win = LyricsWindow("spotify", self)
- thread = threading.Thread(target=win.get_spotify)
+ def app_lyrics(self, source, app):
+ win = LyricsWindow("app", self)
+ thread = threading.Thread(target=win.get_lyrics, args=(app,))
thread.daemon = True
thread.start()
@@ -60,4 +78,4 @@ def preferences(self, source):
win = PreferenceWindow(self)
def quit(self, source):
- Gtk.main_quit()
\ No newline at end of file
+ Gtk.main_quit()
diff --git a/src/windows.py b/src/windows.py
index e842706..1ef2ebb 100644
--- a/src/windows.py
+++ b/src/windows.py
@@ -55,7 +55,7 @@ def create_input_box(self):
entry_hbox.set_property("margin", 10)
self.input = Gtk.Entry()
- self.input.set_text("song/artist")
+ self.input.set_placeholder_text("song/artist")
self.input.connect("key-release-event", self.on_key_release)
entry_hbox.pack_start(self.input, True, True, 0)
@@ -107,14 +107,14 @@ def fetch_lyrics(self, source=None):
thread.daemon = True
thread.start()
- def get_spotify_song_data(self):
+ def get_song_data(self, app):
session_bus = dbus.SessionBus()
- spotify_bus = session_bus.get_object(
- "org.mpris.MediaPlayer2.spotify", "/org/mpris/MediaPlayer2")
- spotify_properties = dbus.Interface(
- spotify_bus, "org.freedesktop.DBus.Properties")
- metadata = spotify_properties.Get(
+ app_bus = session_bus.get_object(
+ "org.mpris.MediaPlayer2."+app, "/org/mpris/MediaPlayer2")
+ app_properties = dbus.Interface(
+ app_bus, "org.freedesktop.DBus.Properties")
+ metadata = app_properties.Get(
"org.mpris.MediaPlayer2.Player", "Metadata")
title = metadata['xesam:title'].encode(
@@ -123,17 +123,15 @@ def get_spotify_song_data(self):
'utf-8').decode('utf-8').replace("&", "&")
return {'title': title, 'artist': artist}
- def get_spotify(self):
-
+ def get_lyrics(self, app):
try:
- song_data = self.get_spotify_song_data()
+ song_data = self.get_song_data(app)
song = song_data['title']
artist = song_data['artist']
except:
self.title.set_markup("Error")
- message = ("Could not get current spotify song\n"
- "Either spotify is not running or\n"
- "no song is playing on spotify.\n\n"
+ message = ("Could not get current "+app+" song\n"
+ "no song is playing on "+app+".\n\n"
"Else, report an issue here")
From ccabe27a0c86fc0964c07db9c55f619edde1a866 Mon Sep 17 00:00:00 2001
From: Nicolas Guichard
Date: Sat, 1 Apr 2017 18:03:09 +0200
Subject: [PATCH 02/25] - Correct an encoding problem with MetroLyrics lyrics
- More coherent HTTP request headers
---
src/lyrics.py | 24 ++++++++++--------------
1 file changed, 10 insertions(+), 14 deletions(-)
diff --git a/src/lyrics.py b/src/lyrics.py
index b6f995c..7c432dd 100644
--- a/src/lyrics.py
+++ b/src/lyrics.py
@@ -12,12 +12,13 @@ def get_lyrics(song_name):
song_name += ' metrolyrics'
name = quote_plus(song_name)
- hdr = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11'
- '(KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
- 'Accept-Language': 'en-US,en;q=0.8',
+ hdr = {'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0',
+ 'Accept-Encoding': 'gzip, deflate, br',
+ 'DNT': '1',
+ 'UPGRADE-INSECURE-REQUESTS': '1',
'Connection': 'keep-alive'}
- url = 'http://www.google.com/search?q=' + name
+ url = 'https://www.google.com/search?q=' + name
result = requests.get(url, headers=hdr).text
link_start = result.find('http://www.metrolyrics.com')
@@ -25,20 +26,15 @@ def get_lyrics(song_name):
if(link_start == -1):
return("Lyrics not found on Metrolyrics")
- link_end = result.find('html', link_start + 1)
- link = result[link_start:link_end + 4]
+ link_end = result.find('html', link_start + 1) + 4
+ link = result[link_start:link_end]
-
- lyrics_html = requests.get(link, headers={
- 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel'
- 'Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, '
- 'like Gecko) Chrome/55.0.2883.95 Safari/537.36'
- }
- ).text
+ r = requests.get(link, headers=hdr)
+ r.encoding = "utf-8"
+ lyrics_html = r.text
soup = BeautifulSoup(lyrics_html, "lxml")
raw_lyrics = (soup.findAll('p', attrs={'class': 'verse'}))
- paras = []
try:
final_lyrics = unicode.join(u'\n', map(unicode, raw_lyrics))
except NameError:
From 51c72a17d51bc8952eb50dd4de3278cd3db16924 Mon Sep 17 00:00:00 2001
From: Nicolas Guichard
Date: Sat, 1 Apr 2017 18:07:06 +0200
Subject: [PATCH 03/25] Add python encoding headers to files
---
InstantLyrics.py | 3 +++
src/appIndicator.py | 3 +++
src/lyrics.py | 3 +++
src/settings.py | 3 +++
src/utils.py | 5 ++++-
src/windows.py | 3 +++
6 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/InstantLyrics.py b/InstantLyrics.py
index 653c693..04b641c 100644
--- a/InstantLyrics.py
+++ b/InstantLyrics.py
@@ -1,3 +1,6 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
from src.appIndicator import AppIndicator
if __name__ == "__main__":
diff --git a/src/appIndicator.py b/src/appIndicator.py
index 6f6853c..920a294 100644
--- a/src/appIndicator.py
+++ b/src/appIndicator.py
@@ -1,3 +1,6 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
diff --git a/src/lyrics.py b/src/lyrics.py
index 7c432dd..7b00bdc 100644
--- a/src/lyrics.py
+++ b/src/lyrics.py
@@ -1,3 +1,6 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
import requests
from bs4 import BeautifulSoup
import os
diff --git a/src/settings.py b/src/settings.py
index 55f4d59..31106a7 100644
--- a/src/settings.py
+++ b/src/settings.py
@@ -1,3 +1,6 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
import os
APPINDICATOR_ID = 'lyricsappindicator'
diff --git a/src/utils.py b/src/utils.py
index 64c1731..3052b1e 100644
--- a/src/utils.py
+++ b/src/utils.py
@@ -1,3 +1,6 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
import os
try:
import configparser
@@ -58,4 +61,4 @@ def create_desktop_entry():
file.write(ic)
file.write(ex)
file.write(cm)
- file.write(tm)
\ No newline at end of file
+ file.write(tm)
diff --git a/src/windows.py b/src/windows.py
index 1ef2ebb..ed79240 100644
--- a/src/windows.py
+++ b/src/windows.py
@@ -1,3 +1,6 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
From cbe4082cac0caf5823f32d9b12442e725f781593 Mon Sep 17 00:00:00 2001
From: Nicolas Guichard
Date: Sat, 1 Apr 2017 18:10:09 +0200
Subject: [PATCH 04/25] Main app script made executable
---
InstantLyrics.py | 0
1 file changed, 0 insertions(+), 0 deletions(-)
mode change 100644 => 100755 InstantLyrics.py
diff --git a/InstantLyrics.py b/InstantLyrics.py
old mode 100644
new mode 100755
From a2a5450c72083950e795795b8fd55e016ab67425 Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Wed, 19 Sep 2018 12:10:35 +0200
Subject: [PATCH 05/25] refactor
---
.gitignore | 2 ++
icons/instant-lyrics.svg | 1 -
src/appIndicator.py | 44 ++++++++++++++---------
src/lyrics.py | 19 +++++-----
src/utils.py | 18 ++++++----
src/windows.py | 76 ++++++++++++++++++----------------------
6 files changed, 86 insertions(+), 74 deletions(-)
diff --git a/.gitignore b/.gitignore
index c538041..0994cb6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
+.idea/
+
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
diff --git a/icons/instant-lyrics.svg b/icons/instant-lyrics.svg
index 92d2872..2da7891 100644
--- a/icons/instant-lyrics.svg
+++ b/icons/instant-lyrics.svg
@@ -3,7 +3,6 @@
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xml:space="preserve"
style="enable-background:new 0 0 97.536 97.536;"
diff --git a/src/appIndicator.py b/src/appIndicator.py
index 920a294..a9facd8 100644
--- a/src/appIndicator.py
+++ b/src/appIndicator.py
@@ -2,40 +2,48 @@
# -*- coding: utf-8 -*-
import gi
-gi.require_version('Gtk', '3.0')
-gi.require_version('AppIndicator3', '0.1')
-from gi.repository import AppIndicator3 as appindicator
-from gi.repository import Gtk
-
import signal
import threading
from src.windows import LyricsWindow, PreferenceWindow
from . import utils
-from src.settings import APPINDICATOR_ID, CONFIG_PATH
+from src.settings import APPINDICATOR_ID
import dbus
from dbus.mainloop.glib import DBusGMainLoop
+
+gi.require_version('Gtk', '3.0')
+gi.require_version('AppIndicator3', '0.1')
+
+from gi.repository import AppIndicator3 as appindicator
+from gi.repository import Gtk
+
+
DBusGMainLoop(set_as_default=True)
+ICON_PATH = '../icons/instant-lyrics-24.png'
+ICON_PATH = utils.get_icon_path(ICON_PATH) # full path
-class AppIndicator():
+
+class AppIndicator:
def __init__(self):
signal.signal(signal.SIGINT, signal.SIG_DFL)
-
+
bus = dbus.SessionBus()
-
+
bus.add_signal_receiver(self.build_menu, path="/org/mpris/MediaPlayer2")
- self.indicator = appindicator.Indicator.new(APPINDICATOR_ID, utils.get_icon_path(
-'../icons/instant-lyrics-24.png'), appindicator.IndicatorCategory.SYSTEM_SERVICES)
+ self.indicator = appindicator.Indicator.new(
+ APPINDICATOR_ID, ICON_PATH,
+ appindicator.IndicatorCategory.SYSTEM_SERVICES)
self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE)
self.build_menu()
-
+
self.Config = utils.get_config()
-
+
Gtk.main()
- def list_apps(self):
+ @staticmethod
+ def list_apps():
apps = []
session_bus = dbus.SessionBus()
for service in session_bus.list_names():
@@ -53,10 +61,10 @@ def build_menu(self, *args):
apps = self.list_apps()
for app in apps:
- current = Gtk.MenuItem(app.capitalize()+" lyrics")
+ current = Gtk.MenuItem(app.capitalize() + " lyrics")
current.connect('activate', self.app_lyrics, app)
menu.insert(current, 1)
-
+
preferences = Gtk.MenuItem('Preferences')
preferences.connect('activate', self.preferences)
menu.append(preferences)
@@ -70,15 +78,19 @@ def build_menu(self, *args):
def fetch_lyrics(self, source):
win = LyricsWindow("get", self)
+ win.show_all()
def app_lyrics(self, source, app):
win = LyricsWindow("app", self)
+ win.show_all()
+
thread = threading.Thread(target=win.get_lyrics, args=(app,))
thread.daemon = True
thread.start()
def preferences(self, source):
win = PreferenceWindow(self)
+ win.show_all()
def quit(self, source):
Gtk.main_quit()
diff --git a/src/lyrics.py b/src/lyrics.py
index 7b00bdc..897820a 100644
--- a/src/lyrics.py
+++ b/src/lyrics.py
@@ -3,7 +3,6 @@
import requests
from bs4 import BeautifulSoup
-import os
try:
from urllib.parse import quote_plus
@@ -12,23 +11,23 @@
def get_lyrics(song_name):
-
song_name += ' metrolyrics'
name = quote_plus(song_name)
- hdr = {'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0',
- 'Accept-Encoding': 'gzip, deflate, br',
- 'DNT': '1',
- 'UPGRADE-INSECURE-REQUESTS': '1',
- 'Connection': 'keep-alive'}
+ hdr = {
+ 'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0',
+ 'Accept-Encoding': 'gzip, deflate, br',
+ 'DNT': '1',
+ 'UPGRADE-INSECURE-REQUESTS': '1',
+ 'Connection': 'keep-alive'}
url = 'https://www.google.com/search?q=' + name
result = requests.get(url, headers=hdr).text
link_start = result.find('http://www.metrolyrics.com')
- if(link_start == -1):
- return("Lyrics not found on Metrolyrics")
-
+ if (link_start == -1):
+ return ("Lyrics not found on Metrolyrics")
+
link_end = result.find('html', link_start + 1) + 4
link = result[link_start:link_end]
diff --git a/src/utils.py b/src/utils.py
index 3052b1e..475eeef 100644
--- a/src/utils.py
+++ b/src/utils.py
@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
import os
+
try:
import configparser
except ImportError:
@@ -9,12 +10,13 @@
from src.settings import CONFIG_PATH
+
def create_default_config():
if not os.path.isdir(os.path.dirname(CONFIG_PATH)):
os.makedirs(os.path.dirname(CONFIG_PATH))
Config = configparser.ConfigParser()
-
+
Config.add_section('Main')
Config.set('Main', "window width", "350")
Config.set('Main', "window height", "650")
@@ -22,22 +24,25 @@ def create_default_config():
with open(CONFIG_PATH, 'w') as config_file:
Config.write(config_file)
+
def get_config():
if not os.path.isfile(CONFIG_PATH):
create_default_config()
-
+
Config = configparser.ConfigParser()
Config.read(CONFIG_PATH)
return Config
+
def get_icon_path(rel_path):
dir_of_py_file = os.path.dirname(__file__)
rel_path_to_resource = os.path.join(dir_of_py_file, rel_path)
abs_path_to_resource = os.path.abspath(rel_path_to_resource)
return abs_path_to_resource
+
def create_desktop_entry():
- #path of scr folder
+ # path of scr folder
src_path = os.path.dirname(os.path.realpath(__file__))
# path of base folder
base_path = os.path.dirname(src_path)
@@ -46,12 +51,13 @@ def create_desktop_entry():
v = "Version=1.0\n"
tp = "Type=Application\n"
nm = "Name=Instant Lyrics\n"
- ic = "Icon="+base_path+"/icons/instant-lyrics-256.png\n"
- ex = "Exec=python2 "+base_path+"/InstantLyrics.py\n"
+ ic = "Icon=" + base_path + "/icons/instant-lyrics-256.png\n"
+ ex = "Exec=python2 " + base_path + "/InstantLyrics.py\n"
cm = "Comment=Shows lyrics of songs instantly\n"
tm = "Terminal=false\n"
- entry_path = os.getenv("HOME") + "/.local/share/applications/instant-lyrics.desktop"
+ entry_path = os.getenv(
+ "HOME") + "/.local/share/applications/instant-lyrics.desktop"
with open(entry_path, 'w') as file:
file.write(entry)
diff --git a/src/windows.py b/src/windows.py
index ed79240..77307b3 100644
--- a/src/windows.py
+++ b/src/windows.py
@@ -2,39 +2,35 @@
# -*- coding: utf-8 -*-
import gi
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk, Gdk
-
import dbus
import threading
-try:
- import configparser
-except ImportError:
- import ConfigParser as configparser
+
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk, Gdk
from src.lyrics import get_lyrics
from src.settings import CONFIG_PATH
from . import utils
-class LyricsWindow(Gtk.Window):
+class LyricsWindow(Gtk.Window):
def __init__(self, type, app):
Gtk.Window.__init__(self, title="Lyrics")
self.set_icon_from_file(
- utils.get_icon_path('../icons/instant-lyrics-32.png'))
+ utils.get_icon_path('../icons/instant-lyrics-32.png'))
self.set_border_width(20)
self.set_default_size(
- int(app.Config.get('Main', 'window width')),
- int(app.Config.get('Main', 'window height')))
+ int(app.Config.get('Main', 'window width')),
+ int(app.Config.get('Main', 'window height')))
self.set_position(Gtk.WindowPosition.CENTER)
self.main_box = Gtk.Box(
orientation=Gtk.Orientation.VERTICAL, spacing=6)
self.main_box.set_size_request(
- int(app.Config.get('Main', 'window width')),
- int(app.Config.get('Main', 'window height')))
+ int(app.Config.get('Main', 'window width')),
+ int(app.Config.get('Main', 'window height')))
- if(type == "get"):
+ if (type == "get"):
entry_hbox = self.create_input_box()
self.main_box.pack_start(entry_hbox, False, False, 10)
@@ -46,7 +42,6 @@ def __init__(self, type, app):
scrolled.add(self.main_box)
self.add(scrolled)
- self.show_all()
def on_key_release(self, widget, ev, data=None):
if ev.keyval == Gdk.KEY_Return:
@@ -86,8 +81,8 @@ def create_lyrics_box(self, app):
lyrics_vbox.pack_start(self.spinner, False, False, 5)
lyrics_vbox.pack_start(self.lyrics, False, False, 5)
lyrics_vbox.set_size_request(
- int(app.Config.get('Main', 'window width')),
- int(app.Config.get('Main', 'window height')))
+ int(app.Config.get('Main', 'window width')),
+ int(app.Config.get('Main', 'window height')))
return lyrics_vbox
@@ -114,7 +109,7 @@ def get_song_data(self, app):
session_bus = dbus.SessionBus()
app_bus = session_bus.get_object(
- "org.mpris.MediaPlayer2."+app, "/org/mpris/MediaPlayer2")
+ "org.mpris.MediaPlayer2." + app, "/org/mpris/MediaPlayer2")
app_properties = dbus.Interface(
app_bus, "org.freedesktop.DBus.Properties")
metadata = app_properties.Get(
@@ -133,11 +128,11 @@ def get_lyrics(self, app):
artist = song_data['artist']
except:
self.title.set_markup("Error")
- message = ("Could not get current "+app+" song\n"
- "no song is playing on "+app+".\n\n"
- "Else, report an issue here")
+ message = ("Could not get current " + app + " song\n"
+ "no song is playing on " + app + ".\n\n"
+ "Else, report an issue here")
self.lyrics.set_markup(message)
return
@@ -147,13 +142,14 @@ def get_lyrics(self, app):
self.put_lyrics(song + " " + artist)
-class PreferenceWindow(Gtk.Window):
+class PreferenceWindow(Gtk.Window):
def __init__(self, app):
Gtk.Window.__init__(self, title="Instant-Lyrics Prefenreces")
- self.set_icon_from_file(utils.get_icon_path('../icons/instant-lyrics-32.png'))
+ self.set_icon_from_file(
+ utils.get_icon_path('../icons/instant-lyrics-32.png'))
self.set_border_width(20)
- #self.set_default_size(350, 550)
+ # self.set_default_size(350, 550)
self.set_position(Gtk.WindowPosition.CENTER)
self.main_box = Gtk.Box(
@@ -183,7 +179,6 @@ def __init__(self, app):
self.main_box.pack_start(self.message, True, True, 0)
self.add(self.main_box)
- self.show_all()
def create_pref_box(self, app):
listbox = Gtk.ListBox()
@@ -191,7 +186,7 @@ def create_pref_box(self, app):
row = Gtk.ListBoxRow()
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
- row.add(hbox)
+ row.add(hbox)
width = Gtk.Label("Lyrics window width", xalign=0)
self.width_val = Gtk.Entry()
self.width_val.set_text(app.Config.get('Main', 'window width'))
@@ -199,7 +194,7 @@ def create_pref_box(self, app):
hbox.pack_start(width, True, True, 0)
hbox.pack_start(self.width_val, False, True, 0)
-
+
listbox.add(row)
row = Gtk.ListBoxRow()
@@ -210,10 +205,9 @@ def create_pref_box(self, app):
self.height_val.set_text(app.Config.get('Main', 'window height'))
self.height_val.connect("changed", self.entry_change)
-
hbox.pack_start(height, True, True, 0)
hbox.pack_start(self.height_val, False, True, 0)
-
+
listbox.add(row)
""" TODO: autostart
@@ -235,7 +229,7 @@ def create_pref_box(self, app):
listbox.add(row)
"""
-
+
return listbox
def save_config(self, source, *arg):
@@ -246,17 +240,17 @@ def save_config(self, source, *arg):
new_width = self.width_val.get_text()
new_height = self.height_val.get_text()
- if(new_width.isdigit() and new_height.isdigit()):
+ if (new_width.isdigit() and new_height.isdigit()):
app.Config.set('Main', "window width", new_width)
app.Config.set('Main', "window height", new_height)
-
+
with open(CONFIG_PATH, 'w') as config_file:
app.Config.write(config_file)
return
msg = ("Invalid values of height and width\n"
- "Please add valid positive integers")
+ "Please add valid positive integers")
self.show_message(msg)
@@ -274,12 +268,12 @@ def reset_config(self, source, *arg):
def create_desktop_entry(self, source):
utils.create_desktop_entry()
- msg = ("Desktop entry created. You can now start the\n"
- "application from your Applications Launcher.\n\n"
- "If you ever change the location "
- "of the Instant-Lyrics\nfolder, you will "
- "need to create the Desktop Entry\nfrom "
- "here again.")
+ msg = ("Desktop entry created. You can now start the\n"
+ "application from your Applications Launcher.\n\n"
+ "If you ever change the location "
+ "of the Instant-Lyrics\nfolder, you will "
+ "need to create the Desktop Entry\nfrom "
+ "here again.")
self.show_message(msg)
From b673aab4ddd4aced8094e1a7457972285586023d Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Wed, 19 Sep 2018 12:11:20 +0200
Subject: [PATCH 06/25] fix shebang lines
---
InstantLyrics.py | 4 ++--
src/appIndicator.py | 14 +++++++-------
src/lyrics.py | 4 ++--
src/settings.py | 4 ++--
src/utils.py | 4 ++--
src/windows.py | 9 +++++----
6 files changed, 20 insertions(+), 19 deletions(-)
diff --git a/InstantLyrics.py b/InstantLyrics.py
index 04b641c..850dd7e 100755
--- a/InstantLyrics.py
+++ b/InstantLyrics.py
@@ -1,5 +1,5 @@
-#!/usr/bin/python3
-# -*- coding: utf-8 -*-
+# !/usr/bin/python3
+# coding: utf-8
from src.appIndicator import AppIndicator
diff --git a/src/appIndicator.py b/src/appIndicator.py
index a9facd8..b41414a 100644
--- a/src/appIndicator.py
+++ b/src/appIndicator.py
@@ -1,17 +1,17 @@
-#!/usr/bin/python3
-# -*- coding: utf-8 -*-
+# !/usr/bin/python3
+# coding: utf-8
-import gi
import signal
import threading
-from src.windows import LyricsWindow, PreferenceWindow
-from . import utils
-from src.settings import APPINDICATOR_ID
-
import dbus
+import gi
from dbus.mainloop.glib import DBusGMainLoop
+from src.settings import APPINDICATOR_ID
+from src.windows import LyricsWindow, PreferenceWindow
+from . import utils
+
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
diff --git a/src/lyrics.py b/src/lyrics.py
index 897820a..b1b785b 100644
--- a/src/lyrics.py
+++ b/src/lyrics.py
@@ -1,5 +1,5 @@
-#!/usr/bin/python3
-# -*- coding: utf-8 -*-
+# !/usr/bin/python3
+# coding: utf-8
import requests
from bs4 import BeautifulSoup
diff --git a/src/settings.py b/src/settings.py
index 31106a7..c9a559c 100644
--- a/src/settings.py
+++ b/src/settings.py
@@ -1,5 +1,5 @@
-#!/usr/bin/python3
-# -*- coding: utf-8 -*-
+# !/usr/bin/python3
+# coding: utf-8
import os
diff --git a/src/utils.py b/src/utils.py
index 475eeef..9807d8b 100644
--- a/src/utils.py
+++ b/src/utils.py
@@ -1,5 +1,5 @@
-#!/usr/bin/python3
-# -*- coding: utf-8 -*-
+# !/usr/bin/python3
+# coding: utf-8
import os
diff --git a/src/windows.py b/src/windows.py
index 77307b3..7bc5e5e 100644
--- a/src/windows.py
+++ b/src/windows.py
@@ -1,10 +1,11 @@
-#!/usr/bin/python3
-# -*- coding: utf-8 -*-
+# !/usr/bin/python3
+# coding: utf-8
-import gi
-import dbus
import threading
+import dbus
+import gi
+
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
From 55c92a037a44abd1be7a9d20b377d005601fd40c Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Wed, 19 Sep 2018 12:13:41 +0200
Subject: [PATCH 07/25] fix gitignore
---
.gitignore | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++--
_config.yml | 1 -
2 files changed, 212 insertions(+), 7 deletions(-)
delete mode 100644 _config.yml
diff --git a/.gitignore b/.gitignore
index 0994cb6..a921afa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,60 @@
.idea/
+tmp/
+### PyCharm ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff:
+.idea/workspace.xml
+.idea/tasks.xml
+
+# Sensitive or high-churn files:
+.idea/dataSources/
+.idea/dataSources.ids
+.idea/dataSources.xml
+.idea/dataSources.local.xml
+.idea/sqlDataSources.xml
+.idea/dynamic.xml
+.idea/uiDesigner.xml
+
+# Gradle:
+.idea/gradle.xml
+.idea/libraries
+
+# Mongo Explorer plugin:
+.idea/mongoSettings.xml
+
+## File-based project format:
+*.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+/out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+### PyCharm Patch ###
+# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
+
+# *.iml
+# modules.xml
+# .idea/misc.xml
+# *.ipr
+
+
+### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
@@ -22,7 +77,6 @@ lib64/
parts/
sdist/
var/
-wheels/
*.egg-info/
.installed.cfg
*.egg
@@ -78,14 +132,11 @@ target/
# celery beat schedule file
celerybeat-schedule
-# SageMath parsed files
-*.sage.py
-
# dotenv
.env
# virtualenv
-.venv
+.venv/
venv/
ENV/
@@ -93,4 +144,159 @@ ENV/
.spyderproject
# Rope project settings
-.ropeproject
\ No newline at end of file
+.ropeproject
+
+
+### Django ###
+*.pyc
+db.sqlite3
+media
+
+
+### Eclipse ###
+
+.metadata
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.settings/
+.loadpath
+.recommenders
+
+# Eclipse Core
+.project
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# PyDev specific (Python IDE for Eclipse)
+*.pydevproject
+
+# CDT-specific (C/C++ Development Tooling)
+.cproject
+
+# JDT-specific (Eclipse Java Development Tools)
+.classpath
+
+# Java annotation processor (APT)
+.factorypath
+
+# PDT-specific (PHP Development Tools)
+.buildpath
+
+# sbteclipse plugin
+.target
+
+# Tern plugin
+.tern-project
+
+# TeXlipse plugin
+.texlipse
+
+# STS (Spring Tool Suite)
+.springBeans
+
+# Code Recommenders
+.recommenders/
+
+
+### NetBeans ###
+nbproject/private/
+build/
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
+
+
+### Intellij ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff:
+.idea/workspace.xml
+.idea/tasks.xml
+
+# Sensitive or high-churn files:
+.idea/dataSources/
+.idea/dataSources.ids
+.idea/dataSources.xml
+.idea/dataSources.local.xml
+.idea/sqlDataSources.xml
+.idea/dynamic.xml
+.idea/uiDesigner.xml
+
+# Gradle:
+.idea/gradle.xml
+.idea/libraries
+
+# Mongo Explorer plugin:
+.idea/mongoSettings.xml
+
+## File-based project format:
+*.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+/out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+### Intellij Patch ###
+# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
+
+# *.iml
+# modules.xml
+# .idea/misc.xml
+# *.ipr
+
+
+### Intellij+iml ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff:
+
+# Sensitive or high-churn files:
+
+# Gradle:
+
+# Mongo Explorer plugin:
+
+## File-based project format:
+
+## Plugin-specific files:
+
+# IntelliJ
+
+# mpeltonen/sbt-idea plugin
+
+# JIRA plugin
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+
+### Intellij+iml Patch ###
+# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
+
+*.iml
+modules.xml
+.idea/misc.xml
+*.ipr
diff --git a/_config.yml b/_config.yml
deleted file mode 100644
index 2f7efbe..0000000
--- a/_config.yml
+++ /dev/null
@@ -1 +0,0 @@
-theme: jekyll-theme-minimal
\ No newline at end of file
From 0c9aafadc98bc8bdcec83d49d9e01985d5c1603d Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Wed, 19 Sep 2018 12:32:21 +0200
Subject: [PATCH 08/25] pretty readme
---
README.md | 71 +++++++++++++++++++++----------------------------------
1 file changed, 27 insertions(+), 44 deletions(-)
diff --git a/README.md b/README.md
index f6a38e0..7f7347b 100644
--- a/README.md
+++ b/README.md
@@ -1,48 +1,38 @@
-# Instant-Lyrics
-
-Instantly fetches the lyrics of the currently playing spotify song, or any song, and displays it on a window.
-
-A linux application with a very convinient GUI. Build with Python Gtk+3 (gi).
+
+
Instant-Lyrics
+Instantly fetches the lyrics of the currently playing song and displays it on a window.
+A linux application with a very convenient GUI. Built with Python 3 Gtk+3 (gi).
+
+
# Screenshot
![Screenshot](https://cloud.githubusercontent.com/assets/6123105/23824316/3fe58044-069a-11e7-804e-180ea4041002.jpeg)
-# Working
-### GIF
-![working](https://cloud.githubusercontent.com/assets/6123105/23824730/e0e0829e-06a1-11e7-8d57-3235c4266f2c.gif)
-
+# Working example
+![Working](https://cloud.githubusercontent.com/assets/6123105/23824730/e0e0829e-06a1-11e7-8d57-3235c4266f2c.gif)
# Compatibility
-
-* Python 2/3
-
-* Linux
-
+- Python 3
+- Linux
# Installation
-## From Source
-
-### Requirements
-
-* python-gi (PyGObject)
-
-* AppIndicator3
+### system requirements (use `apt`, `pacman`, `dnf` ... your package manager):
+- python-gi (PyGObject)
+- python-dbus
+- AppIndicator3
-* python-dbus
-
-* requests
-
-* beautifulsoup4
-
-* lxml
+### python requirements (use `pip2` or `pip3`):
+- requests
+- beautifulsoup4
+- lxml
First, install the requirements:
### For Ubuntu/Debian based systems:
-``` sh
+``` bash
sudo apt install python-gi python-dbus gir1.2-appindicator3-0.1 python-requests python-bs4 python-lxml
```
@@ -50,26 +40,23 @@ sudo apt install python-gi python-dbus gir1.2-appindicator3-0.1 python-requests
### For Arch users
-``` sh
+``` bash
sudo pacman -S python2-dbus python2-requests python2-lxml python2-beautifulsoup4 python2-gobject libappindicator-gtk3
```
### Fedora
-``` sh
+``` bash
sudo dnf install dbus-python python-gobject libappindicator-gtk3 python2-requests python-beautifulsoup4 python2-lxml
```
-## Install from source
+Then, enter the commands:
-After you've installed the dependencies, open terminal and go to the directory where you want to install. Enter the commands:
-
-``` sh
+``` bash
git clone https://github.com/bhrigu123/Instant-Lyrics.git
-
cd Instant-Lyrics/
-
-python InstantLyrics.py
+python InstantLyrics.py # `python InstantLyrics.py &` to keep it running in
+background
```
The icon will appear in the system tray (indicator panel). You can start using the application from there.
@@ -79,9 +66,7 @@ The icon will appear in the system tray (indicator panel). You can start using t
# Creating a launcher shortcut
If you have installed from source, you can go to **Preferences** from the menu options, and click on the button `Create Desktop Entry`.
-
You should be able to see the `Instant Lyrics` application shortcut in your launcher menu.
-
You can also find several manual ways of doing so from the web.
![Launcher](https://cloud.githubusercontent.com/assets/6123105/23824317/4735e83e-069a-11e7-8b1e-2814632bb3aa.jpeg)
@@ -90,12 +75,10 @@ You can also find several manual ways of doing so from the web.
# Contribution
Create an issue to discuss the changes/modifications before sending a PR.
-======
-## Icon Credits
+# Icon Credits
Icon made by [Freepik](http://www.freepik.com/) from www.flaticon.com
-======
-
+# License
## The MIT License
> Copyright (c) 2017 Bhrigu Srivastava http://bhrigu.me
From 3eb81ba14267dde20010ad8e1cbca93ce6d61396 Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Wed, 19 Sep 2018 12:55:09 +0200
Subject: [PATCH 09/25] add default icon getter
---
src/appIndicator.py | 27 +++++++++++++--------------
src/utils.py | 4 ++++
src/windows.py | 7 +++----
3 files changed, 20 insertions(+), 18 deletions(-)
diff --git a/src/appIndicator.py b/src/appIndicator.py
index b41414a..2a82fee 100644
--- a/src/appIndicator.py
+++ b/src/appIndicator.py
@@ -10,6 +10,7 @@
from src.settings import APPINDICATOR_ID
from src.windows import LyricsWindow, PreferenceWindow
+from utils import get_default_icon_path
from . import utils
gi.require_version('Gtk', '3.0')
@@ -20,8 +21,17 @@
DBusGMainLoop(set_as_default=True)
-ICON_PATH = '../icons/instant-lyrics-24.png'
-ICON_PATH = utils.get_icon_path(ICON_PATH) # full path
+ICON_PATH = get_default_icon_path() # full path
+
+
+def list_music_apps():
+ apps = []
+ session_bus = dbus.SessionBus()
+ for service in session_bus.list_names():
+ if service[:22] == "org.mpris.MediaPlayer2":
+ apps.append(service[23:])
+
+ return apps
class AppIndicator:
@@ -42,16 +52,6 @@ def __init__(self):
Gtk.main()
- @staticmethod
- def list_apps():
- apps = []
- session_bus = dbus.SessionBus()
- for service in session_bus.list_names():
- if service[:22] == "org.mpris.MediaPlayer2":
- apps.append(service[23:])
-
- return apps
-
def build_menu(self, *args):
menu = Gtk.Menu()
@@ -59,8 +59,7 @@ def build_menu(self, *args):
item_lyrics.connect('activate', self.fetch_lyrics)
menu.append(item_lyrics)
- apps = self.list_apps()
- for app in apps:
+ for app in list_music_apps():
current = Gtk.MenuItem(app.capitalize() + " lyrics")
current.connect('activate', self.app_lyrics, app)
menu.insert(current, 1)
diff --git a/src/utils.py b/src/utils.py
index 9807d8b..d0aeb60 100644
--- a/src/utils.py
+++ b/src/utils.py
@@ -41,6 +41,10 @@ def get_icon_path(rel_path):
return abs_path_to_resource
+def get_default_icon_path():
+ return get_icon_path('../icons/instant-lyrics-32.png')
+
+
def create_desktop_entry():
# path of scr folder
src_path = os.path.dirname(os.path.realpath(__file__))
diff --git a/src/windows.py b/src/windows.py
index 7bc5e5e..eac308f 100644
--- a/src/windows.py
+++ b/src/windows.py
@@ -17,8 +17,7 @@
class LyricsWindow(Gtk.Window):
def __init__(self, type, app):
Gtk.Window.__init__(self, title="Lyrics")
- self.set_icon_from_file(
- utils.get_icon_path('../icons/instant-lyrics-32.png'))
+ self.set_icon_from_file(utils.get_default_icon_path())
self.set_border_width(20)
self.set_default_size(
int(app.Config.get('Main', 'window width')),
@@ -31,7 +30,8 @@ def __init__(self, type, app):
int(app.Config.get('Main', 'window width')),
int(app.Config.get('Main', 'window height')))
- if (type == "get"):
+ if type == "get":
+ self.input = Gtk.Entry()
entry_hbox = self.create_input_box()
self.main_box.pack_start(entry_hbox, False, False, 10)
@@ -53,7 +53,6 @@ def create_input_box(self):
orientation=Gtk.Orientation.HORIZONTAL, spacing=50)
entry_hbox.set_property("margin", 10)
- self.input = Gtk.Entry()
self.input.set_placeholder_text("song/artist")
self.input.connect("key-release-event", self.on_key_release)
entry_hbox.pack_start(self.input, True, True, 0)
From 2151966ab52187f92b22d32aee10ef3fb66f39d1 Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Tue, 25 Sep 2018 09:54:37 +0200
Subject: [PATCH 10/25] fix lyrics params
---
src/lyrics.py | 69 ++++++++++++++++++++++++++++++++------------------
src/utils.py | 23 ++++++++++-------
src/windows.py | 16 +++++-------
3 files changed, 64 insertions(+), 44 deletions(-)
diff --git a/src/lyrics.py b/src/lyrics.py
index b1b785b..d22af1e 100644
--- a/src/lyrics.py
+++ b/src/lyrics.py
@@ -1,48 +1,67 @@
# !/usr/bin/python3
# coding: utf-8
+import urllib.parse as urlparse
+from urllib.parse import urlencode
+
import requests
from bs4 import BeautifulSoup
-try:
- from urllib.parse import quote_plus
-except ImportError:
- from urllib import quote_plus
+HEADERS = {
+ 'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.0) '
+ 'Gecko/20100101 Firefox/52.0',
+ 'UPGRADE-INSECURE-REQUESTS': '1',
+ 'Connection': 'keep-alive'
+}
+BASE_URL = "https://www.google.com/search"
+
+def add_params_to_url(url, params):
+ """
+ :param url: str
+ Url to add params to
+ :param params: {}
+ List of params to add to url
+ :return: void
+ Adds params to url
+ """
-def get_lyrics(song_name):
- song_name += ' metrolyrics'
- name = quote_plus(song_name)
- hdr = {
- 'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0',
- 'Accept-Encoding': 'gzip, deflate, br',
- 'DNT': '1',
- 'UPGRADE-INSECURE-REQUESTS': '1',
- 'Connection': 'keep-alive'}
+ url_parts = list(urlparse.urlparse(url)) # get url parts
+ query = dict(urlparse.parse_qsl(url_parts[4])) # get url query
+ query.update(params) # add new params
+ url_parts[4] = urlencode(query)
+ return urlparse.urlunparse(url_parts)
- url = 'https://www.google.com/search?q=' + name
- result = requests.get(url, headers=hdr).text
+def get_metrolyrics_from_google(query):
+ query += ' metrolyrics:' # search just metrolyrics
+ url = add_params_to_url(BASE_URL, {
+ "q": query
+ })
+ response = requests.get(url, headers=HEADERS)
+ result = response.text
link_start = result.find('http://www.metrolyrics.com')
+ return link_start, result
+
+
+def get_lyrics(query):
+ link_start, result = get_metrolyrics_from_google(query)
- if (link_start == -1):
- return ("Lyrics not found on Metrolyrics")
+ if link_start == -1:
+ return "Lyrics not found"
link_end = result.find('html', link_start + 1) + 4
link = result[link_start:link_end]
- r = requests.get(link, headers=hdr)
+ r = requests.get(link, headers=HEADERS)
r.encoding = "utf-8"
lyrics_html = r.text
soup = BeautifulSoup(lyrics_html, "lxml")
raw_lyrics = (soup.findAll('p', attrs={'class': 'verse'}))
- try:
- final_lyrics = unicode.join(u'\n', map(unicode, raw_lyrics))
- except NameError:
- final_lyrics = str.join(u'\n', map(str, raw_lyrics))
+ final_lyrics = str.join(u'\n', map(str, raw_lyrics))
- final_lyrics = (final_lyrics.replace('', '\n'))
- final_lyrics = (final_lyrics.replace('
', ' '))
+ final_lyrics = final_lyrics.replace('
', '\n')
+ final_lyrics = final_lyrics.replace('
', ' ')
final_lyrics = final_lyrics.replace('
', ' ')
- return (final_lyrics)
+ return final_lyrics
diff --git a/src/utils.py b/src/utils.py
index d0aeb60..60f00d5 100644
--- a/src/utils.py
+++ b/src/utils.py
@@ -1,15 +1,15 @@
# !/usr/bin/python3
# coding: utf-8
+import configparser
import os
-try:
- import configparser
-except ImportError:
- import ConfigParser as configparser
-
from src.settings import CONFIG_PATH
+BUG_ISSUES_REPORT_ERROR = "If that's not the case, report an issue here "
+
def create_default_config():
if not os.path.isdir(os.path.dirname(CONFIG_PATH)):
@@ -45,11 +45,16 @@ def get_default_icon_path():
return get_icon_path('../icons/instant-lyrics-32.png')
+def get_general_error(app):
+ out = "Could not get current " + app + " song"
+ out += "\nno song is playing on " + app
+ out += "\n\n" + BUG_ISSUES_REPORT_ERROR
+ return out
+
+
def create_desktop_entry():
- # path of scr folder
- src_path = os.path.dirname(os.path.realpath(__file__))
- # path of base folder
- base_path = os.path.dirname(src_path)
+ src_path = os.path.dirname(os.path.realpath(__file__)) # path of scr folder
+ base_path = os.path.dirname(src_path) # path of base folder
entry = "[Desktop Entry]\n"
v = "Version=1.0\n"
diff --git a/src/windows.py b/src/windows.py
index eac308f..7b4a9b3 100644
--- a/src/windows.py
+++ b/src/windows.py
@@ -6,6 +6,8 @@
import dbus
import gi
+from utils import get_general_error
+
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
@@ -86,11 +88,11 @@ def create_lyrics_box(self, app):
return lyrics_vbox
- def put_lyrics(self, song):
+ def put_lyrics(self, query):
self.spinner.start()
self.lyrics.set_text("")
- lyrics = get_lyrics(song)
+ lyrics = get_lyrics(query)
self.lyrics.set_text(lyrics)
self.spinner.stop()
@@ -101,7 +103,7 @@ def fetch_lyrics(self, source=None):
self.title.set_markup(text)
thread = threading.Thread(
- target=self.put_lyrics, kwargs={'song': input})
+ target=self.put_lyrics, kwargs={'query': input})
thread.daemon = True
thread.start()
@@ -128,18 +130,12 @@ def get_lyrics(self, app):
artist = song_data['artist']
except:
self.title.set_markup("Error")
- message = ("Could not get current " + app + " song\n"
- "no song is playing on " + app + ".\n\n"
- "Else, report an issue here")
-
+ message = get_general_error(app)
self.lyrics.set_markup(message)
return
title = "" + song + "\n" + artist + ""
self.title.set_markup(title)
-
self.put_lyrics(song + " " + artist)
From 8f15e0e13b6973558558cefaecb7905f52d5ee5b Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Tue, 25 Sep 2018 10:37:48 +0200
Subject: [PATCH 11/25] add duckduckgo metrolyrics fetcher
---
src/lyrics.py | 68 +++++++++++++++++++++++++++++++++++++++++---------
src/windows.py | 40 ++++++++++++++++++-----------
2 files changed, 82 insertions(+), 26 deletions(-)
diff --git a/src/lyrics.py b/src/lyrics.py
index d22af1e..0b4af8b 100644
--- a/src/lyrics.py
+++ b/src/lyrics.py
@@ -1,6 +1,7 @@
# !/usr/bin/python3
# coding: utf-8
+import abc
import urllib.parse as urlparse
from urllib.parse import urlencode
@@ -13,7 +14,7 @@
'UPGRADE-INSECURE-REQUESTS': '1',
'Connection': 'keep-alive'
}
-BASE_URL = "https://www.google.com/search"
+NOT_FOUND_MESSAGE = "Lyrics not found"
def add_params_to_url(url, params):
@@ -33,22 +34,60 @@ def add_params_to_url(url, params):
return urlparse.urlunparse(url_parts)
-def get_metrolyrics_from_google(query):
- query += ' metrolyrics:' # search just metrolyrics
- url = add_params_to_url(BASE_URL, {
- "q": query
- })
- response = requests.get(url, headers=HEADERS)
- result = response.text
- link_start = result.find('http://www.metrolyrics.com')
- return link_start, result
+class MetrolyricsFetcher:
+ """
+ Define abstract primitive operations that concrete subclasses define
+ to implement steps of an algorithm.
+ Implement a template method defining the skeleton of an algorithm.
+ The template method calls primitive operations as well as operations
+ defined in AbstractClass or those of other objects.
+ """
+
+ def __init__(self, query):
+ self.query = query
+
+ def get_lyrics(self):
+ query = self._get_query(self.query)
+ url = self._get_url(query)
+ response = requests.get(url, headers=HEADERS)
+ result = response.text
+ link_start = result.find('http://www.metrolyrics.com')
+ return link_start, result
+
+ @abc.abstractmethod
+ def _get_query(self, query):
+ pass
+
+ @abc.abstractmethod
+ def _get_url(self, query):
+ pass
+
+
+class GoogleMetrolyricsFetcher(MetrolyricsFetcher):
+ def _get_query(self, query):
+ return query + ' metrolyrics:' # search just metrolyrics
+
+ def _get_url(self, query):
+ return add_params_to_url("https://www.google.com/search", {
+ "q": query
+ })
+
+
+class DuckDuckGoMetrolyricsFetcher(MetrolyricsFetcher):
+ def _get_query(self, query):
+ return 'site:metrolyrics.com ' + query # search just metrolyrics
+
+ def _get_url(self, query):
+ return add_params_to_url("https://duckduckgo.com/html", {
+ "q": query
+ })
def get_lyrics(query):
- link_start, result = get_metrolyrics_from_google(query)
+ link_start, result = DuckDuckGoMetrolyricsFetcher(query).get_lyrics()
if link_start == -1:
- return "Lyrics not found"
+ return NOT_FOUND_MESSAGE
link_end = result.find('html', link_start + 1) + 4
link = result[link_start:link_end]
@@ -64,4 +103,9 @@ def get_lyrics(query):
final_lyrics = final_lyrics.replace('', '\n')
final_lyrics = final_lyrics.replace('
', ' ')
final_lyrics = final_lyrics.replace('
', ' ')
+ final_lyrics = final_lyrics.strip()
+
+ if len(final_lyrics) < 10:
+ return NOT_FOUND_MESSAGE
+
return final_lyrics
diff --git a/src/windows.py b/src/windows.py
index 7b4a9b3..996332f 100644
--- a/src/windows.py
+++ b/src/windows.py
@@ -2,6 +2,7 @@
# coding: utf-8
import threading
+import time
import dbus
import gi
@@ -45,6 +46,8 @@ def __init__(self, type, app):
scrolled.add(self.main_box)
self.add(scrolled)
+ self.current_song = ""
+ self.current_artist = ""
def on_key_release(self, widget, ev, data=None):
if ev.keyval == Gdk.KEY_Return:
@@ -107,7 +110,7 @@ def fetch_lyrics(self, source=None):
thread.daemon = True
thread.start()
- def get_song_data(self, app):
+ def fetch_song_data(self, app):
session_bus = dbus.SessionBus()
app_bus = session_bus.get_object(
@@ -121,22 +124,31 @@ def get_song_data(self, app):
'utf-8').decode('utf-8').replace("&", "&")
artist = metadata['xesam:artist'][0].encode(
'utf-8').decode('utf-8').replace("&", "&")
- return {'title': title, 'artist': artist}
- def get_lyrics(self, app):
- try:
- song_data = self.get_song_data(app)
- song = song_data['title']
- artist = song_data['artist']
- except:
- self.title.set_markup("Error")
- message = get_general_error(app)
- self.lyrics.set_markup(message)
- return
+ self.current_song = title
+ self.current_artist = artist
- title = "" + song + "\n" + artist + ""
+ def set_current_song_title(self):
+ title = "" + self.current_song + "\n" + \
+ self.current_artist + ""
self.title.set_markup(title)
- self.put_lyrics(song + " " + artist)
+
+ def set_current_song_lyrics(self):
+ self.put_lyrics(self.current_song + " " + self.current_artist)
+
+ def get_lyrics(self, app):
+ while True:
+ try:
+ self.fetch_song_data(app)
+ except:
+ self.title.set_markup("Error")
+ message = get_general_error(app)
+ self.lyrics.set_markup(message)
+ return
+
+ self.set_current_song_title()
+ self.set_current_song_lyrics()
+ time.sleep(2)
class PreferenceWindow(Gtk.Window):
From 8aca7f9b4b95c2b0df79fcef9109528020f818d3 Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Tue, 25 Sep 2018 10:45:54 +0200
Subject: [PATCH 12/25] listen to song change, fix #10
---
src/lyrics.py | 7 +++----
src/windows.py | 25 +++++++++++++++++++------
2 files changed, 22 insertions(+), 10 deletions(-)
diff --git a/src/lyrics.py b/src/lyrics.py
index 0b4af8b..c0b2abc 100644
--- a/src/lyrics.py
+++ b/src/lyrics.py
@@ -14,7 +14,6 @@
'UPGRADE-INSECURE-REQUESTS': '1',
'Connection': 'keep-alive'
}
-NOT_FOUND_MESSAGE = "Lyrics not found"
def add_params_to_url(url, params):
@@ -87,7 +86,7 @@ def get_lyrics(query):
link_start, result = DuckDuckGoMetrolyricsFetcher(query).get_lyrics()
if link_start == -1:
- return NOT_FOUND_MESSAGE
+ return None
link_end = result.find('html', link_start + 1) + 4
link = result[link_start:link_end]
@@ -105,7 +104,7 @@ def get_lyrics(query):
final_lyrics = final_lyrics.replace('', ' ')
final_lyrics = final_lyrics.strip()
- if len(final_lyrics) < 10:
- return NOT_FOUND_MESSAGE
+ if len(final_lyrics) < 20: # too little to be lyrcs => not found
+ return None
return final_lyrics
diff --git a/src/windows.py b/src/windows.py
index 996332f..4f65670 100644
--- a/src/windows.py
+++ b/src/windows.py
@@ -48,6 +48,7 @@ def __init__(self, type, app):
self.add(scrolled)
self.current_song = ""
self.current_artist = ""
+ self.current_lyrics = None
def on_key_release(self, widget, ev, data=None):
if ev.keyval == Gdk.KEY_Return:
@@ -95,8 +96,12 @@ def put_lyrics(self, query):
self.spinner.start()
self.lyrics.set_text("")
- lyrics = get_lyrics(query)
- self.lyrics.set_text(lyrics)
+ self.current_lyrics = get_lyrics(query)
+
+ if self.current_lyrics is None:
+ self.lyrics.set_text("Lyrics not found")
+ else:
+ self.lyrics.set_text(self.current_lyrics)
self.spinner.stop()
@@ -139,16 +144,24 @@ def set_current_song_lyrics(self):
def get_lyrics(self, app):
while True:
try:
+ previous_song = self.current_song
+ previous_artist = self.current_artist
self.fetch_song_data(app)
+
+ new_song = (previous_song != self.current_song) or (
+ previous_artist != self.current_artist)
+
+ print(previous_song, "VS", self.current_song)
+
+ if new_song: # no new song => return
+ self.set_current_song_title()
+ self.set_current_song_lyrics()
except:
self.title.set_markup("Error")
message = get_general_error(app)
self.lyrics.set_markup(message)
- return
- self.set_current_song_title()
- self.set_current_song_lyrics()
- time.sleep(2)
+ time.sleep(5)
class PreferenceWindow(Gtk.Window):
From 88a57c87004d8e9ec65615a4fc82ec9b896ea3d8 Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Tue, 25 Sep 2018 20:11:16 +0200
Subject: [PATCH 13/25] fix link finder
---
src/appIndicator.py | 6 +-
src/lyrics.py | 147 ++++++++++++++++++++++++++++++++++----------
src/windows.py | 2 -
3 files changed, 121 insertions(+), 34 deletions(-)
diff --git a/src/appIndicator.py b/src/appIndicator.py
index 2a82fee..2b2d1ce 100644
--- a/src/appIndicator.py
+++ b/src/appIndicator.py
@@ -18,10 +18,14 @@
from gi.repository import AppIndicator3 as appindicator
from gi.repository import Gtk
-
+import os
DBusGMainLoop(set_as_default=True)
ICON_PATH = get_default_icon_path() # full path
+LOCAL_LYRICS_PATH = os.path.join(
+ os.getenv("HOME"),
+ ".local", "Instant-Lyrics", "lyrics"
+)
def list_music_apps():
diff --git a/src/lyrics.py b/src/lyrics.py
index c0b2abc..76197e5 100644
--- a/src/lyrics.py
+++ b/src/lyrics.py
@@ -33,25 +33,57 @@ def add_params_to_url(url, params):
return urlparse.urlunparse(url_parts)
-class MetrolyricsFetcher:
+class LyricsFetcher:
"""
- Define abstract primitive operations that concrete subclasses define
- to implement steps of an algorithm.
- Implement a template method defining the skeleton of an algorithm.
- The template method calls primitive operations as well as operations
- defined in AbstractClass or those of other objects.
+ Searches for song lyrics in local folder
"""
def __init__(self, query):
self.query = query
+ @abc.abstractclassmethod
def get_lyrics(self):
+ return None
+
+
+class InternetLyricsFetcher(LyricsFetcher):
+ """
+ Searches internet for song lyrics
+ """
+
+ def __init__(self, query, search_engine):
+ LyricsFetcher.__init__(self, query)
+ self.url = search_engine
+
+ def get_lyrics(self):
+ try:
+ link = self.get_lyrics_link()
+ r = requests.get(link, headers=HEADERS)
+ r.encoding = "utf-8"
+ result = r.text
+ return self._parse_result(result)
+ except:
+ return None
+
+ def get_lyrics_link(self):
query = self._get_query(self.query)
url = self._get_url(query)
response = requests.get(url, headers=HEADERS)
result = response.text
- link_start = result.find('http://www.metrolyrics.com')
- return link_start, result
+
+ link_start = result.find(self.url)
+ if link_start == -1:
+ return None
+
+ link_end = min(
+ result.find('\'', link_start + 1),
+ result.find('"', link_start + 1)
+ )
+ return result[link_start:link_end]
+
+ @abc.abstractclassmethod
+ def _parse_result(self, result):
+ pass
@abc.abstractmethod
def _get_query(self, query):
@@ -62,9 +94,36 @@ def _get_url(self, query):
pass
-class GoogleMetrolyricsFetcher(MetrolyricsFetcher):
+class GeniusFetcher(InternetLyricsFetcher):
+ """
+ Define abstract primitive operations that concrete subclasses define
+ to implement steps of an algorithm.
+ Implement a template method defining the skeleton of an algorithm.
+ The template method calls primitive operations as well as operations
+ defined in AbstractClass or those of other objects.
+ """
+
+ def __init__(self, query):
+ InternetLyricsFetcher.__init__(
+ self, query, 'https://genius.com'
+ )
+
+ def _parse_result(self, result):
+ soup = BeautifulSoup(result, "lxml")
+ raw = soup.findAll("div", {"class": "lyrics"})[0]
+
+ parsed = raw.findAll("p")[0].text
+ # parsed = str.join(u'\n', map(str, parsed))
+
+ if len(parsed) < 20: # too little to be lyrcs => not found
+ return None
+
+ return parsed
+
+
+class GoogleGeniusFetcher(GeniusFetcher):
def _get_query(self, query):
- return query + ' metrolyrics:' # search just metrolyrics
+ return 'site:genius.com ' + query # search just genius
def _get_url(self, query):
return add_params_to_url("https://www.google.com/search", {
@@ -72,9 +131,9 @@ def _get_url(self, query):
})
-class DuckDuckGoMetrolyricsFetcher(MetrolyricsFetcher):
+class DuckDuckGoGeniusFetcher(GeniusFetcher):
def _get_query(self, query):
- return 'site:metrolyrics.com ' + query # search just metrolyrics
+ return 'site:genius.com ' + query # search just metrolyrics
def _get_url(self, query):
return add_params_to_url("https://duckduckgo.com/html", {
@@ -82,29 +141,55 @@ def _get_url(self, query):
})
-def get_lyrics(query):
- link_start, result = DuckDuckGoMetrolyricsFetcher(query).get_lyrics()
+class MetrolyricsFetcher(InternetLyricsFetcher):
+ """
+ Define abstract primitive operations that concrete subclasses define
+ to implement steps of an algorithm.
+ Implement a template method defining the skeleton of an algorithm.
+ The template method calls primitive operations as well as operations
+ defined in AbstractClass or those of other objects.
+ """
- if link_start == -1:
- return None
+ def __init__(self, query):
+ InternetLyricsFetcher.__init__(
+ self, query, 'http://www.metrolyrics.com'
+ )
- link_end = result.find('html', link_start + 1) + 4
- link = result[link_start:link_end]
+ def _parse_result(self, result):
+ soup = BeautifulSoup(result, "lxml")
+ raw = (soup.findAll('p', attrs={'class': 'verse'}))
- r = requests.get(link, headers=HEADERS)
- r.encoding = "utf-8"
- lyrics_html = r.text
+ parsed = str.join(u'\n', map(str, raw))
+ parsed = parsed.replace('', '\n')
+ parsed = parsed.replace('
', ' ')
+ parsed = parsed.replace('
', ' ')
+ parsed = parsed.strip()
- soup = BeautifulSoup(lyrics_html, "lxml")
- raw_lyrics = (soup.findAll('p', attrs={'class': 'verse'}))
- final_lyrics = str.join(u'\n', map(str, raw_lyrics))
+ if len(parsed) < 20: # too little to be lyrcs => not found
+ return None
- final_lyrics = final_lyrics.replace('', '\n')
- final_lyrics = final_lyrics.replace('
', ' ')
- final_lyrics = final_lyrics.replace('
', ' ')
- final_lyrics = final_lyrics.strip()
+ return parsed
- if len(final_lyrics) < 20: # too little to be lyrcs => not found
- return None
- return final_lyrics
+class GoogleMetrolyricsFetcher(MetrolyricsFetcher):
+ def _get_query(self, query):
+ return 'site:metrolyrics.com ' + query # search just metrolyrics
+
+ def _get_url(self, query):
+ return add_params_to_url("https://www.google.com/search", {
+ "q": query
+ })
+
+
+class DuckDuckGoMetrolyricsFetcher(MetrolyricsFetcher):
+ def _get_query(self, query):
+ return 'site:metrolyrics.com ' + query # search just metrolyrics
+
+ def _get_url(self, query):
+ return add_params_to_url("https://duckduckgo.com/html", {
+ "q": query
+ })
+
+
+def get_lyrics(query):
+ return GoogleMetrolyricsFetcher(query).get_lyrics()
diff --git a/src/windows.py b/src/windows.py
index 4f65670..80dcc4b 100644
--- a/src/windows.py
+++ b/src/windows.py
@@ -151,8 +151,6 @@ def get_lyrics(self, app):
new_song = (previous_song != self.current_song) or (
previous_artist != self.current_artist)
- print(previous_song, "VS", self.current_song)
-
if new_song: # no new song => return
self.set_current_song_title()
self.set_current_song_lyrics()
From 98caf7f98817516fe0506e45962901f7c5b5daa6 Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Tue, 25 Sep 2018 20:15:33 +0200
Subject: [PATCH 14/25] refactor lyrics fetchers
---
src/lyrics.py | 191 +-------------------------------------
src/lyrics/__init__.py | 0
src/lyrics/genius.py | 53 +++++++++++
src/lyrics/metrolyrics.py | 56 +++++++++++
src/lyrics/models.py | 93 +++++++++++++++++++
5 files changed, 204 insertions(+), 189 deletions(-)
create mode 100644 src/lyrics/__init__.py
create mode 100644 src/lyrics/genius.py
create mode 100644 src/lyrics/metrolyrics.py
create mode 100644 src/lyrics/models.py
diff --git a/src/lyrics.py b/src/lyrics.py
index 76197e5..74e87c4 100644
--- a/src/lyrics.py
+++ b/src/lyrics.py
@@ -1,195 +1,8 @@
# !/usr/bin/python3
# coding: utf-8
-import abc
-import urllib.parse as urlparse
-from urllib.parse import urlencode
-
-import requests
-from bs4 import BeautifulSoup
-
-HEADERS = {
- 'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.0) '
- 'Gecko/20100101 Firefox/52.0',
- 'UPGRADE-INSECURE-REQUESTS': '1',
- 'Connection': 'keep-alive'
-}
-
-
-def add_params_to_url(url, params):
- """
- :param url: str
- Url to add params to
- :param params: {}
- List of params to add to url
- :return: void
- Adds params to url
- """
-
- url_parts = list(urlparse.urlparse(url)) # get url parts
- query = dict(urlparse.parse_qsl(url_parts[4])) # get url query
- query.update(params) # add new params
- url_parts[4] = urlencode(query)
- return urlparse.urlunparse(url_parts)
-
-
-class LyricsFetcher:
- """
- Searches for song lyrics in local folder
- """
-
- def __init__(self, query):
- self.query = query
-
- @abc.abstractclassmethod
- def get_lyrics(self):
- return None
-
-
-class InternetLyricsFetcher(LyricsFetcher):
- """
- Searches internet for song lyrics
- """
-
- def __init__(self, query, search_engine):
- LyricsFetcher.__init__(self, query)
- self.url = search_engine
-
- def get_lyrics(self):
- try:
- link = self.get_lyrics_link()
- r = requests.get(link, headers=HEADERS)
- r.encoding = "utf-8"
- result = r.text
- return self._parse_result(result)
- except:
- return None
-
- def get_lyrics_link(self):
- query = self._get_query(self.query)
- url = self._get_url(query)
- response = requests.get(url, headers=HEADERS)
- result = response.text
-
- link_start = result.find(self.url)
- if link_start == -1:
- return None
-
- link_end = min(
- result.find('\'', link_start + 1),
- result.find('"', link_start + 1)
- )
- return result[link_start:link_end]
-
- @abc.abstractclassmethod
- def _parse_result(self, result):
- pass
-
- @abc.abstractmethod
- def _get_query(self, query):
- pass
-
- @abc.abstractmethod
- def _get_url(self, query):
- pass
-
-
-class GeniusFetcher(InternetLyricsFetcher):
- """
- Define abstract primitive operations that concrete subclasses define
- to implement steps of an algorithm.
- Implement a template method defining the skeleton of an algorithm.
- The template method calls primitive operations as well as operations
- defined in AbstractClass or those of other objects.
- """
-
- def __init__(self, query):
- InternetLyricsFetcher.__init__(
- self, query, 'https://genius.com'
- )
-
- def _parse_result(self, result):
- soup = BeautifulSoup(result, "lxml")
- raw = soup.findAll("div", {"class": "lyrics"})[0]
-
- parsed = raw.findAll("p")[0].text
- # parsed = str.join(u'\n', map(str, parsed))
-
- if len(parsed) < 20: # too little to be lyrcs => not found
- return None
-
- return parsed
-
-
-class GoogleGeniusFetcher(GeniusFetcher):
- def _get_query(self, query):
- return 'site:genius.com ' + query # search just genius
-
- def _get_url(self, query):
- return add_params_to_url("https://www.google.com/search", {
- "q": query
- })
-
-
-class DuckDuckGoGeniusFetcher(GeniusFetcher):
- def _get_query(self, query):
- return 'site:genius.com ' + query # search just metrolyrics
-
- def _get_url(self, query):
- return add_params_to_url("https://duckduckgo.com/html", {
- "q": query
- })
-
-
-class MetrolyricsFetcher(InternetLyricsFetcher):
- """
- Define abstract primitive operations that concrete subclasses define
- to implement steps of an algorithm.
- Implement a template method defining the skeleton of an algorithm.
- The template method calls primitive operations as well as operations
- defined in AbstractClass or those of other objects.
- """
-
- def __init__(self, query):
- InternetLyricsFetcher.__init__(
- self, query, 'http://www.metrolyrics.com'
- )
-
- def _parse_result(self, result):
- soup = BeautifulSoup(result, "lxml")
- raw = (soup.findAll('p', attrs={'class': 'verse'}))
-
- parsed = str.join(u'\n', map(str, raw))
- parsed = parsed.replace('', '\n')
- parsed = parsed.replace('
', ' ')
- parsed = parsed.replace('
', ' ')
- parsed = parsed.strip()
-
- if len(parsed) < 20: # too little to be lyrcs => not found
- return None
-
- return parsed
-
-
-class GoogleMetrolyricsFetcher(MetrolyricsFetcher):
- def _get_query(self, query):
- return 'site:metrolyrics.com ' + query # search just metrolyrics
-
- def _get_url(self, query):
- return add_params_to_url("https://www.google.com/search", {
- "q": query
- })
-
-
-class DuckDuckGoMetrolyricsFetcher(MetrolyricsFetcher):
- def _get_query(self, query):
- return 'site:metrolyrics.com ' + query # search just metrolyrics
-
- def _get_url(self, query):
- return add_params_to_url("https://duckduckgo.com/html", {
- "q": query
- })
+from lyrics.genius import GoogleGeniusFetcher
def get_lyrics(query):
- return GoogleMetrolyricsFetcher(query).get_lyrics()
+ return GoogleGeniusFetcher(query).get_lyrics()
diff --git a/src/lyrics/__init__.py b/src/lyrics/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/lyrics/genius.py b/src/lyrics/genius.py
new file mode 100644
index 0000000..63e8e8a
--- /dev/null
+++ b/src/lyrics/genius.py
@@ -0,0 +1,53 @@
+# !/usr/bin/python3
+# coding: utf-8
+
+from bs4 import BeautifulSoup
+
+from lyrics.models import InternetLyricsFetcher
+
+
+class GeniusFetcher(InternetLyricsFetcher):
+ """
+ Define abstract primitive operations that concrete subclasses define
+ to implement steps of an algorithm.
+ Implement a template method defining the skeleton of an algorithm.
+ The template method calls primitive operations as well as operations
+ defined in AbstractClass or those of other objects.
+ """
+
+ def __init__(self, query):
+ InternetLyricsFetcher.__init__(
+ self, query, 'https://genius.com'
+ )
+
+ def _parse_result(self, result):
+ soup = BeautifulSoup(result, "lxml")
+ raw = soup.findAll("div", {"class": "lyrics"})[0]
+
+ parsed = raw.findAll("p")[0].text
+ # parsed = str.join(u'\n', map(str, parsed))
+
+ if len(parsed) < 20: # too little to be lyrcs => not found
+ return None
+
+ return parsed
+
+
+class GoogleGeniusFetcher(GeniusFetcher):
+ def _get_query(self, query):
+ return 'site:genius.com ' + query # search just genius
+
+ def _get_url(self, query):
+ return self.add_params_to_url("https://www.google.com/search", {
+ "q": query
+ })
+
+
+class DuckDuckGoGeniusFetcher(GeniusFetcher):
+ def _get_query(self, query):
+ return 'site:genius.com ' + query # search just metrolyrics
+
+ def _get_url(self, query):
+ return self.add_params_to_url("https://duckduckgo.com/html", {
+ "q": query
+ })
diff --git a/src/lyrics/metrolyrics.py b/src/lyrics/metrolyrics.py
new file mode 100644
index 0000000..1c310c8
--- /dev/null
+++ b/src/lyrics/metrolyrics.py
@@ -0,0 +1,56 @@
+# !/usr/bin/python3
+# coding: utf-8
+
+from bs4 import BeautifulSoup
+
+from lyrics.models import InternetLyricsFetcher
+
+
+class MetrolyricsFetcher(InternetLyricsFetcher):
+ """
+ Define abstract primitive operations that concrete subclasses define
+ to implement steps of an algorithm.
+ Implement a template method defining the skeleton of an algorithm.
+ The template method calls primitive operations as well as operations
+ defined in AbstractClass or those of other objects.
+ """
+
+ def __init__(self, query):
+ InternetLyricsFetcher.__init__(
+ self, query, 'http://www.metrolyrics.com'
+ )
+
+ def _parse_result(self, result):
+ soup = BeautifulSoup(result, "lxml")
+ raw = (soup.findAll('p', attrs={'class': 'verse'}))
+
+ parsed = str.join(u'\n', map(str, raw))
+ parsed = parsed.replace('', '\n')
+ parsed = parsed.replace('
', ' ')
+ parsed = parsed.replace('
', ' ')
+ parsed = parsed.strip()
+
+ if len(parsed) < 20: # too little to be lyrcs => not found
+ return None
+
+ return parsed
+
+
+class GoogleMetrolyricsFetcher(MetrolyricsFetcher):
+ def _get_query(self, query):
+ return 'site:metrolyrics.com ' + query # search just metrolyrics
+
+ def _get_url(self, query):
+ return self.add_params_to_url("https://www.google.com/search", {
+ "q": query
+ })
+
+
+class DuckDuckGoMetrolyricsFetcher(MetrolyricsFetcher):
+ def _get_query(self, query):
+ return 'site:metrolyrics.com ' + query # search just metrolyrics
+
+ def _get_url(self, query):
+ return self.add_params_to_url("https://duckduckgo.com/html", {
+ "q": query
+ })
diff --git a/src/lyrics/models.py b/src/lyrics/models.py
new file mode 100644
index 0000000..4954bd7
--- /dev/null
+++ b/src/lyrics/models.py
@@ -0,0 +1,93 @@
+# !/usr/bin/python3
+# coding: utf-8
+
+import abc
+import urllib.parse as urlparse
+from urllib.parse import urlencode
+
+import requests
+
+HEADERS = {
+ 'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.0) '
+ 'Gecko/20100101 Firefox/52.0',
+ 'UPGRADE-INSECURE-REQUESTS': '1',
+ 'Connection': 'keep-alive'
+}
+
+
+class LyricsFetcher:
+ """
+ Searches for song lyrics in local folder
+ """
+
+ def __init__(self, query):
+ self.query = query
+
+ @abc.abstractclassmethod
+ def get_lyrics(self):
+ return None
+
+
+class InternetLyricsFetcher(LyricsFetcher):
+ """
+ Searches internet for song lyrics
+ """
+
+ def __init__(self, query, search_engine):
+ LyricsFetcher.__init__(self, query)
+ self.url = search_engine
+
+ def get_lyrics(self):
+ try:
+ link = self.get_lyrics_link()
+ r = requests.get(link, headers=HEADERS)
+ r.encoding = "utf-8"
+ result = r.text
+ return self._parse_result(result)
+ except:
+ return None
+
+ def get_lyrics_link(self):
+ query = self._get_query(self.query)
+ url = self._get_url(query)
+ response = requests.get(url, headers=HEADERS)
+ result = response.text
+
+ link_start = result.find(self.url)
+ if link_start == -1:
+ return None
+
+ link_end = min(
+ result.find('\'', link_start + 1),
+ result.find('"', link_start + 1)
+ )
+ return result[link_start:link_end]
+
+ @abc.abstractclassmethod
+ def _parse_result(self, result):
+ pass
+
+ @abc.abstractmethod
+ def _get_query(self, query):
+ pass
+
+ @abc.abstractmethod
+ def _get_url(self, query):
+ pass
+
+ @staticmethod
+ def add_params_to_url(url, params):
+ """
+ :param url: str
+ Url to add params to
+ :param params: {}
+ List of params to add to url
+ :return: void
+ Adds params to url
+ """
+
+ url_parts = list(urlparse.urlparse(url)) # get url parts
+ query = dict(urlparse.parse_qsl(url_parts[4])) # get url query
+ query.update(params) # add new params
+ url_parts[4] = urlencode(query)
+ return urlparse.urlunparse(url_parts)
From 83d984b87dea3df4a163277122be87bd1b40660f Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Tue, 25 Sep 2018 20:26:07 +0200
Subject: [PATCH 15/25] add local lyrics finder
---
src/lyrics.py | 8 --------
src/lyrics/genius.py | 2 +-
src/lyrics/local.py | 37 +++++++++++++++++++++++++++++++++++++
src/lyrics/metrolyrics.py | 2 +-
src/lyrics/models.py | 4 ----
src/windows.py | 4 ++--
6 files changed, 41 insertions(+), 16 deletions(-)
delete mode 100644 src/lyrics.py
create mode 100644 src/lyrics/local.py
diff --git a/src/lyrics.py b/src/lyrics.py
deleted file mode 100644
index 74e87c4..0000000
--- a/src/lyrics.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# !/usr/bin/python3
-# coding: utf-8
-
-from lyrics.genius import GoogleGeniusFetcher
-
-
-def get_lyrics(query):
- return GoogleGeniusFetcher(query).get_lyrics()
diff --git a/src/lyrics/genius.py b/src/lyrics/genius.py
index 63e8e8a..a99e8a9 100644
--- a/src/lyrics/genius.py
+++ b/src/lyrics/genius.py
@@ -3,7 +3,7 @@
from bs4 import BeautifulSoup
-from lyrics.models import InternetLyricsFetcher
+from .models import InternetLyricsFetcher
class GeniusFetcher(InternetLyricsFetcher):
diff --git a/src/lyrics/local.py b/src/lyrics/local.py
new file mode 100644
index 0000000..fb6848a
--- /dev/null
+++ b/src/lyrics/local.py
@@ -0,0 +1,37 @@
+# !/usr/bin/python3
+# coding: utf-8
+
+import os
+
+from .models import LyricsFetcher
+
+
+class LocalLyricsFetcher(LyricsFetcher):
+ """
+ Searches for song lyrics in local folder
+ """
+
+ def __init__(self, query, folder):
+ LyricsFetcher.__init__(self, query)
+ self.search_folder = folder
+
+ def get_available_lyrics(self):
+ folder_content = os.listdir(self.search_folder)
+ return [
+ os.path.join(self.search_folder, f) # full path
+ for f in folder_content
+ if os.path.isfile(f) # just files
+ ]
+
+ def get_lyrics(self):
+ lyrics_file = self.get_query_file()
+ if lyrics_file in self.get_available_lyrics():
+ with open(lyrics_file, "r") as reader:
+ lines = reader.readlines()
+ return "\n".join(lines) # content
+
+ return None
+
+ def get_query_file(self):
+ file_name = self.query.replace(" ", "_")
+ return os.path.join(self.search_folder, file_name)
diff --git a/src/lyrics/metrolyrics.py b/src/lyrics/metrolyrics.py
index 1c310c8..147aa0f 100644
--- a/src/lyrics/metrolyrics.py
+++ b/src/lyrics/metrolyrics.py
@@ -3,7 +3,7 @@
from bs4 import BeautifulSoup
-from lyrics.models import InternetLyricsFetcher
+from .models import InternetLyricsFetcher
class MetrolyricsFetcher(InternetLyricsFetcher):
diff --git a/src/lyrics/models.py b/src/lyrics/models.py
index 4954bd7..70eabd8 100644
--- a/src/lyrics/models.py
+++ b/src/lyrics/models.py
@@ -16,10 +16,6 @@
class LyricsFetcher:
- """
- Searches for song lyrics in local folder
- """
-
def __init__(self, query):
self.query = query
diff --git a/src/windows.py b/src/windows.py
index 80dcc4b..bac6d4f 100644
--- a/src/windows.py
+++ b/src/windows.py
@@ -7,12 +7,12 @@
import dbus
import gi
+from lyrics.genius import GoogleGeniusFetcher
from utils import get_general_error
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
-from src.lyrics import get_lyrics
from src.settings import CONFIG_PATH
from . import utils
@@ -96,7 +96,7 @@ def put_lyrics(self, query):
self.spinner.start()
self.lyrics.set_text("")
- self.current_lyrics = get_lyrics(query)
+ self.current_lyrics = GoogleGeniusFetcher(query).get_lyrics()
if self.current_lyrics is None:
self.lyrics.set_text("Lyrics not found")
From 522b81f8f0b1429f23e743a6693cb45fc6707339 Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Wed, 26 Sep 2018 09:29:19 +0200
Subject: [PATCH 16/25] add local + internet lyrics finders
---
src/lyrics/genius.py | 10 ++++++++--
src/lyrics/local.py | 20 ++++++++++++--------
src/lyrics/metrolyrics.py | 10 ++++++++--
src/lyrics/models.py | 14 ++++++++------
src/lyrics/utils.py | 36 ++++++++++++++++++++++++++++++++++++
src/windows.py | 6 +++---
6 files changed, 75 insertions(+), 21 deletions(-)
create mode 100644 src/lyrics/utils.py
diff --git a/src/lyrics/genius.py b/src/lyrics/genius.py
index a99e8a9..b263608 100644
--- a/src/lyrics/genius.py
+++ b/src/lyrics/genius.py
@@ -15,9 +15,9 @@ class GeniusFetcher(InternetLyricsFetcher):
defined in AbstractClass or those of other objects.
"""
- def __init__(self, query):
+ def __init__(self):
InternetLyricsFetcher.__init__(
- self, query, 'https://genius.com'
+ self, 'https://genius.com'
)
def _parse_result(self, result):
@@ -51,3 +51,9 @@ def _get_url(self, query):
return self.add_params_to_url("https://duckduckgo.com/html", {
"q": query
})
+
+
+LYRICS_FINDERS = [
+ GoogleGeniusFetcher(),
+ DuckDuckGoGeniusFetcher()
+]
diff --git a/src/lyrics/local.py b/src/lyrics/local.py
index fb6848a..4ee9d2a 100644
--- a/src/lyrics/local.py
+++ b/src/lyrics/local.py
@@ -6,13 +6,19 @@
from .models import LyricsFetcher
+def get_local_lyrics_filename(query, folder):
+ file_name = query.replace(" ", "_")
+ file_name += ".txt"
+ return os.path.join(folder, file_name)
+
+
class LocalLyricsFetcher(LyricsFetcher):
"""
Searches for song lyrics in local folder
"""
- def __init__(self, query, folder):
- LyricsFetcher.__init__(self, query)
+ def __init__(self, folder):
+ LyricsFetcher.__init__(self)
self.search_folder = folder
def get_available_lyrics(self):
@@ -23,15 +29,13 @@ def get_available_lyrics(self):
if os.path.isfile(f) # just files
]
- def get_lyrics(self):
- lyrics_file = self.get_query_file()
+ def get_lyrics(self, query):
+ lyrics_file = get_local_lyrics_filename(self.query, self.search_folder)
if lyrics_file in self.get_available_lyrics():
+ print("Fetching lyrics from", lyrics_file)
+
with open(lyrics_file, "r") as reader:
lines = reader.readlines()
return "\n".join(lines) # content
return None
-
- def get_query_file(self):
- file_name = self.query.replace(" ", "_")
- return os.path.join(self.search_folder, file_name)
diff --git a/src/lyrics/metrolyrics.py b/src/lyrics/metrolyrics.py
index 147aa0f..19283c5 100644
--- a/src/lyrics/metrolyrics.py
+++ b/src/lyrics/metrolyrics.py
@@ -15,9 +15,9 @@ class MetrolyricsFetcher(InternetLyricsFetcher):
defined in AbstractClass or those of other objects.
"""
- def __init__(self, query):
+ def __init__(self):
InternetLyricsFetcher.__init__(
- self, query, 'http://www.metrolyrics.com'
+ self, 'http://www.metrolyrics.com'
)
def _parse_result(self, result):
@@ -54,3 +54,9 @@ def _get_url(self, query):
return self.add_params_to_url("https://duckduckgo.com/html", {
"q": query
})
+
+
+LYRICS_FINDERS = [
+ GoogleMetrolyricsFetcher(),
+ DuckDuckGoMetrolyricsFetcher()
+]
diff --git a/src/lyrics/models.py b/src/lyrics/models.py
index 70eabd8..e6d0c7a 100644
--- a/src/lyrics/models.py
+++ b/src/lyrics/models.py
@@ -16,11 +16,11 @@
class LyricsFetcher:
- def __init__(self, query):
- self.query = query
+ def __init__(self):
+ self.query = None
@abc.abstractclassmethod
- def get_lyrics(self):
+ def get_lyrics(self, query):
return None
@@ -29,11 +29,13 @@ class InternetLyricsFetcher(LyricsFetcher):
Searches internet for song lyrics
"""
- def __init__(self, query, search_engine):
- LyricsFetcher.__init__(self, query)
+ def __init__(self, search_engine):
+ LyricsFetcher.__init__(self)
self.url = search_engine
- def get_lyrics(self):
+ def get_lyrics(self, query):
+ self.query = query
+
try:
link = self.get_lyrics_link()
r = requests.get(link, headers=HEADERS)
diff --git a/src/lyrics/utils.py b/src/lyrics/utils.py
new file mode 100644
index 0000000..f2e6829
--- /dev/null
+++ b/src/lyrics/utils.py
@@ -0,0 +1,36 @@
+# !/usr/bin/python3
+# coding: utf-8
+
+import os
+
+from lyrics.genius import LYRICS_FINDERS as GENIUS_FINDERS
+from lyrics.metrolyrics import LYRICS_FINDERS as METROLYRICS_FINDERS
+from .local import LocalLyricsFetcher, get_local_lyrics_filename
+
+LOCAL_LYRICS_FOLDER = os.path.join(
+ os.getenv("HOME"),
+ ".local", "Instant-Lyrics", "lyrics"
+)
+
+if not os.path.exists(LOCAL_LYRICS_FOLDER):
+ os.makedirs(LOCAL_LYRICS_FOLDER) # make sure there is a local folder
+
+
+def save_lyrics(query, lyrics):
+ file_name = get_local_lyrics_filename(query, LOCAL_LYRICS_FOLDER)
+ with open(file_name, "w") as writer:
+ writer.write(lyrics)
+
+
+def get_lyrics(query):
+ finders = [
+ LocalLyricsFetcher(LOCAL_LYRICS_FOLDER) # local
+ ] + GENIUS_FINDERS + METROLYRICS_FINDERS # internet
+
+ for finder in finders:
+ result = finder.get_lyrics(query)
+ if result is not None:
+ save_lyrics(query, result) # save to local
+ return result
+
+ return None
diff --git a/src/windows.py b/src/windows.py
index bac6d4f..ebadc0c 100644
--- a/src/windows.py
+++ b/src/windows.py
@@ -7,13 +7,13 @@
import dbus
import gi
-from lyrics.genius import GoogleGeniusFetcher
-from utils import get_general_error
+from lyrics.utils import get_lyrics
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
from src.settings import CONFIG_PATH
+from .utils import get_general_error
from . import utils
@@ -96,7 +96,7 @@ def put_lyrics(self, query):
self.spinner.start()
self.lyrics.set_text("")
- self.current_lyrics = GoogleGeniusFetcher(query).get_lyrics()
+ self.current_lyrics = get_lyrics(query)
if self.current_lyrics is None:
self.lyrics.set_text("Lyrics not found")
From da8e13dcd85fd5cf4b76cab1897ccf332b78e03e Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Wed, 26 Sep 2018 09:54:32 +0200
Subject: [PATCH 17/25] fix local finder, fix #11
---
src/lyrics/genius.py | 4 ++++
src/lyrics/local.py | 13 +++++++++----
src/lyrics/metrolyrics.py | 4 ++++
src/lyrics/models.py | 11 +++--------
src/lyrics/utils.py | 3 +++
src/utils.py | 8 +-------
src/windows.py | 10 +++++-----
7 files changed, 29 insertions(+), 24 deletions(-)
diff --git a/src/lyrics/genius.py b/src/lyrics/genius.py
index b263608..c54b189 100644
--- a/src/lyrics/genius.py
+++ b/src/lyrics/genius.py
@@ -35,6 +35,8 @@ def _parse_result(self, result):
class GoogleGeniusFetcher(GeniusFetcher):
def _get_query(self, query):
+ print("Searching Google for Genius lyrics")
+
return 'site:genius.com ' + query # search just genius
def _get_url(self, query):
@@ -45,6 +47,8 @@ def _get_url(self, query):
class DuckDuckGoGeniusFetcher(GeniusFetcher):
def _get_query(self, query):
+ print("Searching DuckDuckGo for Genius lyrics")
+
return 'site:genius.com ' + query # search just metrolyrics
def _get_url(self, query):
diff --git a/src/lyrics/local.py b/src/lyrics/local.py
index 4ee9d2a..bc2a9f1 100644
--- a/src/lyrics/local.py
+++ b/src/lyrics/local.py
@@ -23,17 +23,22 @@ def __init__(self, folder):
def get_available_lyrics(self):
folder_content = os.listdir(self.search_folder)
- return [
+ folder_content = [
os.path.join(self.search_folder, f) # full path
for f in folder_content
+ ]
+ return [
+ f
+ for f in folder_content
if os.path.isfile(f) # just files
]
def get_lyrics(self, query):
- lyrics_file = get_local_lyrics_filename(self.query, self.search_folder)
- if lyrics_file in self.get_available_lyrics():
- print("Fetching lyrics from", lyrics_file)
+ print("Searching local folder for lyrics")
+ available_lyrics = self.get_available_lyrics()
+ lyrics_file = get_local_lyrics_filename(query, self.search_folder)
+ if lyrics_file in available_lyrics:
with open(lyrics_file, "r") as reader:
lines = reader.readlines()
return "\n".join(lines) # content
diff --git a/src/lyrics/metrolyrics.py b/src/lyrics/metrolyrics.py
index 19283c5..84f691a 100644
--- a/src/lyrics/metrolyrics.py
+++ b/src/lyrics/metrolyrics.py
@@ -38,6 +38,8 @@ def _parse_result(self, result):
class GoogleMetrolyricsFetcher(MetrolyricsFetcher):
def _get_query(self, query):
+ print("Searching Google for Metrolyrics lyrics")
+
return 'site:metrolyrics.com ' + query # search just metrolyrics
def _get_url(self, query):
@@ -48,6 +50,8 @@ def _get_url(self, query):
class DuckDuckGoMetrolyricsFetcher(MetrolyricsFetcher):
def _get_query(self, query):
+ print("Searching DuckDuckGo for Metrolyrics lyrics")
+
return 'site:metrolyrics.com ' + query # search just metrolyrics
def _get_url(self, query):
diff --git a/src/lyrics/models.py b/src/lyrics/models.py
index e6d0c7a..5cc10b1 100644
--- a/src/lyrics/models.py
+++ b/src/lyrics/models.py
@@ -16,9 +16,6 @@
class LyricsFetcher:
- def __init__(self):
- self.query = None
-
@abc.abstractclassmethod
def get_lyrics(self, query):
return None
@@ -34,10 +31,8 @@ def __init__(self, search_engine):
self.url = search_engine
def get_lyrics(self, query):
- self.query = query
-
try:
- link = self.get_lyrics_link()
+ link = self.get_lyrics_link(query)
r = requests.get(link, headers=HEADERS)
r.encoding = "utf-8"
result = r.text
@@ -45,8 +40,8 @@ def get_lyrics(self, query):
except:
return None
- def get_lyrics_link(self):
- query = self._get_query(self.query)
+ def get_lyrics_link(self, query):
+ query = self._get_query(query)
url = self._get_url(query)
response = requests.get(url, headers=HEADERS)
result = response.text
diff --git a/src/lyrics/utils.py b/src/lyrics/utils.py
index f2e6829..9a51656 100644
--- a/src/lyrics/utils.py
+++ b/src/lyrics/utils.py
@@ -18,6 +18,9 @@
def save_lyrics(query, lyrics):
file_name = get_local_lyrics_filename(query, LOCAL_LYRICS_FOLDER)
+ while "\n\n" in lyrics: # remove double line endings
+ lyrics = lyrics.replace("\n\n", "\n")
+
with open(file_name, "w") as writer:
writer.write(lyrics)
diff --git a/src/utils.py b/src/utils.py
index 60f00d5..75ac779 100644
--- a/src/utils.py
+++ b/src/utils.py
@@ -9,6 +9,7 @@
BUG_ISSUES_REPORT_ERROR = "If that's not the case, report an issue here "
+ERROR = "Error"
def create_default_config():
@@ -45,13 +46,6 @@ def get_default_icon_path():
return get_icon_path('../icons/instant-lyrics-32.png')
-def get_general_error(app):
- out = "Could not get current " + app + " song"
- out += "\nno song is playing on " + app
- out += "\n\n" + BUG_ISSUES_REPORT_ERROR
- return out
-
-
def create_desktop_entry():
src_path = os.path.dirname(os.path.realpath(__file__)) # path of scr folder
base_path = os.path.dirname(src_path) # path of base folder
diff --git a/src/windows.py b/src/windows.py
index ebadc0c..ecf6347 100644
--- a/src/windows.py
+++ b/src/windows.py
@@ -8,12 +8,12 @@
import gi
from lyrics.utils import get_lyrics
+from utils import ERROR
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
from src.settings import CONFIG_PATH
-from .utils import get_general_error
from . import utils
@@ -151,13 +151,13 @@ def get_lyrics(self, app):
new_song = (previous_song != self.current_song) or (
previous_artist != self.current_artist)
- if new_song: # no new song => return
+ if new_song:
+ print("New song detected:")
+ print("\t", self.current_song + " " + self.current_artist)
self.set_current_song_title()
self.set_current_song_lyrics()
except:
- self.title.set_markup("Error")
- message = get_general_error(app)
- self.lyrics.set_markup(message)
+ self.title.set_markup(ERROR)
time.sleep(5)
From b1a10f9a37e77acf073980d5011cf39ed66c8e93 Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Wed, 26 Sep 2018 10:48:41 +0200
Subject: [PATCH 18/25] add install script
---
InstantLyrics.py | 9 +++++++--
setup.py | 40 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 47 insertions(+), 2 deletions(-)
create mode 100755 setup.py
diff --git a/InstantLyrics.py b/InstantLyrics.py
index 850dd7e..31268b6 100755
--- a/InstantLyrics.py
+++ b/InstantLyrics.py
@@ -1,7 +1,12 @@
# !/usr/bin/python3
# coding: utf-8
-from src.appIndicator import AppIndicator
+from .src.appIndicator import AppIndicator
-if __name__ == "__main__":
+
+def main():
app = AppIndicator()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..caebcb1
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,40 @@
+# !/usr/bin/python3
+# coding: utf_8
+
+""" Setups library and install dependencies """
+
+from setuptools import setup, find_packages
+
+DESCRIPTION = \
+ "Instant-Lyrics\n\n\
+ Instantly fetches the lyrics of the currently playing song and displays it on a window.\n\
+ \n\
+ Install\n\n\
+ - $ pip3 install . --upgrade --force-reinstall, from the source\n\
+ \n\
+ Questions and issues\n\n\
+ The Github issue tracker is only for bug reports and feature requests."
+
+setup(
+ name="Instant-Lyrics",
+ version="2.0",
+ author="sirfoga",
+ author_email='captain.bhrigu@gmail.com,nicolas.guichard@ensimag.fr,'
+ 'sirfoga@protonmail.com',
+ description="Instantly fetches the lyrics of the currently playing song "
+ "and displays it on a window.",
+ long_description=DESCRIPTION,
+ keywords="linux-app gtk3 pygobject lyrics spotify rhythmbox",
+ url="https://github.com/sirfoga/Instant-Lyrics",
+ packages=find_packages(exclude=["tests"]),
+ install_requires=[
+ "requests",
+ "beautifulsoup4",
+ "lxml"
+ ],
+ entry_points={
+ "console_scripts": [
+ "instantlyrics = InstantLyrics:main"
+ ]
+ }
+)
From 1804f12541c1b9bfb1f6fa610b4532a2912ac880 Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Wed, 26 Sep 2018 11:13:58 +0200
Subject: [PATCH 19/25] add lyricstranslate.com lyrics search engine
---
{src => InstantLyrics}/__init__.py | 0
{src/lyrics => InstantLyrics/app}/__init__.py | 0
{src => InstantLyrics/app}/appIndicator.py | 12 ++--
{src => InstantLyrics/app}/settings.py | 0
{src => InstantLyrics/app}/windows.py | 10 ++-
InstantLyrics.py => InstantLyrics/cmd.py | 4 +-
InstantLyrics/lyrics/__init__.py | 0
{src => InstantLyrics}/lyrics/genius.py | 8 +--
{src => InstantLyrics}/lyrics/local.py | 2 +-
InstantLyrics/lyrics/lyricstranslate.py | 63 +++++++++++++++++++
{src => InstantLyrics}/lyrics/metrolyrics.py | 6 +-
{src => InstantLyrics}/lyrics/models.py | 0
{src => InstantLyrics}/lyrics/utils.py | 2 +-
{src => InstantLyrics}/utils.py | 4 +-
setup.py | 4 +-
15 files changed, 88 insertions(+), 27 deletions(-)
rename {src => InstantLyrics}/__init__.py (100%)
rename {src/lyrics => InstantLyrics/app}/__init__.py (100%)
rename {src => InstantLyrics/app}/appIndicator.py (92%)
rename {src => InstantLyrics/app}/settings.py (100%)
rename {src => InstantLyrics/app}/windows.py (98%)
rename InstantLyrics.py => InstantLyrics/cmd.py (57%)
create mode 100644 InstantLyrics/lyrics/__init__.py
rename {src => InstantLyrics}/lyrics/genius.py (87%)
rename {src => InstantLyrics}/lyrics/local.py (96%)
create mode 100644 InstantLyrics/lyrics/lyricstranslate.py
rename {src => InstantLyrics}/lyrics/metrolyrics.py (90%)
rename {src => InstantLyrics}/lyrics/models.py (100%)
rename {src => InstantLyrics}/lyrics/utils.py (93%)
rename {src => InstantLyrics}/utils.py (95%)
diff --git a/src/__init__.py b/InstantLyrics/__init__.py
similarity index 100%
rename from src/__init__.py
rename to InstantLyrics/__init__.py
diff --git a/src/lyrics/__init__.py b/InstantLyrics/app/__init__.py
similarity index 100%
rename from src/lyrics/__init__.py
rename to InstantLyrics/app/__init__.py
diff --git a/src/appIndicator.py b/InstantLyrics/app/appIndicator.py
similarity index 92%
rename from src/appIndicator.py
rename to InstantLyrics/app/appIndicator.py
index 2b2d1ce..d96519b 100644
--- a/src/appIndicator.py
+++ b/InstantLyrics/app/appIndicator.py
@@ -8,11 +8,6 @@
import gi
from dbus.mainloop.glib import DBusGMainLoop
-from src.settings import APPINDICATOR_ID
-from src.windows import LyricsWindow, PreferenceWindow
-from utils import get_default_icon_path
-from . import utils
-
gi.require_version('Gtk', '3.0')
gi.require_version('AppIndicator3', '0.1')
@@ -20,8 +15,13 @@
from gi.repository import Gtk
import os
+from app.settings import APPINDICATOR_ID
+from app.windows import LyricsWindow, PreferenceWindow
+from InstantLyrics import utils
+
+
DBusGMainLoop(set_as_default=True)
-ICON_PATH = get_default_icon_path() # full path
+ICON_PATH = utils.get_default_icon_path() # full path
LOCAL_LYRICS_PATH = os.path.join(
os.getenv("HOME"),
".local", "Instant-Lyrics", "lyrics"
diff --git a/src/settings.py b/InstantLyrics/app/settings.py
similarity index 100%
rename from src/settings.py
rename to InstantLyrics/app/settings.py
diff --git a/src/windows.py b/InstantLyrics/app/windows.py
similarity index 98%
rename from src/windows.py
rename to InstantLyrics/app/windows.py
index ecf6347..3b2f859 100644
--- a/src/windows.py
+++ b/InstantLyrics/app/windows.py
@@ -7,14 +7,12 @@
import dbus
import gi
-from lyrics.utils import get_lyrics
-from utils import ERROR
-
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
-from src.settings import CONFIG_PATH
-from . import utils
+from app.settings import CONFIG_PATH
+from InstantLyrics.lyrics.utils import get_lyrics
+from InstantLyrics import utils
class LyricsWindow(Gtk.Window):
@@ -157,7 +155,7 @@ def get_lyrics(self, app):
self.set_current_song_title()
self.set_current_song_lyrics()
except:
- self.title.set_markup(ERROR)
+ self.title.set_markup(utils.ERROR)
time.sleep(5)
diff --git a/InstantLyrics.py b/InstantLyrics/cmd.py
similarity index 57%
rename from InstantLyrics.py
rename to InstantLyrics/cmd.py
index 31268b6..277199e 100755
--- a/InstantLyrics.py
+++ b/InstantLyrics/cmd.py
@@ -1,11 +1,11 @@
# !/usr/bin/python3
# coding: utf-8
-from .src.appIndicator import AppIndicator
+from app.appIndicator import AppIndicator
def main():
- app = AppIndicator()
+ AppIndicator()
if __name__ == "__main__":
diff --git a/InstantLyrics/lyrics/__init__.py b/InstantLyrics/lyrics/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/lyrics/genius.py b/InstantLyrics/lyrics/genius.py
similarity index 87%
rename from src/lyrics/genius.py
rename to InstantLyrics/lyrics/genius.py
index c54b189..e4daf83 100644
--- a/src/lyrics/genius.py
+++ b/InstantLyrics/lyrics/genius.py
@@ -3,7 +3,7 @@
from bs4 import BeautifulSoup
-from .models import InternetLyricsFetcher
+from lyrics.models import InternetLyricsFetcher
class GeniusFetcher(InternetLyricsFetcher):
@@ -25,7 +25,7 @@ def _parse_result(self, result):
raw = soup.findAll("div", {"class": "lyrics"})[0]
parsed = raw.findAll("p")[0].text
- # parsed = str.join(u'\n', map(str, parsed))
+ parsed = str.join(u'\n', map(str, parsed))
if len(parsed) < 20: # too little to be lyrcs => not found
return None
@@ -35,7 +35,7 @@ def _parse_result(self, result):
class GoogleGeniusFetcher(GeniusFetcher):
def _get_query(self, query):
- print("Searching Google for Genius lyrics")
+ print("Searching google.com for genius.com lyrics")
return 'site:genius.com ' + query # search just genius
@@ -47,7 +47,7 @@ def _get_url(self, query):
class DuckDuckGoGeniusFetcher(GeniusFetcher):
def _get_query(self, query):
- print("Searching DuckDuckGo for Genius lyrics")
+ print("Searching duckduckgo.com for genius.com lyrics")
return 'site:genius.com ' + query # search just metrolyrics
diff --git a/src/lyrics/local.py b/InstantLyrics/lyrics/local.py
similarity index 96%
rename from src/lyrics/local.py
rename to InstantLyrics/lyrics/local.py
index bc2a9f1..740746c 100644
--- a/src/lyrics/local.py
+++ b/InstantLyrics/lyrics/local.py
@@ -3,7 +3,7 @@
import os
-from .models import LyricsFetcher
+from lyrics.models import LyricsFetcher
def get_local_lyrics_filename(query, folder):
diff --git a/InstantLyrics/lyrics/lyricstranslate.py b/InstantLyrics/lyrics/lyricstranslate.py
new file mode 100644
index 0000000..658cbe6
--- /dev/null
+++ b/InstantLyrics/lyrics/lyricstranslate.py
@@ -0,0 +1,63 @@
+# !/usr/bin/python3
+# coding: utf-8
+
+from bs4 import BeautifulSoup
+
+from lyrics.models import InternetLyricsFetcher
+
+
+class LyricsTranslateFetcher(InternetLyricsFetcher):
+ """
+ Define abstract primitive operations that concrete subclasses define
+ to implement steps of an algorithm.
+ Implement a template method defining the skeleton of an algorithm.
+ The template method calls primitive operations as well as operations
+ defined in AbstractClass or those of other objects.
+ """
+
+ def __init__(self):
+ InternetLyricsFetcher.__init__(
+ self, 'https://lyricstranslate.com/en/'
+ )
+
+ def _parse_result(self, result):
+ soup = BeautifulSoup(result, "lxml")
+ raw = soup.findAll("div", {"class": "ltf"})[0]
+
+ parsed = raw.findAll("p")[0].text
+ parsed = str.join(u'\n', map(str, parsed))
+
+ if len(parsed) < 20: # too little to be lyrics => not found
+ return None
+
+ return parsed
+
+
+class GoogleLyricstranslateFetcher(LyricsTranslateFetcher):
+ def _get_query(self, query):
+ print("Searching google.com for lyricstranslate.com lyrics")
+
+ return 'site:lyricstranslate.com ' + query # search just genius
+
+ def _get_url(self, query):
+ return self.add_params_to_url("https://www.google.com/search", {
+ "q": query
+ })
+
+
+class DuckDuckGoLyricstranslateFetcher(LyricsTranslateFetcher):
+ def _get_query(self, query):
+ print("Searching duckduckgo.com for lyricstranslate.com lyrics")
+
+ return 'site:lyricstranslate.com ' + query # search just metrolyrics
+
+ def _get_url(self, query):
+ return self.add_params_to_url("https://duckduckgo.com/html", {
+ "q": query
+ })
+
+
+LYRICS_FINDERS = [
+ GoogleLyricstranslateFetcher(),
+ DuckDuckGoLyricstranslateFetcher()
+]
diff --git a/src/lyrics/metrolyrics.py b/InstantLyrics/lyrics/metrolyrics.py
similarity index 90%
rename from src/lyrics/metrolyrics.py
rename to InstantLyrics/lyrics/metrolyrics.py
index 84f691a..9c43e9d 100644
--- a/src/lyrics/metrolyrics.py
+++ b/InstantLyrics/lyrics/metrolyrics.py
@@ -3,7 +3,7 @@
from bs4 import BeautifulSoup
-from .models import InternetLyricsFetcher
+from lyrics.models import InternetLyricsFetcher
class MetrolyricsFetcher(InternetLyricsFetcher):
@@ -38,7 +38,7 @@ def _parse_result(self, result):
class GoogleMetrolyricsFetcher(MetrolyricsFetcher):
def _get_query(self, query):
- print("Searching Google for Metrolyrics lyrics")
+ print("Searching google.com for metrolyrics.com lyrics")
return 'site:metrolyrics.com ' + query # search just metrolyrics
@@ -50,7 +50,7 @@ def _get_url(self, query):
class DuckDuckGoMetrolyricsFetcher(MetrolyricsFetcher):
def _get_query(self, query):
- print("Searching DuckDuckGo for Metrolyrics lyrics")
+ print("Searching duckduckgo.com for metrolyrics.com lyrics")
return 'site:metrolyrics.com ' + query # search just metrolyrics
diff --git a/src/lyrics/models.py b/InstantLyrics/lyrics/models.py
similarity index 100%
rename from src/lyrics/models.py
rename to InstantLyrics/lyrics/models.py
diff --git a/src/lyrics/utils.py b/InstantLyrics/lyrics/utils.py
similarity index 93%
rename from src/lyrics/utils.py
rename to InstantLyrics/lyrics/utils.py
index 9a51656..c071771 100644
--- a/src/lyrics/utils.py
+++ b/InstantLyrics/lyrics/utils.py
@@ -4,8 +4,8 @@
import os
from lyrics.genius import LYRICS_FINDERS as GENIUS_FINDERS
+from lyrics.local import LocalLyricsFetcher, get_local_lyrics_filename
from lyrics.metrolyrics import LYRICS_FINDERS as METROLYRICS_FINDERS
-from .local import LocalLyricsFetcher, get_local_lyrics_filename
LOCAL_LYRICS_FOLDER = os.path.join(
os.getenv("HOME"),
diff --git a/src/utils.py b/InstantLyrics/utils.py
similarity index 95%
rename from src/utils.py
rename to InstantLyrics/utils.py
index 75ac779..154dd0b 100644
--- a/src/utils.py
+++ b/InstantLyrics/utils.py
@@ -4,7 +4,7 @@
import configparser
import os
-from src.settings import CONFIG_PATH
+from app.settings import CONFIG_PATH
BUG_ISSUES_REPORT_ERROR = "If that's not the case, report an issue
Date: Wed, 26 Sep 2018 11:22:23 +0200
Subject: [PATCH 20/25] fix local reader duplicate lines
---
InstantLyrics/lyrics/genius.py | 6 ------
InstantLyrics/lyrics/local.py | 3 +--
InstantLyrics/lyrics/lyricstranslate.py | 10 ++--------
InstantLyrics/lyrics/metrolyrics.py | 7 +------
InstantLyrics/lyrics/models.py | 7 ++++++-
InstantLyrics/lyrics/utils.py | 3 ++-
6 files changed, 12 insertions(+), 24 deletions(-)
diff --git a/InstantLyrics/lyrics/genius.py b/InstantLyrics/lyrics/genius.py
index e4daf83..3530a99 100644
--- a/InstantLyrics/lyrics/genius.py
+++ b/InstantLyrics/lyrics/genius.py
@@ -23,13 +23,7 @@ def __init__(self):
def _parse_result(self, result):
soup = BeautifulSoup(result, "lxml")
raw = soup.findAll("div", {"class": "lyrics"})[0]
-
parsed = raw.findAll("p")[0].text
- parsed = str.join(u'\n', map(str, parsed))
-
- if len(parsed) < 20: # too little to be lyrcs => not found
- return None
-
return parsed
diff --git a/InstantLyrics/lyrics/local.py b/InstantLyrics/lyrics/local.py
index 740746c..f6db60f 100644
--- a/InstantLyrics/lyrics/local.py
+++ b/InstantLyrics/lyrics/local.py
@@ -40,7 +40,6 @@ def get_lyrics(self, query):
lyrics_file = get_local_lyrics_filename(query, self.search_folder)
if lyrics_file in available_lyrics:
with open(lyrics_file, "r") as reader:
- lines = reader.readlines()
- return "\n".join(lines) # content
+ return "".join(reader.readlines()) # content
return None
diff --git a/InstantLyrics/lyrics/lyricstranslate.py b/InstantLyrics/lyrics/lyricstranslate.py
index 658cbe6..92e93b7 100644
--- a/InstantLyrics/lyrics/lyricstranslate.py
+++ b/InstantLyrics/lyrics/lyricstranslate.py
@@ -17,19 +17,13 @@ class LyricsTranslateFetcher(InternetLyricsFetcher):
def __init__(self):
InternetLyricsFetcher.__init__(
- self, 'https://lyricstranslate.com/en/'
+ self, 'https://lyricstranslate.com/'
)
def _parse_result(self, result):
soup = BeautifulSoup(result, "lxml")
raw = soup.findAll("div", {"class": "ltf"})[0]
-
- parsed = raw.findAll("p")[0].text
- parsed = str.join(u'\n', map(str, parsed))
-
- if len(parsed) < 20: # too little to be lyrics => not found
- return None
-
+ parsed = raw.text
return parsed
diff --git a/InstantLyrics/lyrics/metrolyrics.py b/InstantLyrics/lyrics/metrolyrics.py
index 9c43e9d..7243b8d 100644
--- a/InstantLyrics/lyrics/metrolyrics.py
+++ b/InstantLyrics/lyrics/metrolyrics.py
@@ -23,16 +23,11 @@ def __init__(self):
def _parse_result(self, result):
soup = BeautifulSoup(result, "lxml")
raw = (soup.findAll('p', attrs={'class': 'verse'}))
-
- parsed = str.join(u'\n', map(str, raw))
- parsed = parsed.replace('', '\n')
+ parsed = raw.replace('
', '\n')
parsed = parsed.replace('
', ' ')
parsed = parsed.replace('
', ' ')
parsed = parsed.strip()
- if len(parsed) < 20: # too little to be lyrcs => not found
- return None
-
return parsed
diff --git a/InstantLyrics/lyrics/models.py b/InstantLyrics/lyrics/models.py
index 5cc10b1..c0928a6 100644
--- a/InstantLyrics/lyrics/models.py
+++ b/InstantLyrics/lyrics/models.py
@@ -36,7 +36,12 @@ def get_lyrics(self, query):
r = requests.get(link, headers=HEADERS)
r.encoding = "utf-8"
result = r.text
- return self._parse_result(result)
+ parsed = self._parse_result(result)
+
+ if len(parsed) < 20: # too little to be lyrics
+ return None
+
+ return parsed
except:
return None
diff --git a/InstantLyrics/lyrics/utils.py b/InstantLyrics/lyrics/utils.py
index c071771..4debaa8 100644
--- a/InstantLyrics/lyrics/utils.py
+++ b/InstantLyrics/lyrics/utils.py
@@ -5,6 +5,7 @@
from lyrics.genius import LYRICS_FINDERS as GENIUS_FINDERS
from lyrics.local import LocalLyricsFetcher, get_local_lyrics_filename
+from lyrics.lyricstranslate import LYRICS_FINDERS as LYRICSTRANSLATE_FINDERS
from lyrics.metrolyrics import LYRICS_FINDERS as METROLYRICS_FINDERS
LOCAL_LYRICS_FOLDER = os.path.join(
@@ -28,7 +29,7 @@ def save_lyrics(query, lyrics):
def get_lyrics(query):
finders = [
LocalLyricsFetcher(LOCAL_LYRICS_FOLDER) # local
- ] + GENIUS_FINDERS + METROLYRICS_FINDERS # internet
+ ] + GENIUS_FINDERS + METROLYRICS_FINDERS + LYRICSTRANSLATE_FINDERS
for finder in finders:
result = finder.get_lyrics(query)
From 3040b7eb88de067e656c935e421f76a8bcce5024 Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Wed, 26 Sep 2018 11:39:28 +0200
Subject: [PATCH 21/25] fix images setup
---
InstantLyrics/app/appIndicator.py | 4 ++--
InstantLyrics/app/windows.py | 2 +-
InstantLyrics/cmd.py | 2 +-
.../icons}/instant-lyrics-24.png | Bin
.../icons}/instant-lyrics-256.png | Bin
.../icons}/instant-lyrics-32.png | Bin
.../icons}/instant-lyrics-512.png | Bin
{icons => InstantLyrics/icons}/instant-lyrics.svg | 0
InstantLyrics/lyrics/genius.py | 2 +-
InstantLyrics/lyrics/local.py | 2 +-
InstantLyrics/lyrics/lyricstranslate.py | 2 +-
InstantLyrics/lyrics/metrolyrics.py | 2 +-
InstantLyrics/lyrics/utils.py | 11 +++++++----
InstantLyrics/utils.py | 4 ++--
setup.py | 12 +++++++++++-
15 files changed, 28 insertions(+), 15 deletions(-)
rename {icons => InstantLyrics/icons}/instant-lyrics-24.png (100%)
rename {icons => InstantLyrics/icons}/instant-lyrics-256.png (100%)
rename {icons => InstantLyrics/icons}/instant-lyrics-32.png (100%)
rename {icons => InstantLyrics/icons}/instant-lyrics-512.png (100%)
rename {icons => InstantLyrics/icons}/instant-lyrics.svg (100%)
diff --git a/InstantLyrics/app/appIndicator.py b/InstantLyrics/app/appIndicator.py
index d96519b..8901083 100644
--- a/InstantLyrics/app/appIndicator.py
+++ b/InstantLyrics/app/appIndicator.py
@@ -15,8 +15,8 @@
from gi.repository import Gtk
import os
-from app.settings import APPINDICATOR_ID
-from app.windows import LyricsWindow, PreferenceWindow
+from InstantLyrics.app.settings import APPINDICATOR_ID
+from InstantLyrics.app.windows import LyricsWindow, PreferenceWindow
from InstantLyrics import utils
diff --git a/InstantLyrics/app/windows.py b/InstantLyrics/app/windows.py
index 3b2f859..a3241fa 100644
--- a/InstantLyrics/app/windows.py
+++ b/InstantLyrics/app/windows.py
@@ -10,7 +10,7 @@
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk
-from app.settings import CONFIG_PATH
+from InstantLyrics.app.settings import CONFIG_PATH
from InstantLyrics.lyrics.utils import get_lyrics
from InstantLyrics import utils
diff --git a/InstantLyrics/cmd.py b/InstantLyrics/cmd.py
index 277199e..271b46b 100755
--- a/InstantLyrics/cmd.py
+++ b/InstantLyrics/cmd.py
@@ -1,7 +1,7 @@
# !/usr/bin/python3
# coding: utf-8
-from app.appIndicator import AppIndicator
+from InstantLyrics.app.appIndicator import AppIndicator
def main():
diff --git a/icons/instant-lyrics-24.png b/InstantLyrics/icons/instant-lyrics-24.png
similarity index 100%
rename from icons/instant-lyrics-24.png
rename to InstantLyrics/icons/instant-lyrics-24.png
diff --git a/icons/instant-lyrics-256.png b/InstantLyrics/icons/instant-lyrics-256.png
similarity index 100%
rename from icons/instant-lyrics-256.png
rename to InstantLyrics/icons/instant-lyrics-256.png
diff --git a/icons/instant-lyrics-32.png b/InstantLyrics/icons/instant-lyrics-32.png
similarity index 100%
rename from icons/instant-lyrics-32.png
rename to InstantLyrics/icons/instant-lyrics-32.png
diff --git a/icons/instant-lyrics-512.png b/InstantLyrics/icons/instant-lyrics-512.png
similarity index 100%
rename from icons/instant-lyrics-512.png
rename to InstantLyrics/icons/instant-lyrics-512.png
diff --git a/icons/instant-lyrics.svg b/InstantLyrics/icons/instant-lyrics.svg
similarity index 100%
rename from icons/instant-lyrics.svg
rename to InstantLyrics/icons/instant-lyrics.svg
diff --git a/InstantLyrics/lyrics/genius.py b/InstantLyrics/lyrics/genius.py
index 3530a99..b34f345 100644
--- a/InstantLyrics/lyrics/genius.py
+++ b/InstantLyrics/lyrics/genius.py
@@ -3,7 +3,7 @@
from bs4 import BeautifulSoup
-from lyrics.models import InternetLyricsFetcher
+from InstantLyrics.lyrics.models import InternetLyricsFetcher
class GeniusFetcher(InternetLyricsFetcher):
diff --git a/InstantLyrics/lyrics/local.py b/InstantLyrics/lyrics/local.py
index f6db60f..b3b5248 100644
--- a/InstantLyrics/lyrics/local.py
+++ b/InstantLyrics/lyrics/local.py
@@ -3,7 +3,7 @@
import os
-from lyrics.models import LyricsFetcher
+from InstantLyrics.lyrics.models import LyricsFetcher
def get_local_lyrics_filename(query, folder):
diff --git a/InstantLyrics/lyrics/lyricstranslate.py b/InstantLyrics/lyrics/lyricstranslate.py
index 92e93b7..62f25b4 100644
--- a/InstantLyrics/lyrics/lyricstranslate.py
+++ b/InstantLyrics/lyrics/lyricstranslate.py
@@ -3,7 +3,7 @@
from bs4 import BeautifulSoup
-from lyrics.models import InternetLyricsFetcher
+from InstantLyrics.lyrics.models import InternetLyricsFetcher
class LyricsTranslateFetcher(InternetLyricsFetcher):
diff --git a/InstantLyrics/lyrics/metrolyrics.py b/InstantLyrics/lyrics/metrolyrics.py
index 7243b8d..54f714c 100644
--- a/InstantLyrics/lyrics/metrolyrics.py
+++ b/InstantLyrics/lyrics/metrolyrics.py
@@ -3,7 +3,7 @@
from bs4 import BeautifulSoup
-from lyrics.models import InternetLyricsFetcher
+from InstantLyrics.lyrics.models import InternetLyricsFetcher
class MetrolyricsFetcher(InternetLyricsFetcher):
diff --git a/InstantLyrics/lyrics/utils.py b/InstantLyrics/lyrics/utils.py
index 4debaa8..0eec137 100644
--- a/InstantLyrics/lyrics/utils.py
+++ b/InstantLyrics/lyrics/utils.py
@@ -3,10 +3,13 @@
import os
-from lyrics.genius import LYRICS_FINDERS as GENIUS_FINDERS
-from lyrics.local import LocalLyricsFetcher, get_local_lyrics_filename
-from lyrics.lyricstranslate import LYRICS_FINDERS as LYRICSTRANSLATE_FINDERS
-from lyrics.metrolyrics import LYRICS_FINDERS as METROLYRICS_FINDERS
+from InstantLyrics.lyrics.genius import LYRICS_FINDERS as GENIUS_FINDERS
+from InstantLyrics.lyrics.local import LocalLyricsFetcher, \
+ get_local_lyrics_filename
+from InstantLyrics.lyrics.lyricstranslate import \
+ LYRICS_FINDERS as LYRICSTRANSLATE_FINDERS
+from InstantLyrics.lyrics.metrolyrics import \
+ LYRICS_FINDERS as METROLYRICS_FINDERS
LOCAL_LYRICS_FOLDER = os.path.join(
os.getenv("HOME"),
diff --git a/InstantLyrics/utils.py b/InstantLyrics/utils.py
index 154dd0b..eddb319 100644
--- a/InstantLyrics/utils.py
+++ b/InstantLyrics/utils.py
@@ -4,7 +4,7 @@
import configparser
import os
-from app.settings import CONFIG_PATH
+from InstantLyrics.app.settings import CONFIG_PATH
BUG_ISSUES_REPORT_ERROR = "If that's not the case, report an issue
Date: Wed, 26 Sep 2018 11:46:49 +0200
Subject: [PATCH 22/25] fix app icon path
---
InstantLyrics/app/windows.py | 2 +-
InstantLyrics/utils.py | 15 ++++++++-------
README.md | 10 ++++------
3 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/InstantLyrics/app/windows.py b/InstantLyrics/app/windows.py
index a3241fa..97520c3 100644
--- a/InstantLyrics/app/windows.py
+++ b/InstantLyrics/app/windows.py
@@ -164,7 +164,7 @@ class PreferenceWindow(Gtk.Window):
def __init__(self, app):
Gtk.Window.__init__(self, title="Instant-Lyrics Prefenreces")
self.set_icon_from_file(
- utils.get_icon_path('../icons/instant-lyrics-32.png'))
+ utils.get_default_icon_path())
self.set_border_width(20)
# self.set_default_size(350, 550)
self.set_position(Gtk.WindowPosition.CENTER)
diff --git a/InstantLyrics/utils.py b/InstantLyrics/utils.py
index eddb319..ea20aa3 100644
--- a/InstantLyrics/utils.py
+++ b/InstantLyrics/utils.py
@@ -46,21 +46,22 @@ def get_default_icon_path():
return get_icon_path('./icons/instant-lyrics-32.png')
-def create_desktop_entry():
- src_path = os.path.dirname(os.path.realpath(__file__)) # path of scr folder
- base_path = os.path.dirname(src_path) # path of base folder
+def get_desktop_icon_path():
+ return get_icon_path('./icons/instant-lyrics-256.png')
+
+def create_desktop_entry():
entry = "[Desktop Entry]\n"
v = "Version=1.0\n"
tp = "Type=Application\n"
nm = "Name=Instant Lyrics\n"
- ic = "Icon=" + base_path + "/icons/instant-lyrics-256.png\n"
- ex = "Exec=python3 " + base_path + "/cmd.py\n"
+ ic = "Icon=" + get_desktop_icon_path() + "\n"
+ ex = "Exec=instantlyrics\n"
cm = "Comment=Shows lyrics of songs instantly\n"
tm = "Terminal=false\n"
- entry_path = os.getenv(
- "HOME") + "/.local/share/applications/instant-lyrics.desktop"
+ entry_path = os.getenv("HOME") + \
+ "/.local/share/applications/Instant-Lyrics.desktop"
with open(entry_path, 'w') as file:
file.write(entry)
diff --git a/README.md b/README.md
index 7f7347b..5d19da3 100644
--- a/README.md
+++ b/README.md
@@ -36,18 +36,16 @@ First, install the requirements:
sudo apt install python-gi python-dbus gir1.2-appindicator3-0.1 python-requests python-bs4 python-lxml
```
-(requests, lxml and bs4 can be install from `pip` also: `pip install requests lxml beautifiulsoup4`)
-
### For Arch users
``` bash
-sudo pacman -S python2-dbus python2-requests python2-lxml python2-beautifulsoup4 python2-gobject libappindicator-gtk3
+sudo pacman -S python3-dbus python3-requests python3-lxml python3-beautifulsoup4 python3-gobject libappindicator-gtk3
```
### Fedora
``` bash
-sudo dnf install dbus-python python-gobject libappindicator-gtk3 python2-requests python-beautifulsoup4 python2-lxml
+sudo dnf install dbus-python python-gobject libappindicator-gtk3 python3-requests python-beautifulsoup4 python3-lxml
```
Then, enter the commands:
@@ -55,8 +53,8 @@ Then, enter the commands:
``` bash
git clone https://github.com/bhrigu123/Instant-Lyrics.git
cd Instant-Lyrics/
-python InstantLyrics.py # `python InstantLyrics.py &` to keep it running in
-background
+pip3 install .# this installs requests, lxml and bs4
+instantlyrics # `instantlyrics.py &` to keep it running in background
```
The icon will appear in the system tray (indicator panel). You can start using the application from there.
From e8d5c80d4cb59bdf3648d3a595f8e978e96d6b57 Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Wed, 26 Sep 2018 11:53:39 +0200
Subject: [PATCH 23/25] add appimage recipe
---
VERSION | 1 +
appimage/Instant-Lyrics.yml | 20 ++++++++++++++++++++
2 files changed, 21 insertions(+)
create mode 100644 VERSION
create mode 100644 appimage/Instant-Lyrics.yml
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..da05b92
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+Version: 0.7.0 (315666d826caffdfee31968cd6463488987db8f5 at 11:46:49 18-09-26 +0200)
\ No newline at end of file
diff --git a/appimage/Instant-Lyrics.yml b/appimage/Instant-Lyrics.yml
new file mode 100644
index 0000000..ac9285d
--- /dev/null
+++ b/appimage/Instant-Lyrics.yml
@@ -0,0 +1,20 @@
+app: instantlyrics
+binpatch: true
+
+ingredients:
+ dist: trusty
+ sources:
+ - deb http://archive.ubuntu.com/ubuntu/ trusty main universe
+ packages:
+ - python-gi
+ - python-dbus
+ - gir1.2-appindicator3-0.1
+ - python-requests
+ - python-bs4
+ - python-lxml
+ script:
+ - wget -c https://github.com/sirfoga/Instant-Lyrics/archive/master.zip
+ - unzip master.zip
+
+script:
+ - pip3 install .
From 66ff73bf65a2034ccd8fb05d69ba9ffd971a52d9 Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Thu, 21 Mar 2019 17:52:31 +0100
Subject: [PATCH 24/25] fix readme
---
README.md | 104 +++++++++++++++---------------------------------------
1 file changed, 28 insertions(+), 76 deletions(-)
diff --git a/README.md b/README.md
index 5d19da3..6d5bb48 100644
--- a/README.md
+++ b/README.md
@@ -1,101 +1,53 @@
-
-
Instant-Lyrics
-
Instantly fetches the lyrics of the currently playing song and displays it on a window.
-
A linux application with a very convenient GUI. Built with Python 3 Gtk+3 (gi).
+
+
Instant-Lyrics
+
Shows lyrics of the currently playing song
+
+
A linux application with a very convenient GUI. Built with Python 3 and Gtk+3
+
+
-# Screenshot
-![Screenshot](https://cloud.githubusercontent.com/assets/6123105/23824316/3fe58044-069a-11e7-804e-180ea4041002.jpeg)
-
-# Working example
-![Working](https://cloud.githubusercontent.com/assets/6123105/23824730/e0e0829e-06a1-11e7-8d57-3235c4266f2c.gif)
-
-# Compatibility
-- Python 3
-- Linux
-# Installation
+## Install
-### system requirements (use `apt`, `pacman`, `dnf` ... your package manager):
+### system requirements (use `apt`, `pacman`, `dnf`...):
- python-gi (PyGObject)
- python-dbus
- AppIndicator3
### python requirements (use `pip2` or `pip3`):
-- requests
-- beautifulsoup4
-- lxml
-
-
-First, install the requirements:
-
-### For Ubuntu/Debian based systems:
-
-``` bash
-sudo apt install python-gi python-dbus gir1.2-appindicator3-0.1 python-requests python-bs4 python-lxml
+```bash
+pip install .
```
-### For Arch users
-
+## Usage
``` bash
-sudo pacman -S python3-dbus python3-requests python3-lxml python3-beautifulsoup4 python3-gobject libappindicator-gtk3
-```
-
-### Fedora
-
-``` bash
-sudo dnf install dbus-python python-gobject libappindicator-gtk3 python3-requests python-beautifulsoup4 python3-lxml
-```
-
-Then, enter the commands:
-
-``` bash
-git clone https://github.com/bhrigu123/Instant-Lyrics.git
-cd Instant-Lyrics/
-pip3 install .# this installs requests, lxml and bs4
instantlyrics # `instantlyrics.py &` to keep it running in background
```
The icon will appear in the system tray (indicator panel). You can start using the application from there.
-
-
-# Creating a launcher shortcut
-
-If you have installed from source, you can go to **Preferences** from the menu options, and click on the button `Create Desktop Entry`.
-You should be able to see the `Instant Lyrics` application shortcut in your launcher menu.
-You can also find several manual ways of doing so from the web.
-
-![Launcher](https://cloud.githubusercontent.com/assets/6123105/23824317/4735e83e-069a-11e7-8b1e-2814632bb3aa.jpeg)
-
+## Got questions?
+If you have questions or general suggestions, don't hesitate to submit
+a new [Github issue](https://github.com/sirfoga/Instant-Lyrics/issues/new).
-# Contribution
-Create an issue to discuss the changes/modifications before sending a PR.
+## Contributing
+[Fork](https://github.com/sirfoga/Instant-Lyrics/fork) | Patch | Push | [Pull request](https://github.com/sirfoga/Instant-Lyrics/pulls)
-# Icon Credits
-Icon made by [Freepik](http://www.freepik.com/) from www.flaticon.com
+## Feedback
+Suggestions and improvements are [welcome](https://github.com/sirfoga/Instant-Lyrics/issues)!
-# License
-## The MIT License
-> Copyright (c) 2017 Bhrigu Srivastava http://bhrigu.me
+## Authors
-> Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+| [![sirfoga](https://avatars0.githubusercontent.com/u/14162628?s=128&v=4)](https://github.com/sirfoga "Follow @sirfoga on Github") |
+|---|
+| [Stefano Fogarollo](https://sirfoga.github.io) |
-> The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
+## Thanks
+Thanks to [Bhrigu Srivastava](https://github.com/bhrigu123), the owner of the [original repository](https://github.com/bhrigu123/Instant-Lyrics).
-> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+## License
+All of the codebases are **[MIT licensed](https://opensource.org/licenses/MIT)** unless otherwise specified.
+**[Back to top](#topOfReadme)**
From 46f983ce848fb77e949a6866b136354a28d9c957 Mon Sep 17 00:00:00 2001
From: sirfoga
Date: Thu, 21 Mar 2019 17:55:37 +0100
Subject: [PATCH 25/25] remove version
---
VERSION | 1 -
setup.py | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
delete mode 100644 VERSION
diff --git a/VERSION b/VERSION
deleted file mode 100644
index da05b92..0000000
--- a/VERSION
+++ /dev/null
@@ -1 +0,0 @@
-Version: 0.7.0 (315666d826caffdfee31968cd6463488987db8f5 at 11:46:49 18-09-26 +0200)
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 279bc06..b03ebe4 100755
--- a/setup.py
+++ b/setup.py
@@ -24,7 +24,7 @@
setup(
name="Instant-Lyrics",
- version="2.0",
+ version="2.0.1",
author="sirfoga",
author_email='captain.bhrigu@gmail.com,nicolas.guichard@ensimag.fr,'
'sirfoga@protonmail.com',