forked from robbeofficial/spotifyripper
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjbripper.py
executable file
·158 lines (130 loc) · 4.93 KB
/
jbripper.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#!/usr/bin/env python
# -*- coding: utf8 -*-
from subprocess import call, Popen, PIPE
from spotify import Link, Image
from jukebox import Jukebox, container_loaded
import os, sys
import threading
import time
playback = False # set if you want to listen to the tracks that are currently ripped (start with "padsp ./jbripper.py ..." if using pulse audio)
rawpcm = False # also saves a .pcm file with the raw PCM data as delivered by libspotify ()
pcmfile = None
pipe = None
ripping = False
end_of_track = threading.Event()
def printstr(str): # print without newline
sys.stdout.write(str)
sys.stdout.flush()
def shell(cmdline): # execute shell commands (unicode support)
call(cmdline, shell=True)
def rip_init(session, track):
global pipe, ripping, pcmfile, rawpcm
num_track = "%02d" % (track.index(),)
mp3file = track.name()+".mp3"
pcmfile = track.name()+".pcm"
directory = os.getcwd() + "/" + track.artists()[0].name() + "/" + track.album().name() + "/"
if not os.path.exists(directory):
os.makedirs(directory)
printstr("ripping " + mp3file + " ...")
p = Popen("lame --silent -V0 -h -r - \""+ directory + mp3file+"\"", stdin=PIPE, shell=True)
pipe = p.stdin
if rawpcm:
pcmfile = open(directory + pcmfile, 'w')
ripping = True
def rip_terminate(session, track):
global ripping, pipe, pcmfile, rawpcm
if pipe is not None:
print(' done!')
pipe.close()
if rawpcm:
pcmfile.close()
ripping = False
def rip(session, frames, frame_size, num_frames, sample_type, sample_rate, channels):
if ripping:
printstr('.')
pipe.write(frames);
if rawpcm:
pcmfile.write(frames)
def rip_id3(session, track): # write ID3 data
num_track = "%02d" % (track.index(),)
mp3file = track.name()+".mp3"
artist = track.artists()[0].name()
album = track.album().name()
title = track.name()
year = track.album().year()
directory = os.getcwd() + "/" + track.artists()[0].name() + "/" + track.album().name() + "/"
# download cover
image = session.image_create(track.album().cover())
while not image.is_loaded(): # does not work from MainThread!
time.sleep(0.1)
fh_cover = open('cover.jpg','wb')
fh_cover.write(image.data())
fh_cover.close()
# write id3 data
cmd = "eyeD3" + \
" --add-image cover.jpg:FRONT_COVER" + \
" -t \"" + title + "\"" + \
" -a \"" + artist + "\"" + \
" -A \"" + album + "\"" + \
" -n " + str(num_track) + \
" -Y " + str(year) + \
" -Q " + \
" \"" + directory + mp3file + "\""
shell(cmd)
# delete cover
shell("rm -f cover.jpg")
class RipperThread(threading.Thread):
def __init__(self, ripper):
threading.Thread.__init__(self)
self.ripper = ripper
def run(self):
# wait for container
container_loaded.wait()
container_loaded.clear()
# create track iterator
link = Link.from_string(sys.argv[3])
if link.type() == Link.LINK_TRACK:
track = link.as_track()
itrack = iter([track])
elif link.type() == Link.LINK_PLAYLIST:
playlist = link.as_playlist()
print('loading playlist ...')
while not playlist.is_loaded():
time.sleep(0.1)
print('done')
itrack = iter(playlist)
# ripping loop
session = self.ripper.session
for track in itrack:
self.ripper.load_track(track)
rip_init(session, track)
self.ripper.play()
end_of_track.wait()
end_of_track.clear() # TODO check if necessary
rip_terminate(session, track)
rip_id3(session, track)
self.ripper.disconnect()
class Ripper(Jukebox):
def __init__(self, *a, **kw):
Jukebox.__init__(self, *a, **kw)
self.ui = RipperThread(self) # replace JukeboxUI
self.session.set_preferred_bitrate(1) # 320 bps
def music_delivery_safe(self, session, frames, frame_size, num_frames, sample_type, sample_rate, channels):
rip(session, frames, frame_size, num_frames, sample_type, sample_rate, channels)
if playback:
return Jukebox.music_delivery_safe(self, session, frames, frame_size, num_frames, sample_type, sample_rate, channels)
else:
return num_frames
def end_of_track(self, session):
Jukebox.end_of_track(self, session)
end_of_track.set()
if __name__ == '__main__':
if len(sys.argv) >= 3:
ripper = Ripper(sys.argv[1],sys.argv[2]) # login
ripper.connect()
else:
print "usage : \n"
print " ./jbripper.py [username] [password] [spotify_url]"
print "example : \n"
print " ./jbripper.py user pass spotify:track:52xaypL0Kjzk0ngwv3oBPR - for a single file"
print " ./jbripper.py user pass spotify:user:username:playlist:4vkGNcsS8lRXj4q945NIA4 - rips entire playlist"