Merge pull request #65 from Ritiek/develop

Add --song and --user options
This commit is contained in:
Ritiek Malhotra
2017-06-08 09:51:41 +05:30
committed by GitHub
2 changed files with 135 additions and 85 deletions

View File

@@ -48,14 +48,10 @@ git clone https://github.com/Ritiek/Spotify-Downloader
cd Spotify-Downloader
pip install -U -r requirements.txt
```
You'll also need to install avconv:
You'll also need to install avconv (use `--ffmpeg` option when using the script if you want `ffmpeg`):
`sudo apt-get install libav-tools` (`brew install libav` for Mac)
Use `python spotdl.py` to launch the script.
For list of available options, use `python spotdl.py --help`
### Windows
Assuming you have Python already installed..
@@ -68,25 +64,42 @@ Shift+right-click on empty area and open cmd and type:
`"Scripts/pip.exe" install -U -r requirements.txt`
Now to run the script type:
`python.exe spotdl.py`
For list of available options, use `python.exe spotdl.py --help`
(you can create a batch file shortcut to run the script just by double-click anytime)
## Instructions for Downloading Songs
- Launch the script using the above command as mentioned for your OS.
- For all available options, run `python spotdl.py --help` (or for windows run `python.exe spotdl.py --help`).
- For available options, run `sudo python spotdl.py --help` (or for windows run `python.exe spotdl.py --help`).
```
usage: spotdl.py [-h] (-s SONG | -l LIST | -u USERNAME) [-n] [-m] [-f] [-v]
[-i INPUT_EXT] [-o OUTPUT_EXT]
Download and convert songs from Spotify, Youtube etc.
optional arguments:
-h, --help show this help message and exit
-s SONG, --song SONG download song by spotify link or name (default: None)
-l LIST, --list LIST download songs from a file (default: None)
-u USERNAME, --username USERNAME
load user's playlists into <playlist_name>.txt
(default: None)
-n, --no-convert skip the conversion process and meta-tags (default:
False)
-m, --manual choose the song to download manually (default: False)
-f, --ffmpeg Use ffmpeg instead of libav for conversion. If not set
defaults to libav (default: False)
-v, --verbose show debug output (default: False)
-i INPUT_EXT, --input_ext INPUT_EXT
prefered input format .m4a or .webm (Opus) (default:
.m4a)
-o OUTPUT_EXT, --output_ext OUTPUT_EXT
prefered output extension .mp3 or .m4a (AAC) (default:
.mp3)
```
#### Downloading by Name
For example
- We want to download Hello by Adele, simply run the script and type `adele hello`.
- We want to download Hello by Adele, simply run `python spotdl.py --song "adele hello"`.
- The script will automatically look for the best matching song and download it in the folder `Music/` placed in your current directory.
@@ -98,56 +111,56 @@ For example
- We want to download the same song (i.e: Hello by Adele) but using Spotify Link this time that looks like `http://open.spotify.com/track/1MDoll6jK4rrk2BcFRP5i7`, you can copy it from your Spotify desktop or mobile app by right clicking or long tap on the song and copy HTTP link.
- Now simply paste this link after running the script, it should download Hello by Adele.
- Run `python spotdl.py --song http://open.spotify.com/track/1MDoll6jK4rrk2BcFRP5i7`, it should download Hello by Adele.
- Just like before, it will again convert the song to an mp3 but since we used a Spotify HTTP link, the script is guaranteed to fetch the correct meta-tags and album-art.
- Just hit `ctrl+c` to exit out of the script.
#### What if we want to download multiple songs at once?
#### Download multiple songs at once
For example
- We want to download Hello by Adele, The Nights by Avicci and 21 Guns by Green Day just using a single command.
- We want to download `Hello by Adele`, `The Nights by Avicci` and `21 Guns by Green Day` just using a single command.
Also this time we have the Spotify link only for Hello by Adele but not for other two songs.
Let's suppose, we have the Spotify link for only `Hello by Adele` and `21 Guns by Green Day`.
No problem!
- Just make a `list.txt` by running the following commands:
- Just make a `list.txt` in the same folder as the script and add all the songs you want to download, in our case it is
```
cd
cd Spotify-Downloader
nano list.txt
```
(if you are on windows, just edit `list.txt` - i.e `C:\Python27\list.txt`)
Add all the songs you want to download, in our case it is
```
https://open.spotify.com/track/1MDoll6jK4rrk2BcFRP5i7
the nights avicci
21 guns green day
http://open.spotify.com/track/64yrDBpcdwEdNY9loyEGbX
```
- Now pass `--list` or `-l` to the script, i.e `python spotdl.py --list` (for windows: `python.exe spotdl.py --list`) and it will start downloading songs mentioned in `list.txt`.
- Now pass `--list=list.txt` to the script, i.e `python spotdl.py --list=list.txt` and it will start downloading songs mentioned in `list.txt`.
- You can stop downloading songs by hitting `ctrl+c`, the script will automatically resume from the song where you stopped it the next time you want to download the songs present in `list.txt`.
- To download all songs in your playlist, just select all the songs `ctrl+a` in Spotify desktop app, copy them `ctrl+c` and paste `ctrl+v` in `list.txt` and then use `--list` argument.
- Songs that are already downloaded will be skipped and not be downloaded again.
- Songs that are already downloaded will be skipped and will not be downloaded again.
#### Downloading playlists
- You can also load songs from any playlist provided you have spotify username of that user.
- Try running `python spotdl.py -u <your_username>`, it will show all your public playlists.
- Once you select the one you want to download, the script will load all the tracks from the playlist into `<playlist_name>.txt`
- Then you can simply run `python spotdl.py --list=<playlist_name>.txt` to download them all!
## FAQ
#### I get system cannot find the specified file when downloading?
Check out these issues [#22](../../issues/22), [#35](../../issues/35), [#36](../../issues/36).
Check out these issues [#22](https://github.com/Ritiek/Spotify-Downloader/issues/22), [#35](https://github.com/Ritiek/Spotify-Downloader/issues/35), [#36](https://github.com/Ritiek/Spotify-Downloader/issues/36).
#### How can I download whole playlist with its URI?
Currently this is not possible without generating unique tokens from Spotify but you can copy all the songs from a playlist and paste them in `list.txt`. ~~I am avoiding tokens as much possible to retain the portability of this tool but if you would like to add it as an optional feature to this tool, PR's welcome!~~ Okay, I am working on it since Spotify has made it mandatory to use tokens but the good thing is that they can be hardcoded so that users don't have to generate their own.
~~Currently this is not possible without generating unique tokens from Spotify but you can copy all the songs from a playlist and paste them in `list.txt`. I am avoiding tokens as much possible to retain the portability of this tool but if you would like to add it as an optional feature to this tool, PR's welcome!~~
This feature has been added!
#### You write horrible code. What's wrong with you?

135
spotdl.py
View File

@@ -18,6 +18,8 @@ import argparse
def getInputLink(links):
#for i in range(len(links)):
# links[i] = str(i + 1) + '. ' + links[i]
while True:
try:
the_chosen_one = int(raw_input('>> Choose your number: '))
@@ -112,6 +114,37 @@ def getYouTubeTitle(content, number):
else:
return str(number) + '. ' + title
def feedTracks(file, tracks):
with open(file, 'a') as fout:
for item in tracks['items']:
track = item['track']
try:
fout.write(track['external_urls']['spotify'] + '\n')
except KeyError:
pass
def feedPlaylist(username):
playlists = spotify.user_playlists(username)
links = []
check = 1
for playlist in playlists['items']:
print(str(check) + '. ' + playlist['name'] + ' (' + str(playlist['tracks']['total']) + ' tracks)')
links.append(playlist)
check += 1
print('')
playlist = getInputLink(links)
results = spotify.user_playlist(playlist['owner']['id'], playlist['id'], fields="tracks,next")
print('')
file = slugify(playlist['name'], ok='-_()[]{}') + '.txt'
print('Feeding ' + str(playlist['tracks']['total']) + ' tracks to ' + file)
tracks = results['tracks']
feedTracks(file, tracks)
while tracks['next']:
tracks = spotify.next(tracks)
feedTracks(file, tracks)
# Generate name for the song to be downloaded
@@ -133,7 +166,7 @@ def downloadSong(content):
link.download(filepath='Music/' + music_file + input_ext)
def convertToMP3(music_file):
def convertWithAvconv(music_file):
if os.name == 'nt':
os.system(
'Scripts\\avconv.exe -loglevel 0 -i "' +
@@ -187,7 +220,7 @@ def convertWithFfmpeg(music_file):
print('Unknown formats. Unable to convert.', input_ext, output_ext)
return
if not args.quiet:
if args.verbose:
print(ffmpeg_pre +
'-i "Music/' + music_file + input_ext + '" ' +
ffmpeg_params +
@@ -226,7 +259,7 @@ def checkExists(music_file, raw_song, islist):
else:
return True
# Remove song from list.txt once downloaded
# Remove song from file once downloaded
def trimSong(file):
@@ -236,7 +269,7 @@ def trimSong(file):
fout.writelines(data[1:])
def fixSong(music_file, meta_tags):
def fixSongMP3(music_file, meta_tags):
audiofile = eyed3.load("Music/" + music_file + '.mp3')
audiofile.tag.artist = meta_tags['artists'][0]['name']
audiofile.tag.album_artist = meta_tags['artists'][0]['name']
@@ -304,6 +337,26 @@ def fixSongM4A(music_file, meta_tags):
audiofile.save()
def convertSong(music_file):
print('Converting ' + music_file + input_ext + ' to ' + output_ext[1:])
if args.ffmpeg:
convertWithFfmpeg(music_file)
else:
convertWithAvconv(music_file)
def fixSong(music_file, meta_tags):
if meta_tags is None:
print('Could not find meta-tags')
elif output_ext == '.m4a':
print('Fixing meta-tags')
fixSongM4A(music_file, meta_tags)
elif output_ext == '.mp3':
print('Fixing meta-tags')
fixSongMP3(music_file, meta_tags)
else:
print('Cannot embed meta-tags into given output extension')
# Logic behind preparing the song to download to finishing meta-tags
@@ -321,20 +374,9 @@ def grabSingle(raw_song, number=None):
downloadSong(content)
print('')
if not args.no_convert:
print('Converting ' + music_file + input_ext + ' to ' + output_ext)
if args.ffmpeg:
convertWithFfmpeg(music_file)
else:
convertToMP3(music_file)
convertSong(music_file)
meta_tags = generateMetaTags(raw_song)
if output_ext == '.m4a':
if meta_tags is not None:
print('Fixing meta-tags')
fixSongM4A(music_file, meta_tags)
elif output_ext == '.mp3':
if meta_tags is not None:
print('Fixing meta-tags')
fixSong(music_file, meta_tags)
fixSong(music_file, meta_tags)
# Fix python2 encoding issues
@@ -349,7 +391,7 @@ def fixEncoding(query):
def grabList(file):
lines = open(file, 'r').read()
lines = lines.splitlines()
# Ignore blank lines in list.txt (if any)
# Ignore blank lines in file (if any)
try:
lines.remove('')
except ValueError:
@@ -378,21 +420,29 @@ def getArgs(argv=None):
parser = argparse.ArgumentParser(description='Download and convert songs \
from Spotify, Youtube etc.',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-s', '--song',
help='download song by spotify link or name')
group.add_argument('-l', '--list',
help='download songs from a file')
group.add_argument('-u', '--username',
help="load user's playlists into <playlist_name>.txt")
parser.add_argument('-n', '--no-convert', default=False,
help='skip the conversion process and meta-tags', action='store_true')
parser.add_argument('-m', '--manual', default=False,
help='choose the song to download manually', action='store_true')
parser.add_argument('-l', '--list', default=False,
help='download songs present in list.txt', action='store_true')
parser.add_argument('-f', '--ffmpeg', default=False,
help='Use ffmpeg instead of libav for conversion. If not set defaults to libav',
action='store_true')
parser.add_argument('-q', '--quiet', default=False,
help='spare us output of ffmpeg conversion', action='store_true')
parser.add_argument('-v', '--verbose', default=False,
help='show debug output', action='store_true')
parser.add_argument('-i', '--input_ext', default='.m4a',
help='prefered input format .m4a or .webm (Opus)')
parser.add_argument('-o', '--output_ext', default='.mp3',
help='prefered output extension .mp3 or .m4a (AAC)')
return parser.parse_args(argv)
@@ -403,21 +453,6 @@ def graceQuit():
exit()
def spotifyDownload():
while True:
for temp in os.listdir('Music/'):
if temp.endswith('.m4a.temp'):
os.remove('Music/' + temp)
try:
print('Enter a Spotify URL or Song Name: ')
command = raw_input('>> ')
print('')
grabSingle(raw_song=command)
print('')
except KeyboardInterrupt:
graceQuit()
if __name__ == '__main__':
# Python 3 compatibility
@@ -427,9 +462,10 @@ if __name__ == '__main__':
os.chdir(path[0])
if not os.path.exists("Music"):
os.makedirs("Music")
open('list.txt', 'a').close()
eyed3.log.setLevel("ERROR")
for temp in os.listdir('Music/'):
if temp.endswith('.m4a.temp'):
os.remove('Music/' + temp)
# Please respect this user token :)
oauth2 = oauth2.SpotifyClientCredentials(
@@ -440,19 +476,20 @@ if __name__ == '__main__':
# Set up arguments
args = getArgs()
if not args.verbose:
eyed3.log.setLevel("ERROR")
if args.ffmpeg:
input_ext = args.input_ext
output_ext = args.output_ext
else:
input_ext = '.m4a'
output_ext = '.mp3'
if args.no_convert:
print("-n, --no-convert skip the conversion process and meta-tags")
if args.manual:
print("-m, --manual choose the song to download manually")
print('')
if args.list:
grabList(file='list.txt')
exit()
spotifyDownload()
if args.song:
grabSingle(raw_song=args.song)
elif args.list:
grabList(file=args.list)
elif args.username:
feedPlaylist(username=args.username)