-
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
Add parentwork plugin #2580
Add parentwork plugin #2580
Conversation
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
beetsplug/parentwork.py
Outdated
item['parent_composer'] = parent_composer | ||
item['parent_composer_sort'] = parent_composer_sort | ||
|
||
item.try_write() |
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.
If you're not planning on adding actual file tags for these fields, there's no need to call the write method.
Added flexible fields and corrected the typo of the tag: it was a list before for each tag, now it is a string. Also cleaned up a bit, but there is probably some whitespace to remove still or lines to break.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
It seems that I should have lines shorter than 79 chars. However, I have so many nested loops or tests that this is hardly possible without removing the multiple tests for reaching the MB server. Normally, when beets tries to fetch something from MB using musicbrainzngs it has 8 tries and raises an error if it still doesn't work. However, for me, 8 tries are usually not enough (it doesn't work about half the times ) so I added more tries. This will be discarded because it creates two supplementary indentation blocks.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Put the search of the parentwork in a separate function, to allow to make longer lines.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@sampsyo it seems like the tests are finally running fine. However, each time I want to edit the changelog I get a merging conflict, do you know why, or is it just that I didn't touch it for so long that I don't know how to use git anymore? Once this is cleared, could this be the moment to merge it? |
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.
Thanks for updating your changes!
Your PR is trying to add 191 commits to beets, which seems like rather a lot. Could you look into rebasing to squash some of your intermediate commits? Perhaps hidden by this is that you've changed the file mode on some unrelated scripts like extra/release.py
, which doesn't seem intended.
We reorganised the changelog file recently and something related to that is resulting in a confusing diff where it looks like your PR is re-applying those changes rather than just adding a new line. Something doesn't look right with the changelog.
EDIT: I realise you and Adrian discussed the commit count earlier, but I think the current situation is not ideal at least for commits that touch core files in beets. Sometimes we need to dig through the history of a file to debug or port changes, and it's made more difficult when the commit history contains many intermediate commits.
item.track_alt = track_info.track_alt | ||
|
||
# Miscellaneous/nullable metadata. | ||
misc_fields = { |
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.
Minor: this seems like it should be 2 variables misc_album_fields
and misc_item_fields
rather than a dict.
MP4StorageStyle('----:com.apple.iTunes:Work'), | ||
StorageStyle('WORK'), | ||
ASFStorageStyle('beets/Work'), | ||
) |
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.
We're planning to split MediaFile out into a standalone library so this addition will also need to be ported there. I can help with that if you like.
MP3DescStorageStyle(u'Work'), | ||
MP4StorageStyle('----:com.apple.iTunes:Work'), | ||
StorageStyle('WORK'), | ||
ASFStorageStyle('beets/Work'), |
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.
We probably don't want anything beets-specific in the storage style.
|
||
def find_parentwork(work_id): | ||
"""This function gives the work relationships (dict) of a parent_work | ||
given the id of the work""" |
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.
The docstring style used by beets needs the final """
to be on its own line.
classical music, just fetching the work title as in MusicBrainz is not | ||
satisfying, because MusicBrainz has separate works for, for example, all the | ||
movements of a symphony. This plugin aims to solve this problem by not only | ||
fetching the work as in MusicBrainz but also his parentwork which would be, |
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.
fetching the work as in MusicBrainz but also his parentwork which would be, | |
fetching the work itself from MusicBrainz but also its parentwork which would be, |
Parentwork Plugin | ||
================= | ||
|
||
The `parentwork` plugin fetches the work title, parentwork title and |
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.
The `parentwork` plugin fetches the work title, parentwork title and | |
The ``parentwork`` plugin fetches the work title, parentwork title and |
If there are several works linked to a recording, they all get a | ||
disambiguation (empty as default) and if all disambiguations are empty, the | ||
disambiguation field is left empty, else the disambiguation field can look | ||
like `,disambig,,` (if there are four works and only the second has a |
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.
like `,disambig,,` (if there are four works and only the second has a | |
like ``,disambig,,`` (if there are four works and only the second has a |
item.artist + ' - ' + item.title) | ||
self._log.debug("Work fetched: " + u', '.join(parent_work) + | ||
' - ' + u', '.join(parent_composer)) | ||
item['parent_work'] = u'' |
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.
Lines like there aren't necessary since u', '.join([]) == u''
try: | ||
item.parent_work | ||
hasparent = True | ||
except AttributeError: |
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.
Is an AttributeError
really raised when accessing a flexible field?
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.
Thanks for continuing to make progress on this effort!
At a very high level, what do you think about splitting up PRs for the new core functionality and for the plugin? I realize that one depends on the other, but this would make for somewhat easier reviewing and could be part of the flattening that @arcresu brought up (and which I agree might be a good idea at this point).
|
||
|
||
def work_father(work_id, work_date=None): | ||
""" This function finds the id of the father work given its id""" |
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.
It seems like this docstring should fully describe the return type: namely, it's a pair of an ID and a date. It's also probably worth mentioning that this communicates with MusicBrainz.
work_info = musicbrainzngs.get_work_by_id(work_id, | ||
includes=["work-rels", | ||
"artist-rels"]) | ||
if 'artist-relation-list' in work_info['work'] and work_date is None: |
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.
A comment here could be useful to point out that what we're doing is filling in work_date
if it's unspecified (how?).
"""This function finds the parentwork id of a work given its id. """ | ||
work_date = None | ||
while True: | ||
(new_work_id, work_date) = work_father(work_id, work_date) |
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.
No parentheses are necessary on the left-hand side.
(new_work_id, work_date) = work_father(work_id, work_date) | ||
if not new_work_id: | ||
return work_id, work_date | ||
break |
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.
This break
will never be executed.
and work_father.get('direction') == 'backward': | ||
father_id = work_father['work']['id'] | ||
return father_id, work_date | ||
return None, work_date |
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.
What does it mean to return None
as the first part of the tuple here?
self._command.parser.add_option( | ||
u'-f', u'--force', dest='force', | ||
action='store_true', default=None, | ||
help=u'Re-fetches all parent works') |
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.
As above.
composer_ids): | ||
"""Given the parentwork info dict, this function updates | ||
parent_composer, parent_composer_sort, parent_work, | ||
parent_work_disambig, work_ids and composer_ids""" |
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'd prefer for this function to return information rather than modifying its arguments. The caller can then update all the arrays to collect the data across multiple works.
self._log.info(item.artist + ' - ' + item.title) | ||
self._log.info( | ||
"no composer, add one at https://musicbrainz.org/work/" + | ||
work_info['work']['id']) |
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 don't think we need this log message—if anything, this can go to the debug log.
item['parent_composer'] = u', '.join(parent_composer) | ||
item['parent_composer_sort'] = u'' | ||
item['parent_composer_sort'] = u', '.join(parent_composer_sort) | ||
if not (work_date is None): |
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.
if not (work_date is None): | |
if work_date: |
' - ' + u', '.join(parent_composer)) | ||
item['parent_work'] = u'' | ||
item['parent_work'] = u', '.join(parent_work) | ||
if len(parent_work_disambig) > 0: |
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.
if len(parent_work_disambig) > 0: | |
if parent_work_disambig: |
Hi! Thanks for the feedback! I'm not familiar with git or github, so I tried rebasing, but without success I think. Could an easy solution be for me to restart from scratch and decompose it accordingly into different PRs? Splitting between changes to the core and the plugin might be tricky considering that the plugin relies on the changes to the core. |
New PRs would work just fine! You could even start with the core fields PR and base the plugin PR on that branch to encode the fact that the latter depends on the former. |
Add a plugin that is able to fetch a performer, his sort name, the work, the work it is part of ( as explained in #2452 ) and the respective disambiguations, composer names and sort names.
This plugin doesn't work in its current form, I don't understand why (there is certainly a valuable reason and an easy fix, I'm just completely helpless when confronted with SQL).
I would be interested in additional featues or options for this plugin.
What it should do so far is:
fetch the performers of the recording
fetch the work title and disambiguation
look if the work is part of a bigger one, if yes take the work it is part of, repeat
fetch the parent work (which is part of no work) and disambiguation
fetch its composer (sort)name
Afaict the fetch part works fine, it's just the writing part that doesn't.
So far, I would only put the tags into the library, but if there is an actual tag out there where it can be written in the file itself I would be interested.