Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Super heavy pull request: fixes multiple issues, refactors code and adds features #14

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix link finder
sirfoga committed Sep 25, 2018
commit 88a57c87004d8e9ec65615a4fc82ec9b896ea3d8
6 changes: 5 additions & 1 deletion src/appIndicator.py
Original file line number Diff line number Diff line change
@@ -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():
147 changes: 116 additions & 31 deletions src/lyrics.py
Original file line number Diff line number Diff line change
@@ -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,49 +94,102 @@ 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", {
"q": 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", {
"q": 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('<p class="verse">', '\n')
parsed = parsed.replace('<br/>', ' ')
parsed = parsed.replace('</p>', ' ')
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('<p class="verse">', '\n')
final_lyrics = final_lyrics.replace('<br/>', ' ')
final_lyrics = final_lyrics.replace('</p>', ' ')
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()
2 changes: 0 additions & 2 deletions src/windows.py
Original file line number Diff line number Diff line change
@@ -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()