├── .gitignore ├── LICENSE ├── README.md └── luooder.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | .DS_Store 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Shusen Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # luooder lol 2 | a song downloader from luoo.net built with python 3 | 4 | > note: python 2.7 is required 5 | 6 | ## how to use it 7 | 1. make sure [requests](http://docs.python-requests.org/en/latest/) and [beautifulsoup](http://www.crummy.com/software/BeautifulSoup/) are installed ```pip install beautifulsoup4 pip install requests ``` 8 | 2. ```python luooder.py``` and follow the instruction 9 | 3. get a cup of coffee, and wait... 10 | 11 | 12 | _Note: make sure you are not behind of proxy_ 13 | 14 | __TODO__ 15 | 16 | 1. use ```songdetails``` to add song metadata 17 | 2. add multi-thread downloading support 18 | 3. more todos 19 | 20 | 21 | __Known Issue__ 22 | 23 | 1. Some albums are not accessible via this tool [#1](https://github.com/shuson/luooder/issues/1) 24 | -------------------------------------------------------------------------------- /luooder.py: -------------------------------------------------------------------------------- 1 | # author: Nevermoi (wlgq@hotmail.com) 2 | # date: 15-Jan-2016 3 | 4 | import os 5 | import sys 6 | import requests 7 | #import songdetails 8 | import unicodedata 9 | 10 | from bs4 import BeautifulSoup 11 | 12 | LUOO_URL = "http://www.luoo.net/music/{}" 13 | MP3_URL = "http://luoo-mp3.kssws.ks-cdn.com/low/luoo/radio{}/{}.mp3" 14 | 15 | TRACK_NAME = "{} -{}.mp3" 16 | 17 | proxies = {} 18 | 19 | reload(sys) 20 | sys.setdefaultencoding('utf-8') 21 | 22 | class bcolors: 23 | HEADER = '\033[95m' 24 | OKBLUE = '\033[94m' 25 | OKGREEN = '\033[92m' 26 | WARNING = '\033[93m' 27 | FAIL = '\033[91m' 28 | ENDC = '\033[0m' 29 | BOLD = '\033[1m' 30 | UNDERLINE = '\033[4m' 31 | 32 | def full2half(uc): 33 | """convert full-width character to half-width one 34 | """ 35 | return unicodedata.normalize('NFKC', uc) 36 | 37 | def get_song_list(volumn): 38 | r = requests.get(LUOO_URL.format(volumn)) 39 | r.encoding = 'utf-8' 40 | 41 | bs = BeautifulSoup(r.content, 'html.parser') 42 | songs = bs.find_all('div', 'player-wrapper') 43 | 44 | print bcolors.OKBLUE + "song list of volumn {}:".format(volumn) + bcolors.ENDC 45 | result = [] 46 | for song in songs: 47 | meta = {} 48 | meta['name'] = song.find('p', 'name').getText() 49 | meta['artist'] = song.find('p', 'artist').getText()[7:] 50 | meta['album'] = song.find('p', 'album').getText()[6:] 51 | print bcolors.UNDERLINE + '{} by {}'.format(meta['name'], meta['artist']) + bcolors.ENDC 52 | result.append(meta) 53 | 54 | return result 55 | 56 | def download_songs(volumn): 57 | songs = get_song_list(volumn) 58 | if len(songs) == 0: 59 | print bcolors.WARNING + 'please make sure the volumn exists in luoo.net' + bcolors.ENDC 60 | return 61 | 62 | if not os.path.exists(str(volumn)): 63 | os.makedirs(str(volumn)) 64 | os.chdir(str(volumn)) 65 | 66 | print bcolors.OKBLUE + 'downloading...' + bcolors.ENDC 67 | index = 0 68 | for song in songs: 69 | index += 1 70 | track = '%02d' % index 71 | r = requests.get(MP3_URL.format(volumn,track), stream=True, proxies=proxies) 72 | r.encoding = 'utf-8' 73 | track_name = TRACK_NAME.format(song['name'], song['artist']) 74 | with open(track_name, 'wb') as fd: 75 | for chunk in r.iter_content(): 76 | fd.write(chunk) 77 | fd.close() 78 | print bcolors.OKGREEN + "{} is downloaded.".format(song['name']) + bcolors.ENDC 79 | 80 | if __name__ == '__main__': 81 | 82 | while True: 83 | print bcolors.HEADER + "input the volumn number to download all songs within it!\n>" + bcolors.ENDC 84 | vol = raw_input() 85 | if str(vol).isdigit(): 86 | print bcolors.BOLD + 'initiating...' + bcolors.ENDC 87 | download_songs(vol) 88 | break 89 | 90 | --------------------------------------------------------------------------------