Advertisement
Guest User

Untitled

a guest
Sep 20th, 2014
197
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.30 KB | None | 0 0
  1. #!/usr/bin/env python
  2.  
  3. try:
  4.     # python3
  5.     from urllib.request import urlopen, urlretrieve
  6.     from urllib.parse import urlparse, urlencode, urlunparse
  7. except ImportError:
  8.     # python2
  9.     from urllib import urlencode, urlretrieve
  10.     from urllib2 import urlopen
  11.     from urlparse import urlparse, urlunparse
  12.  
  13. import argparse
  14. import json
  15. import sys
  16. import os
  17. import re
  18.  
  19. from PySide.QtCore import *
  20. from PySide.QtGui import *
  21.  
  22.  
  23. BASEDIR = os.path.dirname(os.path.realpath(__file__))
  24. CONFIG_NAME = 'config'
  25.  
  26.  
  27. class Logger(object):
  28.  
  29.     """ Logging information to console or other sources (TODO) """
  30.  
  31.     def __init__(self, console=True):
  32.         super(Logger, self).__init__()
  33.         self.console = console
  34.  
  35.     def notify(self, message, end='\n'):
  36.         if self.console:
  37.             # sys.stdout.flush()
  38.             sys.stdout.write('%s%s' % (message, end))
  39.         else:
  40.             pass
  41.  
  42.  
  43. class StereoSong(object):
  44.  
  45.     """ Song implementation """
  46.  
  47.     def __init__(self, song):
  48.         super(StereoSong, self).__init__()
  49.         self.uuid = song['uuid']
  50.         self.artist = song["artist"]
  51.         self.title = song["songtitle"]
  52.         self.name = self.__get_name()
  53.         self.__percent = 0.0
  54.  
  55.     def __get_name(self):
  56.         """ Building name for song """
  57.         return re.sub(r'[/]', r'|', "{} - {}".format(self.artist, self.title))
  58.  
  59.     def get_url(self, client_id):
  60.         """ Building url for song """
  61.         return 'https://api.soundcloud.com/tracks/{}/stream?client_id={}'.format(self.uuid, client_id)
  62.  
  63.     @property
  64.     def percent(self):
  65.         return self.__percent
  66.  
  67.     @percent.setter
  68.     def percent(self, value):
  69.         self.__percent = min(100, max(0, value))
  70.  
  71.  
  72. class Config(object):
  73.  
  74.     """ Config model for StereodoseHandler """
  75.  
  76.     def __init__(self, logger):
  77.         super(Config, self).__init__()
  78.         self.__logger = logger
  79.         self.__dict__.update(self.get_config())
  80.  
  81.     def get_client_id(self):
  82.         """ Getting client_id from site for downloading songs """
  83.         with urlopen('https://www.stereodose.com') as data:
  84.             match = re.search(r'var\s+sc_client_id\s*=\s*"(\w+)";',
  85.                               data.read().decode(sys.getdefaultencoding()))
  86.         return match.group(1) if match else str()
  87.  
  88.     def generate_config(self):
  89.         """ Generating default config """
  90.         self.__logger.notify('Generating config')
  91.         songs_dir = os.path.join(BASEDIR, 'songs')
  92.  
  93.         # Creating songs directory if dont exists
  94.         if not os.path.exists(songs_dir):
  95.             self.__logger.notify('Creating songs directory')
  96.             os.makedirs(songs_dir)
  97.  
  98.         default = dict(
  99.             songs_directory=songs_dir,
  100.             client_id=get_client_id()
  101.         )
  102.         # Writing default key = value to config file
  103.         with open(os.path.join(BASEDIR, CONFIG_NAME), 'w') as config:
  104.             for key, value in default.items():
  105.                 config.write('{key} = {value}\n'.format(key=key, value=value))
  106.  
  107.     def get_config(self):
  108.         """ Reading and build config from file """
  109.         config_path = os.path.join(BASEDIR, CONFIG_NAME)
  110.  
  111.         # Creating config file
  112.         if not os.path.exists(config_path):
  113.             generate_config()
  114.  
  115.         # Reading config file, then building dict from list of tuple (key,
  116.         # value)
  117.         with open(config_path) as config_file:
  118.             config = config_file.read()
  119.  
  120.         match = re.findall(r'(\S+)\s*=\s*(\S+)', config, re.M)
  121.         return dict(match)
  122.  
  123.  
  124. class StereodoseHandler(object):
  125.  
  126.     """ Simple API for stereodose """
  127.  
  128.     def __init__(self, url, logger):
  129.         super(StereodoseHandler, self).__init__()
  130.         self.url = url
  131.         self.__config = Config(logger)
  132.         self.__logger = logger
  133.         self.songs = self.generate_songs()
  134.  
  135.     def generate_songs(self):
  136.         self.__logger.notify('Building songs list')
  137.         with urlopen(self.url) as data:
  138.             result = data.read().decode(sys.getdefaultencoding())
  139.  
  140.         def get_song_array():
  141.             match = re.search(r'var\s+songarray\s*=\s*(.+?);', result)
  142.             return json.loads(match.group(1)) if match else list()
  143.  
  144.         songs = [StereoSong(song) for song in get_song_array()]
  145.         return songs
  146.  
  147.     def download_songs(self):
  148.         songs_dir = self.__config.songs_directory
  149.  
  150.         for song in self.songs:
  151.             self.__logger.notify('Downloading: {}'.format(song.name))
  152.             songs_path = os.path.join(songs_dir, song.name)
  153.  
  154.             if os.path.isfile(songs_path):
  155.                 self.__logger.notify('Here already'.format(song.name))
  156.                 continue
  157.  
  158.             try:
  159.                 self.download_song(song)
  160.             except ValueError:
  161.                 os.remove(songs_path)
  162.  
  163.     def download_song(self, song):
  164.         songs_path = os.path.join(self.__config.songs_directory, song.name)
  165.         data = urlopen(song.get_url(self.__config.client_id))
  166.  
  167.         with open(songs_path, 'wb') as file:
  168.             meta = data.info()
  169.             file_size = int(meta.get("Content-Length"))
  170.  
  171.             file_size_dl, block_sz = 0, 8192
  172.             while True:
  173.                 buffer = data.read(block_sz)
  174.                 if not buffer:
  175.                     break
  176.  
  177.                 file_size_dl += len(buffer)
  178.                 file.write(buffer)
  179.                 status = file_size_dl * 100.0 / file_size
  180.                 song.percent = status
  181.  
  182.  
  183. class StereodoseGui(QWidget):
  184.  
  185.     """ GUI class for StereodoseHandler """
  186.  
  187.     def __init__(self, app):
  188.         super(StereodoseGui, self).__init__()
  189.         self.qt_app = app
  190.  
  191.         self.setMinimumSize(400, 185)
  192.         self.setWindowTitle('Stereodose Downloader')
  193.  
  194.     def run(self):
  195.         """ Show application window and start the main event loop """
  196.         self.show()
  197.         self.qt_app.exec_()
  198.  
  199.  
  200. def main():
  201.     arg_parser = argparse.ArgumentParser()
  202.     arg_parser.add_argument('-p', '--playlist', action='store')
  203.     args = arg_parser.parse_args()
  204.  
  205.     if args.playlist:
  206.         logger = Logger()
  207.         sd = StereodoseHandler(args.playlist, logger)
  208.         sd.download_songs()
  209.     else:
  210.         qt_app = QApplication(sys.argv)
  211.         StereodoseGui(qt_app).run()
  212.  
  213. if __name__ == '__main__':
  214.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement