Skip to content

Commit 968e89d

Browse files
committed
add run-once version of the script, moved common methods to utils.
1 parent 28a7b2d commit 968e89d

File tree

6 files changed

+241
-102
lines changed

6 files changed

+241
-102
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
build
22
dist
3-
*.egg-info
3+
*.egg-info
4+
__pycache__

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,42 @@ for_window [instance="Lyrics"] floating enable; [instance="Lyrics"] move positio
5858
for_window [instance="Lyrics"] resize set 644 388
5959
```
6060

61+
### Example Use Case (Conky)
62+
You can use spotify-lyrics-once to output the lyrics to the stdout and exit.
63+
If you have program that utilizes stdin, you can use this version.
64+
An example for such program is Conky.
65+
66+
``` lua
67+
#!/usr/bin/lua
68+
conky.config = {
69+
alignment = 'tl',
70+
background = true,
71+
color2 = '2ECC71',
72+
cpu_avg_samples = 2,
73+
default_color = 'FFFFFF',
74+
double_buffer = true,
75+
font = 'Bitstream Vera Sans:size=10',
76+
gap_x = 30,
77+
gap_y = 50,
78+
minimum_width = 200,
79+
no_buffers = true,
80+
own_window = true,
81+
own_window_type = 'override',
82+
own_window_transparent = true,
83+
own_window_argb_visual = true,
84+
-- own_window_type = 'desktop',
85+
update_interval = 3,
86+
use_xft = true,
87+
}
88+
conky.text = [[
89+
${voffset 8}$color2${font Bitstream Vera Sans:size=16}${time %A}$font\
90+
${voffset -8}$alignr$color${font Bitstream Vera Sans:size=38}${time %e}$font
91+
$color${voffset -30}$color${font Bitstream Vera Sans:size=18}${time %b}$font\
92+
${voffset -3} $color${font Bitstream Vera Sans:size=20}${time %Y}$font$color2$hr
93+
${execi 5 spotify-lyrics-once}
94+
]]
95+
```
96+
6197
## License
6298
yet-another-spotify-lyrics is licensed under the MIT License.
6399

setup.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
long_description = f.read()
99

1010
setup(name='yet-another-spotify-lyrics',
11-
version='2.0.0',
11+
version='2.1.0',
1212
description='Command Line Spotify Lyrics with Album Cover',
1313
author='Göktuğ Karakaşlı',
1414
author_email='[email protected]',
@@ -17,12 +17,15 @@
1717
long_description_content_type='text/markdown',
1818
url='https://github.com/goktug97/yet-another-spotify-lyrics',
1919
download_url=(
20-
'https://github.com/goktug97/yet-another-spotify-lyrics/archive/v2.0.0.tar.gz'),
20+
'https://github.com/goktug97/yet-another-spotify-lyrics/archive/v2.1.0.tar.gz'),
2121
py_modules=[os.path.splitext(os.path.basename(path))[0]
22-
for path in ['spotify_lyrics']],
22+
for path in ['spotify_lyrics',
23+
'utils',
24+
'spotify_lyrics_once.py']],
2325
entry_points={
2426
'console_scripts': [
2527
'spotify-lyrics = spotify_lyrics:main',
28+
'spotify-lyrics-once = spotify_lyrics_once:main'
2629
]
2730
},
2831
classifiers=[

spotify_lyrics.py

Lines changed: 18 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -18,93 +18,13 @@
1818
import ueberzug.lib.v0 as ueberzug
1919
from bs4 import BeautifulSoup
2020

21-
def hide_cursor():
22-
sys.stdout.write("\033[?25l")
23-
sys.stdout.flush()
21+
import utils
2422

25-
def show_cursor():
26-
sys.stdout.write("\033[?25h")
27-
sys.stdout.flush()
28-
atexit.register(show_cursor)
29-
30-
def move_cursor(x, y):
31-
sys.stdout.write(f"\033[{y};{x}H")
32-
33-
def delete_line():
34-
sys.stdout.write('\x1b[2K')
35-
36-
def boldify(string):
37-
return f'\033[1m{string}\033[0m'
38-
39-
def fetch_lyrics(artist, title):
40-
title = re.sub(r'(-.*)', '', title)
41-
search_string = f'{artist} {title} lyrics'
42-
search_string = urllib.parse.quote_plus(search_string)
43-
url = 'https://google.com/search?q=' + search_string
44-
r = requests.get(url)
45-
soup = BeautifulSoup(r.content, "lxml", from_encoding='UTF-8')
46-
raw_lyrics = (soup.findAll('div', attrs={'class': 'hwc'}))
47-
final_lyrics = str.join(u'\n', map(str, raw_lyrics))
48-
final_lyrics = re.sub(r'<(.*)>', '', string=final_lyrics)
49-
final_lyrics = '\n'.join(final_lyrics.split('\n')[:-2])
50-
return final_lyrics
51-
52-
def terminal_size():
53-
rows, columns = map(int, os.popen('stty size', 'r').read().split())
54-
return rows, columns
55-
56-
class KeyPoller():
57-
def __enter__(self):
58-
self.fd = sys.stdin.fileno()
59-
self.new_term = termios.tcgetattr(self.fd)
60-
self.old_term = termios.tcgetattr(self.fd)
61-
self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO)
62-
termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term)
63-
return self
64-
65-
def __exit__(self, type, value, traceback):
66-
termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old_term)
67-
68-
def poll(self):
69-
dr,dw,de = select.select([sys.stdin], [], [], 0.0)
70-
return sys.stdin.read(1) if not dr == [] else None
71-
72-
73-
class Spotify(object):
74-
def __init__(self):
75-
session_bus = dbus.SessionBus()
76-
try:
77-
self.spotify_bus = session_bus.get_object(
78-
"org.mpris.MediaPlayer2.spotify",
79-
"/org/mpris/MediaPlayer2")
80-
except dbus.exceptions.DBusException:
81-
sys.exit("Can't access to Spotify DBUS")
82-
self.player_interface = dbus.Interface(
83-
self.spotify_bus, dbus_interface='org.mpris.MediaPlayer2.Player')
84-
self.properties_interface = dbus.Interface(
85-
self.spotify_bus, "org.freedesktop.DBus.Properties")
86-
87-
def metadata(self):
88-
metadata = self.properties_interface.Get(
89-
"org.mpris.MediaPlayer2.Player", "Metadata")
90-
title = metadata['xesam:title'].replace("&", "&amp;")
91-
artist = metadata['xesam:artist'][0].replace("&", "&amp;")
92-
album = metadata['xesam:album'].replace("&", "&amp;")
93-
art_url = metadata['mpris:artUrl'].replace("&", "&amp;")
94-
return title, artist, album, art_url
95-
96-
def next(self):
97-
self.player_interface.Next()
98-
99-
def prev(self):
100-
self.player_interface.Previous()
101-
102-
def toggle(self):
103-
self.player_interface.PlayPause()
23+
atexit.register(utils.show_cursor)
10424

10525
class Lyrics(object):
10626
def __init__(self):
107-
self.spotify = Spotify()
27+
self.spotify = utils.Spotify()
10828
self.home = str(Path.home())
10929

11030
def update_directories(self):
@@ -147,14 +67,14 @@ def save_lyrics(self):
14767

14868
def update_lyrics(self):
14969
if not os.path.exists(self.lyrics_file):
150-
self.lyrics = fetch_lyrics(self.artist, self.song)
70+
self.lyrics = utils.fetch_lyrics(self.artist, self.song)
15171
self.save_lyrics()
15272
else:
15373
self.lyrics = self.read_lyrics()
15474

15575
@ueberzug.Canvas()
15676
def main(self, canvas):
157-
self.rows, self.columns = terminal_size()
77+
self.rows, self.columns = utils.terminal_size()
15878
self.song, self.artist, self.album, self.art_url = self.spotify.metadata()
15979

16080
self.update_directories()
@@ -168,16 +88,16 @@ def main(self, canvas):
16888
album_cover.path = self.image_file
16989
album_cover.visibility = ueberzug.Visibility.VISIBLE
17090

171-
hide_cursor()
91+
utils.hide_cursor()
17292

17393
os.system('clear')
174-
move_cursor(0, 0)
94+
utils.move_cursor(0, 0)
17595
self.print_metadata()
17696

17797
current_line = 0
17898
start_row = 5
17999

180-
with KeyPoller() as key_poller:
100+
with utils.KeyPoller() as key_poller:
181101
while True:
182102
song, artist, album, art_url = self.spotify.metadata()
183103
if self.song != song or self.artist != artist:
@@ -191,10 +111,10 @@ def main(self, canvas):
191111
album_cover.path = self.image_file
192112

193113
os.system('clear')
194-
move_cursor(0, 0)
114+
utils.move_cursor(0, 0)
195115
self.print_metadata()
196116

197-
rows, columns = terminal_size()
117+
rows, columns = utils.terminal_size()
198118
if self.rows != rows or self.columns != columns:
199119
difference = rows - self.rows
200120
self.rows, self.columns = rows, columns
@@ -205,7 +125,7 @@ def main(self, canvas):
205125
album_cover.x = self.columns//2
206126

207127
os.system('clear')
208-
move_cursor(0, 0)
128+
utils.move_cursor(0, 0)
209129
self.print_metadata()
210130

211131
lines = self.lyrics.split('\n')
@@ -214,14 +134,14 @@ def main(self, canvas):
214134
wrapped_lines.extend(
215135
textwrap.fill(line, columns//2-2).split('\n'))
216136

217-
move_cursor(0, start_row)
137+
utils.move_cursor(0, start_row)
218138
n_entries = min(rows+current_line-start_row,
219139
len(wrapped_lines)) - current_line
220140
for i in range(current_line, current_line + n_entries):
221-
delete_line()
222-
print(boldify(wrapped_lines[i]))
223-
move_cursor(0, n_entries+start_row)
224-
delete_line()
141+
utils.delete_line()
142+
print(utils.boldify(wrapped_lines[i]))
143+
utils.move_cursor(0, n_entries+start_row)
144+
utils.delete_line()
225145

226146
key = key_poller.poll()
227147
if key == 'q':
@@ -239,14 +159,14 @@ def main(self, canvas):
239159
EDITOR = os.environ.get('EDITOR')
240160
call([EDITOR, self.lyrics_file])
241161
self.update_lyrics()
242-
hide_cursor()
162+
utils.hide_cursor()
243163
except TypeError:
244164
os.system('clear')
245165
print('$EDITOR is not set')
246166
time.sleep(1)
247167
elif key == 'r':
248168
os.system('clear')
249-
move_cursor(0, 0)
169+
utils.move_cursor(0, 0)
250170
self.print_metadata()
251171
elif key == 'd':
252172
os.remove(self.lyrics_file)

spotify_lyrics_once.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import re
5+
import select
6+
import sys
7+
import textwrap
8+
import urllib.parse
9+
from pathlib import Path
10+
from urllib.request import urlretrieve
11+
12+
import dbus
13+
import requests
14+
from bs4 import BeautifulSoup
15+
16+
import utils
17+
18+
class Lyrics(object):
19+
def __init__(self):
20+
self.spotify = utils.Spotify()
21+
self.home = str(Path.home())
22+
23+
def update_directories(self):
24+
self.lyrics_directory = os.path.join(self.home, '.cache', 'spotify-lyrics')
25+
self.artist_directory = os.path.join(
26+
self.lyrics_directory, self.artist.replace('/', ''))
27+
self.album_directory = os.path.join(
28+
self.artist_directory, self.album.replace('/', ''))
29+
self.image_directory = os.path.join(self.artist_directory, 'album_arts')
30+
self.lyrics_file = os.path.join(
31+
self.album_directory, self.song.replace('/', ''))
32+
self.image_file = f'{os.path.join(self.image_directory, self.album)}.png'
33+
34+
if not os.path.isdir(self.lyrics_directory): os.mkdir(self.lyrics_directory)
35+
if not os.path.isdir(self.artist_directory): os.mkdir(self.artist_directory)
36+
if not os.path.isdir(self.album_directory): os.mkdir(self.album_directory)
37+
if not os.path.isdir(self.image_directory): os.mkdir(self.image_directory)
38+
39+
try:
40+
if not os.path.exists(self.image_file):
41+
urlretrieve(self.art_url, self.image_file)
42+
except FileNotFoundError:
43+
pass
44+
except urllib.error.URLError:
45+
pass
46+
47+
def print_metadata(self):
48+
print(f'Artist: {self.artist}')
49+
print(f'Album: {self.album}')
50+
print(f'Song: {self.song}\n')
51+
52+
def read_lyrics(self):
53+
with open(self.lyrics_file, 'r') as f:
54+
lyrics = ''.join(f.readlines())
55+
return lyrics
56+
57+
def save_lyrics(self):
58+
with open(self.lyrics_file, 'w') as f:
59+
f.write(self.lyrics)
60+
61+
def update_lyrics(self):
62+
if not os.path.exists(self.lyrics_file):
63+
self.lyrics = utils.fetch_lyrics(self.artist, self.song)
64+
self.save_lyrics()
65+
else:
66+
self.lyrics = self.read_lyrics()
67+
68+
def main(self):
69+
self.song, self.artist, self.album, self.art_url = self.spotify.metadata()
70+
self.update_directories()
71+
self.update_lyrics()
72+
self.print_metadata()
73+
lines = self.lyrics.split('\n')
74+
wrapped_lines = []
75+
for line in lines:
76+
wrapped_lines.extend(
77+
textwrap.fill(line, 50).split('\n'))
78+
79+
for i in range(len(wrapped_lines)):
80+
print(wrapped_lines[i])
81+
82+
def main():
83+
Lyrics().main()
84+
85+
if __name__ == '__main__':
86+
main()

0 commit comments

Comments
 (0)