Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
f637a97fc2 | |||
f6f3a653df | |||
b63e1f1536 | |||
f799d31052 | |||
abbf9df872 | |||
a0a84a212f | |||
679bcdd633 | |||
029af27ea2 | |||
4c1480be4c | |||
f2d7ad058c |
14
.editorconfig
Normal file
14
.editorconfig
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[{*.json,config.json.example}]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
*.json
|
*.json
|
||||||
|
*.csv
|
||||||
*.pyc
|
*.pyc
|
||||||
build/
|
build/
|
||||||
dist/
|
dist/
|
||||||
|
35
README.md
35
README.md
@ -1,19 +1,26 @@
|
|||||||

|
# Last.fm Backup
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
**How to use:**
|

|
||||||
--------------
|

|
||||||
* [Get](http://www.last.fm/api/account/create) API Key.
|

|
||||||
|
|
||||||
|
### Features:
|
||||||
|
* Simple
|
||||||
|
* Three formats for export data:
|
||||||
|
1. `dump` - JSON, direct from API
|
||||||
|
2. `simple` - Artist, track name, album & date as JSON
|
||||||
|
3. `csv` - Artist, track name, album & date as comma separated values
|
||||||
|
|
||||||
|
### How to use:
|
||||||
* Stop scrobbling!
|
* Stop scrobbling!
|
||||||
* Rename `config.json.example` to `config.json` and edit.
|
* Modify `config.json`.
|
||||||
* Run script `lastfm_backup.py`.
|
* [Get API Key](http://www.last.fm/api/account/create).
|
||||||
|
* Run `lastfm_backup.py`.
|
||||||
* WAIT =)
|
* WAIT =)
|
||||||
|
|
||||||
**TODO:**
|
### TODO:
|
||||||
--------
|
- [ ] web service [see lfmbak](https://github.com/iiiypuk/lfmbak)
|
||||||
- [ ] web service
|
- [x] more output types (sqlite, csv)
|
||||||
- [ ] more output types (sqlite, csv)
|
- [ ] configurable output
|
||||||
- [ ] confirugurabled output
|
|
||||||
- [ ] continue backup
|
- [ ] continue backup
|
||||||
- [ ] multithreading
|
- [ ] multi-threading
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"username" : "",
|
"username" : "",
|
||||||
"api_key" : ""
|
"api_key" : "",
|
||||||
|
"export_format": "dump, simple, csv"
|
||||||
}
|
}
|
||||||
|
159
lastfm_backup.py
159
lastfm_backup.py
@ -1,61 +1,138 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import urllib.request
|
import csv
|
||||||
import os.path
|
import os.path
|
||||||
|
import requests
|
||||||
|
|
||||||
__author__ = 'Alexander Popov'
|
__author__ = "Alexander Popov"
|
||||||
__version__ = '1.0.1'
|
__version__ = "2.1.0"
|
||||||
__license__ = 'Unlicense'
|
__license__ = "Unlicense"
|
||||||
|
|
||||||
|
|
||||||
def get_pages(username, api_key):
|
def get_pages(username, api_key, limit=200):
|
||||||
response = urllib.request.urlopen(
|
""" Getting the number of pages with scrobbling data """
|
||||||
'http://ws.audioscrobbler.com/2.0/'
|
|
||||||
'?method=user.getrecenttracks&user={0}&api_key={1}&format=json'
|
|
||||||
'&limit=200'.format(username, api_key)).read().decode("utf8")
|
|
||||||
pages = int(json.loads(response)['recenttracks']['@attr']['totalPages'])
|
|
||||||
|
|
||||||
return(pages)
|
response = requests.get(
|
||||||
|
"https://ws.audioscrobbler.com/2.0/"
|
||||||
|
"?method=user.getrecenttracks&user="
|
||||||
|
"{0}&api_key={1}&format=json&limit={2}".format(username, api_key, limit)
|
||||||
|
)
|
||||||
|
|
||||||
|
data = json.loads(response.content.decode("utf8"))
|
||||||
|
|
||||||
|
return int(data["recenttracks"]["@attr"]["totalPages"])
|
||||||
|
|
||||||
|
|
||||||
def get_scrobbles(username, api_key, page):
|
def get_page(username, api_key, page, limit=200):
|
||||||
response = json.loads(urllib.request.urlopen(
|
""" Getting scrobbling data from a page """
|
||||||
'http://ws.audioscrobbler.com/2.0/'
|
|
||||||
'?method=user.getrecenttracks&user={0}&api_key={1}&format=json'
|
|
||||||
'&limit=200&page={2}'.format(username, api_key, page)
|
|
||||||
).read().decode("utf8"))['recenttracks']['track']
|
|
||||||
|
|
||||||
return(response)
|
response = requests.get(
|
||||||
|
"https://ws.audioscrobbler.com/2.0/"
|
||||||
|
"?method=user.getrecenttracks&user={0}&api_key={1}&format=json"
|
||||||
|
"&limit={2}&page={3}".format(username, api_key, limit, page)
|
||||||
|
)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
data = json.loads(response.content.decode("utf8"))
|
||||||
CFG = dict()
|
|
||||||
|
|
||||||
if os.path.exists('./config.json'):
|
return data["recenttracks"]["track"]
|
||||||
with open('./config.json') as f:
|
|
||||||
CFG = json.loads(f.read())
|
|
||||||
|
def get_now_scrobbling(username, api_key):
|
||||||
|
""" Getting now scrobbling track """
|
||||||
|
|
||||||
|
response = requests.get(
|
||||||
|
"https://ws.audioscrobbler.com/2.0/"
|
||||||
|
"?method=user.getrecenttracks&user={0}"
|
||||||
|
"&api_key={1}&format=json&limit=1".format(username, api_key)
|
||||||
|
)
|
||||||
|
|
||||||
|
data = json.loads(response.content.decode("utf-8"))
|
||||||
|
|
||||||
|
if "@attr" in data["recenttracks"]["track"][0]:
|
||||||
|
return True
|
||||||
else:
|
else:
|
||||||
CFG['api_key'] = input('API Key: ')
|
return False
|
||||||
CFG['username'] = input('Username: ')
|
|
||||||
|
|
||||||
PAGES = get_pages(CFG['username'], CFG['api_key'])
|
|
||||||
curPage = 1
|
def scrobbling_export(tracks, username, export_format="dump"):
|
||||||
tracks = []
|
""" Save scrobbled track via various format """
|
||||||
while curPage <= PAGES:
|
|
||||||
print('\r{0}% [{1} of {2}]'.format((curPage * 100 / PAGES), curPage, PAGES), end='')
|
if export_format == "dump":
|
||||||
response = get_scrobbles(CFG['username'], CFG['api_key'], curPage)
|
with open("%s.json" % (username), "w", encoding="utf-8") as f:
|
||||||
|
data = json.dumps(tracks, indent=4, sort_keys=True, ensure_ascii=False)
|
||||||
|
f.write(data)
|
||||||
|
|
||||||
|
elif export_format == "simple":
|
||||||
|
_ = []
|
||||||
|
|
||||||
|
for track in tracks:
|
||||||
|
_.append(
|
||||||
|
{
|
||||||
|
"artist": track["artist"]["#text"],
|
||||||
|
"name": track["name"],
|
||||||
|
"album": track["album"]["#text"],
|
||||||
|
"date": int(track["date"]["uts"]),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
with open("%s.json" % (username), "w", encoding="utf-8") as f:
|
||||||
|
data = json.dumps(_, indent=4, sort_keys=True, ensure_ascii=False)
|
||||||
|
f.write(data)
|
||||||
|
|
||||||
|
elif export_format == "csv":
|
||||||
|
_ = []
|
||||||
|
|
||||||
|
for track in tracks:
|
||||||
|
_.append(
|
||||||
|
[
|
||||||
|
track["artist"]["#text"],
|
||||||
|
track["name"],
|
||||||
|
track["album"]["#text"],
|
||||||
|
int(track["date"]["uts"]),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
with open("%s.csv" % (username), "w", encoding="utf-8", newline="") as f:
|
||||||
|
data = csv.writer(f, quoting=csv.QUOTE_NONNUMERIC, delimiter=",")
|
||||||
|
data.writerow(["artist", "track", "album", "date"])
|
||||||
|
data.writerows(_)
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
_ = dict()
|
||||||
|
|
||||||
|
if os.path.exists("./config.json"):
|
||||||
|
with open("./config.json") as f:
|
||||||
|
_ = json.loads(f.read())
|
||||||
|
else:
|
||||||
|
_["api_key"] = input("API Key: ")
|
||||||
|
_["username"] = input("Username: ")
|
||||||
|
api_key, username = _["api_key"], _["username"]
|
||||||
|
|
||||||
|
total_pages = get_pages(username, api_key)
|
||||||
|
current_page = 1
|
||||||
|
scrobbled = []
|
||||||
|
|
||||||
|
while current_page <= total_pages:
|
||||||
|
print(
|
||||||
|
"\r{0:.2f}% [{1} of {2}]".format(
|
||||||
|
(current_page * 100 / total_pages), current_page, total_pages
|
||||||
|
),
|
||||||
|
end="",
|
||||||
|
)
|
||||||
|
|
||||||
|
response = get_page(username, api_key, current_page)
|
||||||
|
|
||||||
for track in response:
|
for track in response:
|
||||||
tracks.append({'artist': track['artist']['#text'],
|
scrobbled.append(track)
|
||||||
'name': track['name'],
|
|
||||||
'album': track['album']['#text'],
|
|
||||||
'date': track['date']['uts']})
|
|
||||||
|
|
||||||
curPage += 1
|
current_page += 1
|
||||||
|
|
||||||
with open('%s.json' % (CFG['username']), 'w+', encoding='utf-8') as f:
|
# if get_now_scrobbling(username, api_key):
|
||||||
f.write(
|
# scrobbled.pop(0)
|
||||||
json.dumps(tracks, indent=4, sort_keys=True, ensure_ascii=False))
|
|
||||||
|
|
||||||
print('\n{0} tracks saved in {1}.json!'.format(
|
if scrobbling_export(scrobbled, username, _["export_format"]):
|
||||||
len(tracks), CFG['username'],))
|
print("\n{0} tracks saved!".format(len(scrobbled), username))
|
||||||
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
requests==2.24.0
|
@ -2,18 +2,24 @@
|
|||||||
|
|
||||||
import lastfm_backup as lfm
|
import lastfm_backup as lfm
|
||||||
|
|
||||||
API_KEY = '0'
|
API_KEY = "0"
|
||||||
USERNAME = 'admin'
|
USERNAME = "admin"
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
pages = lfm.get_pages(USERNAME, API_KEY)
|
pages = lfm.get_pages(USERNAME, API_KEY)
|
||||||
count = 0
|
count = 0
|
||||||
|
|
||||||
for page in range(1, pages + 1):
|
for page in range(1, pages + 1):
|
||||||
tracks = lfm.get_scrobbles(USERNAME, API_KEY, page)
|
tracks = lfm.get_scrobbles(USERNAME, API_KEY, page)
|
||||||
|
|
||||||
for track in tracks:
|
for track in tracks:
|
||||||
print('%s - %s' % (track['artist']['#text'], track['name'],))
|
print(
|
||||||
count = count + 1
|
"%s - %s"
|
||||||
|
% (
|
||||||
|
track["artist"]["#text"],
|
||||||
|
track["name"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
count = count + 1
|
||||||
|
|
||||||
print('\nTotal scrobbling tracks: %d' % count)
|
print("\nTotal scrobbling tracks: %d" % count)
|
||||||
|
26
setup.py
26
setup.py
@ -3,19 +3,21 @@ import setuptools
|
|||||||
import lastfm_backup
|
import lastfm_backup
|
||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
name='lastfm-backup',
|
name="lastfm-backup",
|
||||||
version=lastfm_backup.__version__,
|
version=lastfm_backup.__version__,
|
||||||
description='Last.fm scrobbles backup',
|
description="Last.fm scrobbling backup",
|
||||||
author=lastfm_backup.__author__,
|
author=lastfm_backup.__author__,
|
||||||
author_email='iiiypuk@fastmail.fm',
|
author_email="iiiypuk@fastmail.fm",
|
||||||
url='https://github.com/iiiypuk/lastfm-backup',
|
url="https://github.com/iiiypuk/lastfm-backup",
|
||||||
py_modules=['lastfm_backup'],
|
py_modules=["lastfm_backup"],
|
||||||
scripts=['lastfm_backup.py'],
|
scripts=["lastfm_backup.py"],
|
||||||
license=lastfm_backup.__license__,
|
license=lastfm_backup.__license__,
|
||||||
platforms='any',
|
platforms="any",
|
||||||
keywords=['last.fm', 'lastfm', 'backup'],
|
keywords=["last.fm", "lastfm", "backup"],
|
||||||
classifiers=['License :: Public Domain',
|
classifiers=[
|
||||||
'Programming Language :: Python :: 3',
|
"License :: Public Domain",
|
||||||
'Operating System :: OS Independent'],
|
"Programming Language :: Python :: 3",
|
||||||
python_requires='>=3.0'
|
"Operating System :: OS Independent",
|
||||||
|
],
|
||||||
|
python_requires=">=3.2",
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user