-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MetaSync: more OO structure + iTunes support #1450
Changes from 2 commits
3dc2bed
04d7c88
27aef76
de5db70
cb13d21
bba8647
abd0205
afeedd2
16531b6
02bec1b
94edc7a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
# This file is part of beets. | ||
# Copyright 2015, Tom Jaspers. | ||
# | ||
# Permission is hereby granted, free of charge, to any person obtaining | ||
# a copy of this software and associated documentation files (the | ||
# "Software"), to deal in the Software without restriction, including | ||
# without limitation the rights to use, copy, modify, merge, publish, | ||
# distribute, sublicense, and/or sell copies of the Software, and to | ||
# permit persons to whom the Software is furnished to do so, subject to | ||
# the following conditions: | ||
# | ||
# The above copyright notice and this permission notice shall be | ||
# included in all copies or substantial portions of the Software. | ||
|
||
"""Synchronize information from iTunes's library | ||
""" | ||
from contextlib import contextmanager | ||
import os | ||
import shutil | ||
import tempfile | ||
from time import mktime | ||
|
||
import plistlib | ||
from beets import util | ||
from beets.util.confit import ConfigValueError | ||
|
||
|
||
@contextmanager | ||
def create_temporary_copy(path): | ||
temp_dir = tempfile.gettempdir() | ||
temp_path = os.path.join(temp_dir, 'temp_itunes_lib') | ||
shutil.copyfile(path, temp_path) | ||
try: | ||
yield temp_path | ||
finally: | ||
shutil.rmtree(temp_dir) | ||
|
||
|
||
class ITunes(object): | ||
|
||
def __init__(self, config=None): | ||
# Load the iTunes library, which has to be the .xml one | ||
library_path = util.normpath(config['itunes']['library'].get(str)) | ||
|
||
try: | ||
with create_temporary_copy(library_path) as library_copy: | ||
raw_library = plistlib.readPlist(library_copy) | ||
except IOError as e: | ||
raise ConfigValueError("invalid iTunes library: " + e.strerror) | ||
except: | ||
# TODO: Tell user to make sure it is the .xml one? | ||
raise ConfigValueError("invalid iTunes library") | ||
|
||
# Convert the library in to something we can query more easily | ||
self.collection = { | ||
(track['Name'], track['Album'], track['Album Artist']): track | ||
for track in raw_library['Tracks'].values()} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like this is looking up tracks by their metadata. Would it be possible/simpler to use their paths instead? Then we wouldn't run into any weird aliasing problems. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That was my original intention (and how I would prefer) it, but iTunes saves path in Unix style on all systems. This is conflicting with the way paths are saved in beets for Windows. In Itunes Library.xml: And in beets: I'm gonna try to resort to converting the iTunes path to match beets' style. Will take a while to test it properly on Windows system though, but using a path is probably better in the long run. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aha; that's really tricky. Yes, maybe converting iTunes's path on load is the way to go (perhaps just a I may be able to find my Windows VM somewhere to do some testing. |
||
|
||
def get_data(self, item): | ||
key = (item.title, item.album, item.albumartist) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tiny style thing: no parentheses are required on this tuple. |
||
result = self.collection.get(key) | ||
|
||
# TODO: Need to investigate behavior for items without title, album, or | ||
# albumartist before allowing them to be queried | ||
if not all(key) or not result: | ||
# TODO: Need to log something here later | ||
# print "No iTunes match found for {0}".format(item) | ||
return | ||
|
||
item.itunes_rating = result.get('Rating') | ||
item.itunes_playcount = result.get('Play Count') | ||
item.itunes_skipcount = result.get('Skip Count') | ||
|
||
if result.get('Play Date UTC'): | ||
item.itunes_lastplayed = mktime( | ||
result.get('Play Date UTC').timetuple()) | ||
|
||
if result.get('Skip Date'): | ||
item.itunes_lastskipped = mktime( | ||
result.get('Skip Date').timetuple()) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,11 +4,13 @@ MetaSync Plugin | |
This plugin provides the ``metasync`` command, which lets you fetch certain | ||
metadata from other sources: for example, your favorite audio player. | ||
|
||
Currently, the plugin supports synchronizing with the `Amarok`_ music player. | ||
Currently, the plugin supports synchronizing with the `Amarok`_ music player, | ||
and with `iTunes`_. | ||
It can fetch the rating, score, first-played date, last-played date, play | ||
count, and track uid from Amarok. | ||
|
||
.. _Amarok: https://amarok.kde.org/ | ||
.. _iTunes: https://www.apple.com/itunes/ | ||
|
||
|
||
Installation | ||
|
@@ -29,10 +31,23 @@ Configuration | |
To configure the plugin, make a ``metasync:`` section in your configuration | ||
file. The available options are: | ||
|
||
- **source**: A list of sources to fetch metadata from. Set this to "amarok" | ||
to enable synchronization with that player. | ||
- **source**: A list of sources to fetch metadata from. Set this to "amarok" or | ||
"itunes" to enable synchronization with that player. | ||
Default: empty | ||
|
||
The follow subsections describe additional configure required for some players. | ||
|
||
itunes | ||
'''''' | ||
|
||
The path to your iTunes library **xml** file has to be configured, e.g.:: | ||
|
||
metaysnc: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. metaysnc => metasync |
||
source: itunes | ||
itunes: | ||
library: ~/Music/iTunes Library.xml | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shall we provide a default location for the library XML? It seems like most users will probably have theirs here (at least on OS X). |
||
|
||
Please note the indentation. | ||
|
||
Usage | ||
----- | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think
.as_path()
instead of.get(str)
will obviate the need for thenormpath
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Has the
as_path()
method maybe been renamed or removed? I can't seem to find it anywhere.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops! Sorry; I was freehanding. I meant
as_filename
: https://github.com/sampsyo/beets/blob/master/beets/util/confit.py#L378