Advertisement
jflory7

py2 => py3 for lastexport.py

Jun 2nd, 2016
158
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.78 KB | None | 0 0
  1. #!/usr/bin/env python
  2. #-*- coding: utf-8 -*-
  3.  
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program.  If not, see <http://www.gnu.org/licenses/>.
  16. #
  17.  
  18. """
  19. Script for exporting tracks through audioscrobbler API.
  20. Usage: lastexport.py -u USER [-o OUTFILE] [-p STARTPAGE] [-s SERVER]
  21. """
  22.  
  23. import sys, time, re
  24. import xml.etree.ElementTree as ET
  25. from optparse import OptionParser
  26. from urllib.parse import urlencode
  27. from urllib.request import urlopen
  28.  
  29. __version__ = '0.0.4'
  30.  
  31. def get_options(parser):
  32.     """ Define command line options."""
  33.     parser.add_option("-u", "--user", dest="username", default=None,
  34.                       help="User name.")
  35.     parser.add_option("-o", "--outfile", dest="outfile", default="exported_tracks.txt",
  36.                       help="Output file, default is exported_tracks.txt")
  37.     parser.add_option("-p", "--page", dest="startpage", type="int", default="1",
  38.                       help="Page to start fetching tracks from, default is 1")
  39.     parser.add_option("-s", "--server", dest="server", default="last.fm",
  40.                       help="Server to fetch track info from, default is last.fm")
  41.     parser.add_option("-t", "--type", dest="infotype", default="scrobbles",
  42.                       help="Type of information to export, scrobbles|loved|banned, default is scrobbles")
  43.     options, args = parser.parse_args()
  44.  
  45.     if not options.username:
  46.         sys.exit("User name not specified, see --help")
  47.  
  48.     if options.infotype == "loved":
  49.         infotype = "lovedtracks"
  50.     elif options.infotype == "banned":
  51.         infotype = "bannedtracks"
  52.     else:
  53.         infotype = "recenttracks"
  54.          
  55.     return options.username, options.outfile, options.startpage, options.server, infotype
  56.  
  57. def connect_server(server, username, startpage, sleep_func=time.sleep, tracktype='recenttracks'):
  58.     """ Connect to server and get a XML page."""
  59.     if server == "libre.fm":
  60.         baseurl = 'http://alpha.libre.fm/2.0/?'
  61.         urlvars = dict(method='user.get%s' % tracktype,
  62.                     api_key=('lastexport.py-%s' % __version__).ljust(32, '-'),
  63.                     user=username,
  64.                     page=startpage,
  65.                     limit=200)
  66.  
  67.     elif server == "last.fm":
  68.         baseurl = 'http://ws.audioscrobbler.com/2.0/?'
  69.         urlvars = dict(method='user.get%s' % tracktype,
  70.                     api_key='e38cc7822bd7476fe4083e36ee69748e',
  71.                     user=username,
  72.                     page=startpage,
  73.                     limit=50)
  74.     else:
  75.         if server[:7] != 'http://':
  76.             server = 'http://%s' % server
  77.         baseurl = server + '/2.0/?'
  78.         urlvars = dict(method='user.get%s' % tracktype,
  79.                     api_key=('lastexport.py-%s' % __version__).ljust(32, '-'),
  80.                     user=username,
  81.                     page=startpage,
  82.                     limit=200)
  83.  
  84.     url = baseurl + urlencode(urlvars)
  85.     for interval in (1, 5, 10, 62):
  86.         try:
  87.             f = urlopen(url)
  88.             break
  89.         except Exception as e:
  90.             last_exc = e
  91.             print ("Exception occured, retrying in %ds: %s" % (interval, e))
  92.             sleep_func(interval)
  93.     else:
  94.         print ("Failed to open page %s" % urlvars['page'])
  95.         raise last_exc
  96.  
  97.     response = f.read()
  98.     f.close()
  99.  
  100.     #bad hack to fix bad xml
  101.     response = re.sub(b'\xef\xbf\xbe', '', response)
  102.     return response
  103.  
  104. def get_pageinfo(response, tracktype='recenttracks'):
  105.     """Check how many pages of tracks the user have."""
  106.     xmlpage = ET.fromstring(response)
  107.     totalpages = xmlpage.find(tracktype).attrib.get('totalPages')
  108.     return int(totalpages)
  109.  
  110. def get_tracklist(response):
  111.     """Read XML page and get a list of tracks and their info."""
  112.     xmlpage = ET.fromstring(response)
  113.     tracklist = xmlpage.getiterator('track')
  114.     return tracklist
  115.  
  116. def parse_track(trackelement):
  117.     """Extract info from every track entry and output to list."""
  118.     if trackelement.find('artist').getchildren():
  119.         #artist info is nested in loved/banned tracks xml
  120.         artistname = trackelement.find('artist').find('name').text
  121.         artistmbid = trackelement.find('artist').find('mbid').text
  122.     else:
  123.         artistname = trackelement.find('artist').text
  124.         artistmbid = trackelement.find('artist').get('mbid')
  125.  
  126.     if trackelement.find('album') is None:
  127.         #no album info for loved/banned tracks
  128.         albumname = ''
  129.         albummbid = ''
  130.     else:
  131.         albumname = trackelement.find('album').text
  132.         albummbid = trackelement.find('album').get('mbid')
  133.  
  134.     trackname = trackelement.find('name').text
  135.     trackmbid = trackelement.find('mbid').text
  136.     date = trackelement.find('date').get('uts')
  137.  
  138.     output = [date, trackname, artistname, albumname, trackmbid, artistmbid, albummbid]
  139.  
  140.     for i, v in enumerate(output):
  141.         if v is None:
  142.             output[i] = ''
  143.  
  144.     return output
  145.  
  146. def write_tracks(tracks, outfileobj):
  147.     """Write tracks to an open file"""
  148.     for fields in tracks:
  149.         outfileobj.write(bytes("\t".join(fields) + "\n").encode('utf-8'))
  150.  
  151. def get_tracks(server, username, startpage=1, sleep_func=time.sleep, tracktype='recenttracks'):
  152.     page = startpage
  153.     response = connect_server(server, username, page, sleep_func, tracktype)
  154.     totalpages = get_pageinfo(response, tracktype)
  155.  
  156.     if startpage > totalpages:
  157.         raise ValueError("First page (%s) is higher than total pages (%s)." % (startpage, totalpages))
  158.  
  159.     while page <= totalpages:
  160.         #Skip connect if on first page, already have that one stored.
  161.  
  162.         if page > startpage:
  163.             response =  connect_server(server, username, page, sleep_func, tracktype)
  164.  
  165.         tracklist = get_tracklist(response)
  166.        
  167.         tracks = []
  168.         for trackelement in tracklist:
  169.             # do not export the currently playing track.
  170.             if "nowplaying" not in trackelement:
  171.                 tracks.append(parse_track(trackelement))
  172.  
  173.         yield page, totalpages, tracks
  174.  
  175.         page += 1
  176.         sleep_func(.5)
  177.  
  178. def main(server, username, startpage, outfile, infotype='recenttracks'):
  179.     trackdict = dict()
  180.     page = startpage  # for case of exception
  181.     totalpages = -1  # ditto
  182.     n = 0
  183.     try:
  184.         for page, totalpages, tracks in get_tracks(server, username, startpage, tracktype=infotype):
  185.             print ("Got page %s of %s.." % (page, totalpages))
  186.             for track in tracks:
  187.                 if infotype == 'recenttracks':
  188.                     trackdict.setdefault(track[0], track)
  189.                 else:
  190.                     #Can not use timestamp as key for loved/banned tracks as it's not unique
  191.                     n += 1
  192.                     trackdict.setdefault(n, track)
  193.     except ValueError as e:
  194.         exit(e)
  195.     except Exception:
  196.         raise
  197.     finally:
  198.         with open(outfile, 'a') as outfileobj:
  199.             tracks = sorted(trackdict.values(), reverse=True)
  200.             write_tracks(tracks, outfileobj)
  201.             print ("Wrote page %s-%s of %s to file %s" % (startpage, page, totalpages, outfile))
  202.  
  203. if __name__ == "__main__":
  204.     parser = OptionParser()
  205.     username, outfile, startpage, server, infotype = get_options(parser)
  206.     main(server, username, startpage, outfile, infotype)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement