anon-iclr-submission/benchname-bug-localization huggingface dataset:
Repository: beetbox/beets
Issue: On Python 3, rarfile<3.0 does not support bytes filename

### Problem

Importing  rar files does not work anymore. It used to work with beets `1.4.2` and below.

The underlying problem is that `is_rarfile` does no accept bytes (and `toppath` variable is byte). See this commit https://github.com/markokr/rarfile/commit/7d7a2cbf04cf4572c26e38ba18b5fe0b049cd850 where future versions (once or twice a year) of `rarfile` will gladly accepts bytes.

Running this command in verbose (`-vv`) mode:

```sh
beet -vv import /mnt/E/Album\\ -\\ group.rar
```

Led to this problem:

```
user configuration: /home/lompik/.config/beets/config.yaml
data directory: /home/lompik/.config/beets
plugin paths: 
Sending event: pluginload
library database: /home/lompik/.data/musiklibrary.blb
library directory: /mnt/music
Sending event: library_opened
Sending event: import_begin
Extracting archive: /mnt/E/Album - group.rar
extraction failed: Invalid object passed as file
No files imported from /mnt/E/Album - group.rar
Sending event: import
Sending event: cli_exit
```



### Setup

* OS: Linux
* Python version: 3.6
* beets version: 1.4.3  / rafile version: 3.0
* Turning off plugins made problem go away (yes/no): no


EDIT: change filename

Changed files:
beets/importer.py

diff --git a/beets/importer.py b/beets/importer.py
index bbe152cd4..690a499ff 100644
--- a/beets/importer.py
+++ b/beets/importer.py
@@ -988,7 +988,7 @@ class ArchiveImportTask(SentinelImportTask):
         `toppath` to that directory.
         """
         for path_test, handler_class in self.handlers():
-            if path_test(self.toppath):
+            if path_test(util.py3_path(self.toppath)):
                 break
 
         try:
Repository: beetbox/beets
Issue: web: Crash when serializing objects with bytes fields

### Problem

For some reason the web interface is not working correctly. I can't actually get it to search for anything. When you type something into the search box nothing happens except for the following text being printed to the terminal.

Running this command in verbose (`-vv`) mode:

```sh
$ beet -vv web
```
Then searching for something in the web interface gives this problem:

```
~
➔ beet -vv web
 * Running on http://127.0.0.1:8337/ (Press CTRL+C to quit)
127.0.0.1 - - [30/Apr/2017 11:05:15] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [30/Apr/2017 11:05:22] "GET /item/query/isabel HTTP/1.1" 200 -
Error on request:
Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/werkzeug/serving.py", line 209, in run_wsgi
    execute(self.server.app)
  File "/usr/lib/python3.6/site-packages/werkzeug/serving.py", line 199, in execute
    for data in application_iter:
  File "/usr/lib/python3.6/site-packages/werkzeug/wsgi.py", line 704, in __next__
    return self._next()
  File "/usr/lib/python3.6/site-packages/werkzeug/wrappers.py", line 81, in _iter_encoded
    for item in iterable:
  File "/usr/lib/python3.6/site-packages/beetsplug/web/__init__.py", line 74, in json_generator
    yield json.dumps(_rep(item, expand=expand))
  File "/usr/lib/python3.6/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/usr/lib/python3.6/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python3.6/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/usr/lib/python3.6/json/encoder.py", line 180, in default
    o.__class__.__name__)
TypeError: Object of type 'bytes' is not JSON serializable
```

### Setup

* OS: Arch Linux
* Python version: Python 3.6
* beets version: 1.4.3
* Turning off plugins made problem go away (yes/no): no, the problem is with the web plugin

My configuration (output of `beet config`) is:

```yaml
directory: ~/Music
library: ~/Music/musicLibrary.db

import:
    move: yes

plugins: chroma web
acoustid:
    apikey: REDACTED
web:
    host: 127.0.0.1
    port: 8337
    cors: ''
chroma:
    auto: yes
```


Changed files:
beetsplug/web/__init__.py

diff --git a/beetsplug/web/__init__.py b/beetsplug/web/__init__.py
index 3c18ebd5d..c154b0cf4 100644
--- a/beetsplug/web/__init__.py
+++ b/beetsplug/web/__init__.py
@@ -25,6 +25,7 @@ from flask import g
 from werkzeug.routing import BaseConverter, PathConverter
 import os
 import json
+import base64
 
 
 # Utilities.
@@ -42,6 +43,11 @@ def _rep(obj, expand=False):
         else:
             del out['path']
 
+        # Filter all bytes attributes and convert them to strings
+        for key, value in out.items():
+            if isinstance(out[key], bytes):
+                out[key] = base64.b64encode(out[key]).decode('ascii')
+
         # Get the size (in bytes) of the backing file. This is useful
         # for the Tomahawk resolver API.
         try:
Repository: beetbox/beets
Issue: web: Use Unicode paths to send files on Windows under Python 2

Hi,

beets version 1.4.3
Python version 2.7.11
plugins: web

Python 2.7.11 (v2.7.11:6d1b6a68f775, Dec  5 2015, 20:32:19) [MSC v.1500 32 bit (Intel)] on win32

Following flask error was encountered when calling item/file method for non-ascii items:
```
[2017-06-14 21:36:27,233] ERROR in app: Exception on /item/52117/file [GET]
Traceback (most recent call last):
  File "c:\\python\\lib\\site-packages\\flask\\app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "c:\\python\\lib\\site-packages\\flask\\app.py", line 1614, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "c:\\python\\lib\\site-packages\\flask_cors\\extension.py", line 161, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
  File "c:\\python\\lib\\site-packages\\flask\\app.py", line 1517, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "c:\\python\\lib\\site-packages\\flask\\app.py", line 1612, in full_dispatch_request
    rv = self.dispatch_request()
  File "c:\\python\\lib\\site-packages\\flask\\app.py", line 1598, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "c:\\python\\lib\\site-packages\\beetsplug\\web\\__init__.py", line 209, in item_file
    attachment_filename=os.path.basename(util.py3_path(item.path)),
  File "c:\\python\\lib\\site-packages\\flask\\helpers.py", line 549, in send_file
    file = open(filename, 'rb')
IOError: [Errno 2] No such file or directory: 'PATH_REDACTED\\\\01 Marta Kubis\\xcc\\x8cova\\xcc\\x81 - Tak Dej Se K Na\\xcc\\x81m A Projdem Svet.flac'
127.0.0.1 - - [14/Jun/2017 21:36:27] "GET /item/52117/file HTTP/1.1" 500 -
```

https://github.com/beetbox/beets/blob/master/beetsplug/web/__init__.py

Unicode path fixes it for me:
```
def item_file(item_id):
    item = g.lib.get_item(item_id)
    response = flask.send_file(
        unicode(item.path, "utf-8", errors="ignore"),
        as_attachment=True,
        attachment_filename=os.path.basename(item.path),
    )
    response.headers['Content-Length'] = os.path.getsize(unicode(item.path, "utf-8", errors="ignore"))
    return response
```

Changed files:
beetsplug/web/__init__.py

diff --git a/beetsplug/web/__init__.py b/beetsplug/web/__init__.py
index 290f25b48..00718dbfb 100644
--- a/beetsplug/web/__init__.py
+++ b/beetsplug/web/__init__.py
@@ -217,12 +217,14 @@ def all_items():
 @app.route('/item/<int:item_id>/file')
 def item_file(item_id):
     item = g.lib.get_item(item_id)
+    item_path = util.syspath(item.path) if (os.name == 'nt') else (
+        util.py3_path(item.path))
     response = flask.send_file(
-        util.py3_path(item.path),
+        item_path,
         as_attachment=True,
         attachment_filename=os.path.basename(util.py3_path(item.path)),
     )
-    response.headers['Content-Length'] = os.path.getsize(item.path)
+    response.headers['Content-Length'] = os.path.getsize(item_path)
     return response
Repository: beetbox/beets
Issue: lastgenre: `force` config option doesn’t work

### Problem

I want to write the popular genres from Last.fm in the metatags of my music.

I have the following configuration:

```
lastgenre:
    whitelist: no
    count: 5
    fallback: nogenre
    min_weight: 20
    source: track
    force: yes
```

Now I run "beets lastgenre"

Other than what I had expected that beets would now write the 5 most popular genres into each file, it just outputs with (keep) and doesn't change anything to the genre metatag...

What could be going wrong here and how can I fix this?

Thanks a lot for any help!


Changed files:
beetsplug/lastgenre/__init__.py

diff --git a/beetsplug/lastgenre/__init__.py b/beetsplug/lastgenre/__init__.py
index 2f660206e..c4b973620 100644
--- a/beetsplug/lastgenre/__init__.py
+++ b/beetsplug/lastgenre/__init__.py
@@ -373,7 +373,7 @@ class LastGenrePlugin(plugins.BeetsPlugin):
         lastgenre_cmd = ui.Subcommand('lastgenre', help=u'fetch genres')
         lastgenre_cmd.parser.add_option(
             u'-f', u'--force', dest='force',
-            action='store_true', default=False,
+            action='store_true',
             help=u're-download genre when already present'
         )
         lastgenre_cmd.parser.add_option(
Repository: beetbox/beets
Issue: BadFiles plugin crashes beets with latest git master

### Problem

If the `badfiles` plugin is activated, beets crashes when starting an import task.

Running this command in verbose (`-vv`) mode:

```sh
$ beet -vv import --write /data/music
user configuration: /home/jan/.config/beets/config.yaml
data directory: /home/jan/.config/beets
plugin paths:
Sending event: pluginload
artresizer: method is (2, (7, 0, 8))
lyrics: Disabling google source: no API key configured.
library database: /home/jan/beets.db
library directory: /data/music
Sending event: library_opened
Traceback (most recent call last):
  File "/home/jan/.local/bin/beet", line 11, in <module>
    load_entry_point('beets', 'console_scripts', 'beet')()
  File "/data/jan/Projects/beets/beets/ui/__init__.py", line 1266, in main
    _raw_main(args)
  File "/data/jan/Projects/beets/beets/ui/__init__.py", line 1253, in _raw_main
    subcommand.func(lib, suboptions, subargs)
  File "/data/jan/Projects/beets/beets/ui/commands.py", line 955, in import_func
    import_files(lib, paths, query)
  File "/data/jan/Projects/beets/beets/ui/commands.py", line 925, in import_files
    session.run()
  File "/data/jan/Projects/beets/beets/importer.py", line 316, in run
    for stage_func in plugins.early_import_stages():
  File "/data/jan/Projects/beets/beets/plugins.py", line 426, in early_import_stages
    stages += plugin.get_early_import_stages()
  File "/data/jan/Projects/beets/beets/plugins.py", line 112, in get_early_import_stages
    return self._set_stage_log_level(self.early_import_stages)
AttributeError: 'BadFiles' object has no attribute 'early_import_stages'
```

### Setup

* OS: Arch Linux
* Python version: 3.7.2
* beets version: be118b92
* Turning off plugins made problem go away (yes/no): Yes (Disabling the `badfiles` plugin suffices)

My configuration (output of `beet config`) is: https://gist.github.com/Holzhaus/500b790c06fe2250ac9182bd8a6760da

Changed files:
beetsplug/badfiles.py

diff --git a/beetsplug/badfiles.py b/beetsplug/badfiles.py
index 52caa9994..0be08bae5 100644
--- a/beetsplug/badfiles.py
+++ b/beetsplug/badfiles.py
@@ -50,6 +50,7 @@ class CheckerCommandException(Exception):
 
 class BadFiles(BeetsPlugin):
     def __init__(self):
+        super(BadFiles, self).__init__()
         self.verbose = False
 
     def run_command(self, cmd):
Repository: beetbox/beets
Issue: beetsplug/fromfilename.py python3 compatability

https://github.com/beetbox/beets/blob/e8afcbe7ec4bc37bb080f6f466b98807d020753b/beetsplug/fromfilename.py#L104

python2

``` python2
d.values()
```

for python 3 this should be

``` python3
list(d.values())
```


Changed files:
beetsplug/fromfilename.py

diff --git a/beetsplug/fromfilename.py b/beetsplug/fromfilename.py
index 648df6258..5e1b822c2 100644
--- a/beetsplug/fromfilename.py
+++ b/beetsplug/fromfilename.py
@@ -101,7 +101,7 @@ def apply_matches(d):
     """Given a mapping from items to field dicts, apply the fields to
     the objects.
     """
-    some_map = d.values()[0]
+    some_map = list(d.values())[0]
     keys = some_map.keys()
 
     # Only proceed if the "tag" field is equal across all filenames.
Repository: beetbox/beets
Issue: smartplaylist: Respect sort terms in queries

It would be nice to be able sort items in smart playlists, like so:

```
smartplaylist:
  relative_to: ~/music
  playlist_dir: ~/.mpd/playlists
  playlists:
    - name: '2014.m3u'
      query: 'year:2014 added+'
    - name: 'imported-%time{$added,%Y-%m}.m3u'
      query: "added:: path+"
```

I'm unfamiliar with the code but it looks like the smartplaylist plugin [discards the query `sort` field](https://github.com/sampsyo/beets/blob/master/beetsplug/smartplaylist.py#L45) when building playlists. Would it be possible to keep the custom sort?


Changed files:
beetsplug/smartplaylist.py

diff --git a/beetsplug/smartplaylist.py b/beetsplug/smartplaylist.py
index 653a59d30..49a3c84de 100644
--- a/beetsplug/smartplaylist.py
+++ b/beetsplug/smartplaylist.py
@@ -18,7 +18,6 @@ from __future__ import print_function
 
 from beets.plugins import BeetsPlugin
 from beets import config, ui, library
-from beets import dbcore
 from beets.util import normpath, syspath
 import os
 
@@ -36,23 +35,21 @@ def _items_for_query(lib, playlist, album=False):
     if key not in playlist:
         return []
 
-    # Parse quer(ies). If it's a list, join the queries with OR.
+    # Parse quer(ies). If it's a list, perform the queries and manually
+    # concatenate the results
     query_strings = playlist[key]
     if not isinstance(query_strings, (list, tuple)):
         query_strings = [query_strings]
     model = library.Album if album else library.Item
-    query = dbcore.OrQuery(
-        [library.parse_query_string(q, model)[0] for q in query_strings]
-    )
-
-    # Execute query, depending on type.
-    if album:
-        result = []
-        for album in lib.albums(query):
-            result.extend(album.items())
-        return result
-    else:
-        return lib.items(query)
+    results = []
+    for q in query_strings:
+        querystr, sort = library.parse_query_string(q, model)
+        if album:
+            new = lib.albums(querystr, sort)
+        else:
+            new = lib.items(querystr, sort)
+        results.extend(new)
+    return results
 
 
 def update_playlists(lib):
Repository: beetbox/beets
Issue: mpdstats: last_played is documented but not implemented

As pointed out [on the mailing list](https://groups.google.com/d/msg/beets-users/VW0pxtCVZG4/sq9gGsNS9zEJ), the mpdstats plugin (paging @pscn and @kljohann) does not seem to set the `last_played` field, even though the field is described in [the plugin's docs](http://beets.readthedocs.org/en/v1.3.2/plugins/mpdstats.html). Grepping in mpdstats.py for "last_played" shows that doesn't seem to be implemented. We should probably either add it to the plugin or remove it from the docs.


Changed files:
beetsplug/mpdstats.py

diff --git a/beetsplug/mpdstats.py b/beetsplug/mpdstats.py
index e2f990f44..04355fb31 100644
--- a/beetsplug/mpdstats.py
+++ b/beetsplug/mpdstats.py
@@ -281,6 +281,9 @@ class MPDStats(object):
             'beets_item': self.get_item(path),
         }
 
+        self.update_item(self.now_playing['beets_item'],
+                         'last_played', value=int(time.time()))
+
     def run(self):
         self.mpd.connect()
         events = ['player']
Repository: beetbox/beets
Issue: Resolve some time zone weirdness in date queries

I ran into some issues today when attempting to find some items by the specific day that they were added. Specifically, it looks like there is a mismatch between the time zones of the timestamps used for the query bounds and hose used for added dates in the database. My guess is that we need `fromtimestamp` where we have `utcfromtimestamp` or vice-versa.

Paging @silb, who might have a better idea of what to do here. (But depending on how close you live to UTC, you might not even have noticed!) I'll look into it also, but if you can figure out what needs to be fixed here, please go ahead and resolve.


Changed files:
beets/dbcore/query.py

diff --git a/beets/dbcore/query.py b/beets/dbcore/query.py
index 318a7fbe2..be4172692 100644
--- a/beets/dbcore/query.py
+++ b/beets/dbcore/query.py
@@ -330,8 +330,15 @@ class FalseQuery(Query):
 
 
 def _to_epoch_time(date):
-    epoch = datetime.utcfromtimestamp(0)
-    return int((date - epoch).total_seconds())
+    epoch = datetime.fromtimestamp(0)
+    delta = date - epoch
+    try:
+        return int((delta).total_seconds())
+    except AttributeError:
+        # datetime.timedelta.total_seconds() is not available on Python 2.6
+        return int((delta.microseconds
+                    + (delta.seconds + delta.days * 24 * 3600) * 10 ** 6)
+                   / 10.0 ** 6)
 
 
 def _parse_periods(pattern):
@@ -454,7 +461,7 @@ class DateQuery(FieldQuery):
         date = datetime.utcfromtimestamp(timestamp)
         return self.interval.contains(date)
 
-    _clause_tmpl = "date({0}, 'unixepoch') {1} date(?, 'unixepoch')"
+    _clause_tmpl = "{0} {1} ?"
 
     def col_clause(self):
         clause_parts = []
@@ -474,5 +481,4 @@ class DateQuery(FieldQuery):
         else:
             # Match any date.
             clause = '1'
-
         return clause, subvals
Repository: beetbox/beets
Issue: %if{} template function should work with Python booleans

Currently, if the first argument to an %if{} is the string "False", it treats the condition as true. It should special-case this string to facilitate use with inline functions that return booleans.


Changed files:
test/test_library.py, beets/library.py

diff --git a/beets/library.py b/beets/library.py
index 95dffee70..bb10f7392 100644
--- a/beets/library.py
+++ b/beets/library.py
@@ -1121,9 +1121,13 @@ class DefaultTemplateFunctions(object):
         otherwise, emit ``falseval`` (if provided).
         """
         try:
-            condition = _int_arg(condition)
+            int_condition = _int_arg(condition)
         except ValueError:
-            condition = condition.strip()
+            if condition.lower() == "false":
+                return falseval
+        else:
+            condition = int_condition
+
         if condition:
             return trueval
         else:
diff --git a/test/test_library.py b/test/test_library.py
index a39e5ce30..eea513454 100644
--- a/test/test_library.py
+++ b/test/test_library.py
@@ -496,6 +496,10 @@ class DestinationFunctionTest(_common.TestCase, PathFormattingMixin):
         self._setf(u'x%if{,foo}')
         self._assert_dest('/base/x')
 
+    def test_if_false_value(self):
+        self._setf(u'x%if{false,foo}')
+        self._assert_dest('/base/x')
+
     def test_if_true(self):
         self._setf(u'%if{bar,foo}')
         self._assert_dest('/base/foo')
@@ -504,6 +508,10 @@ class DestinationFunctionTest(_common.TestCase, PathFormattingMixin):
         self._setf(u'%if{,foo,baz}')
         self._assert_dest('/base/baz')
 
+    def test_if_else_false_value(self):
+        self._setf(u'%if{false,foo,baz}')
+        self._assert_dest('/base/baz')
+
     def test_if_int_value(self):
         self._setf(u'%if{0,foo,baz}')
         self._assert_dest('/base/baz')
Repository: beetbox/beets
Issue: mpdstats: single or last song isn't rated and counted

The `mpdstats` plugin won't update `play_count`+`rating` for the last (or only) song in the playlist. (paging @pscn and @kljohann)


Changed files:
beetsplug/mpdstats.py

diff --git a/beetsplug/mpdstats.py b/beetsplug/mpdstats.py
index 41a0e7d6a..56522a8de 100644
--- a/beetsplug/mpdstats.py
+++ b/beetsplug/mpdstats.py
@@ -245,6 +245,10 @@ class MPDStats(object):
 
     def on_stop(self, status):
         log.info(u'mpdstats: stop')
+
+        if self.now_playing:
+            self.handle_song_change(self.now_playing)
+
         self.now_playing = None
 
     def on_pause(self, status):
Repository: beetbox/beets
Issue: mbsync: Deal with albums that have multiple copies of the same recording

the current way mbsync plugin used to obtain track mapping list is to use the MusicBrainz recoding ID from each track, it's a workaround to handle "missing or extra tracks". This method is based on an assumption that for each MB release, there are no multiple tracks with same MB recording ID. It usually works, and in my case, only 4 out of 700+ albums disobey this assumption. But for this four albums, I have to fix them by tag track number by hand and re-import.

Considering it's called "mbsync", Why not make an assumption that track number in metadata is not corrupt and use it if possible, or fallback to MB recording ID way if it's corrupted(missing or extra track detected)


Changed files:
beetsplug/mbsync.py

diff --git a/beetsplug/mbsync.py b/beetsplug/mbsync.py
index 76226f08c..1e5857bba 100644
--- a/beetsplug/mbsync.py
+++ b/beetsplug/mbsync.py
@@ -64,13 +64,29 @@ def mbsync_albums(lib, query, move, pretend, write):
             log.info(u'Release ID not found: {0}'.format(a.mb_albumid))
             continue
 
+        # Construct an hash mapping recording MBIDs to their information. A
+        # release can have recording MBIDs that appear multiple times in the
+        # same release.
+        track_index = {}
+        for track_info in album_info.tracks:
+            if track_info.track_id in track_index:
+                track_index[track_info.track_id].append(track_info)
+            else:
+                track_index[track_info.track_id] = [track_info]
+
         # Construct a track mapping according to MBIDs. This should work
-        # for albums that have missing or extra tracks.
+        # for albums that have missing or extra tracks. If a mapping is
+        # ambiguous, the items' disc and track number need to match in order
+        # for an item to be mapped.
         mapping = {}
         for item in items:
-            for track_info in album_info.tracks:
-                if item.mb_trackid == track_info.track_id:
-                    mapping[item] = track_info
+            candidates = track_index.get(item.mb_trackid, [])
+            if len(candidates) == 1:
+                mapping[item] = candidates[0]
+                continue
+            for c in candidates:
+                if c.medium_index == item.track and c.medium == item.disc:
+                    mapping[item] = c
                     break
 
         # Apply.
Repository: beetbox/beets
Issue: ftintitle: does not gracefully handle duplicate artist name

Using ftintitle on one of my tracks, it seemed to get tripped up and not be able to fix it. I've tracked it down to being a problem with the fact that the Album Artist's name is in the Artist field twice.

```
Artist: The Roots feat. Talib Kweli / The Roots
Album Artist: The Roots
```

When trying to find the album artist in the artist field, it does a string split using the album artist as a separator. This returns a list with the following values `['', 'feat. Talib Kweli / ', '']`.

The code that tries to find the `feat_part` is then only expecting a two element list, but is instead given a three. It then checks if the `-1th` element, `2` in this case, is blank. If it's not, it extracts the featured artist. 

If it is blank, it goes on to assume the featured artist must be on the left-hand side of the split and checks element `0`.

Both elements `0` and `2` are blank, so no featured part is found.

I've thought of two solutions, but am not sure which one would make more sense
- Attempt to remove duplicate album artists from the artist string before splitting
- Add another/change the current case to iterate over the split parts to find the first non-blank item

Either way, these methods would presumably still leave the trailing slash on the `feat. Talib Kweli /` and add the extraneous trailing slash to the track title. This, I'm not quite sure how to handle if at all.

Thoughts?


Changed files:
beetsplug/ftintitle.py

diff --git a/beetsplug/ftintitle.py b/beetsplug/ftintitle.py
index 75134ae9c..2e93ad36f 100644
--- a/beetsplug/ftintitle.py
+++ b/beetsplug/ftintitle.py
@@ -83,7 +83,7 @@ def ft_in_title(item, drop_feat):
 
         # Look for the album artist in the artist field. If it's not
         # present, give up.
-        albumartist_split = artist.split(albumartist)
+        albumartist_split = artist.split(albumartist, 1)
         if len(albumartist_split) <= 1:
             ui.print_('album artist not present in artist')
Repository: beetbox/beets
Issue: Always set year, month, and day tags on MediaFiles in that order

1.3.10

_Immediately_ after 'write'

> $ beet write -p album:Mutations
> Beck - Mutations - Cold Brains
>   original_day: 00 -> 02
>   bitrate: 931kbps -> 931kbps
> Beck - Mutations - Nobody’s Fault but My Own
>   original_day: 00 -> 02
>   bitrate: 902kbps -> 902kbps
> ...

I check the FLAC files and see

>    comment[44]: ORIGINALDATE=1998-11

Issue 1)  original_day wasn't written.
IMO, 'write' should think the db and files are in sync immediately after a 'write'.

Issue 2)  write thinks the bitrate doesn't match.
I'm not sure what FLAC data to look at in the file...

'update' apparently thinks things are in sync.

> $ beet update -p album:Mutations
> $

So 'update' doesn't care about original_day ??


Changed files:
beets/mediafile.py

diff --git a/beets/mediafile.py b/beets/mediafile.py
index b2a72d84c..e82b14aae 100644
--- a/beets/mediafile.py
+++ b/beets/mediafile.py
@@ -1460,6 +1460,28 @@ class MediaFile(object):
             if isinstance(descriptor, MediaField):
                 yield property.decode('utf8')
 
+    @classmethod
+    def field_sort_name(cls, name):
+        """Get field name for sorting purposes. Fields names are kept
+        unchanged, unless they are instances of :class:`DateItemField`,
+        in which case `year`, `month`, and `day` are replaced by `date0`,
+        `date1`, and `date2`, respectively.
+        """
+        if isinstance(cls.__dict__[name], DateItemField):
+            name = re.sub('year',  'date0', name)
+            name = re.sub('month', 'date1', name)
+            name = re.sub('day',   'date2', name)
+        return name
+
+    @classmethod
+    def sorted_fields(cls):
+        """Get the names of all writable metadata fields sorted by
+        lexicographic order (except for instances of :class:`DateItemField`,
+        which are sorted in year-month-day order).
+        """
+        for property in sorted(cls.fields(), key=cls.field_sort_name):
+            yield property
+
     @classmethod
     def readable_fields(cls):
         """Get all metadata fields: the writable ones from
@@ -1496,7 +1518,7 @@ class MediaFile(object):
         the `MediaFile`. If a key has the value `None`, the
         corresponding property is deleted from the `MediaFile`.
         """
-        for field in self.fields():
+        for field in self.sorted_fields():
             if field in dict:
                 if dict[field] is None:
                     delattr(self, field)
Repository: beetbox/beets
Issue: auto_keep broken by playlist feature

the quick n dirty playlist feature in this commit:
https://github.com/beetbox/beets/commit/861bc69df55cb3d4d20da6082878542ff42d85d8
broken auto_keep:

https://github.com/beetbox/beets/blob/79435c3b6f682d30cac169c97f978491dfd8f80d/beetsplug/convert.py#L204
When auto_keep is true, then this line fails because _get_opts_and_config was modified to return 8 values (+ playlist) instead of 7 and this check wasn't updated.

Changed files:
beetsplug/convert.py

diff --git a/beetsplug/convert.py b/beetsplug/convert.py
index ef6865597..9b28523c6 100644
--- a/beetsplug/convert.py
+++ b/beetsplug/convert.py
@@ -200,8 +200,8 @@ class ConvertPlugin(BeetsPlugin):
     def auto_convert_keep(self, config, task):
         if self.config['auto_keep']:
             empty_opts = self.commands()[0].parser.get_default_values()
-            (dest, threads, path_formats, fmt,
-             pretend, hardlink, link) = self._get_opts_and_config(empty_opts)
+            (dest, threads, path_formats, fmt, pretend,
+             hardlink, link, playlist) = self._get_opts_and_config(empty_opts)
 
             items = task.imported_items()
             self._parallel_convert(dest, False, path_formats, fmt,
Repository: beetbox/beets
Issue: spotify: spotify_updated is always a "null" date

I just checked the `spotify_updated` values and all the values are `1969-12-31 19:00:00`, which prevents the use of any date-related queries. 
 
I have seen this elsewhere, but cannot figure out how to fix the issue. Here's how the date is currently being saved

https://github.com/beetbox/beets/blob/44a7cc74bfe673bcf960c5dc18716b38e33d5ad0/beetsplug/spotify.py#L651

Any idea how to fix it?


Changed files:
beetsplug/spotify.py

diff --git a/beetsplug/spotify.py b/beetsplug/spotify.py
index f1aecb583..66b2b1084 100644
--- a/beetsplug/spotify.py
+++ b/beetsplug/spotify.py
@@ -19,7 +19,6 @@ Spotify playlist construction.
 
 import base64
 import collections
-import datetime
 import json
 import re
 import time
@@ -648,7 +647,7 @@ class SpotifyPlugin(MetadataSourcePlugin, BeetsPlugin):
                 if feature in self.spotify_audio_features.keys():
                     item[self.spotify_audio_features[feature]] = \\
                         audio_features[feature]
-            item['spotify_updated'] = datetime.datetime.now()
+            item['spotify_updated'] = time.time()
             item.store()
             if write:
                 item.try_write()
Repository: beetbox/beets
Issue: beets-play: Cleanup code doesn't run in `play_music()`.

The `play_music()` function in beets-play calls `interactive_open()`, which ends python execution rather then forking a new process. For this reason, cleanup code responsible of the deletion of the temporary playlist after the invocation of the player doesn't actually run.

> We have two options:
> - Make the `play` plugin use a different spawning mechanism (leaving `interactive_open` for `config -e` >and the like).
> - Add an optional flag for `interactive_open` to avoid `exec`ing in place.


Changed files:
beetsplug/play.py

diff --git a/beetsplug/play.py b/beetsplug/play.py
index 1d425e1c8..c943a5155 100644
--- a/beetsplug/play.py
+++ b/beetsplug/play.py
@@ -129,13 +129,8 @@ class PlayPlugin(BeetsPlugin):
         try:
             util.interactive_open(open_args, command_str)
         except OSError as exc:
-            raise ui.UserError("Could not play the music playlist: "
+            raise ui.UserError("Could not play the query: "
                                "{0}".format(exc))
-        finally:
-            if not raw:
-                self._log.debug('Removing temporary playlist: {}',
-                                open_args[0])
-                util.remove(open_args[0])
 
     def _create_tmp_playlist(self, paths_list):
         """Create a temporary .m3u file. Return the filename.
Repository: beetbox/beets
Issue: Uncaught exception by discogs plugin

During use of the discogs plugin, a requests.exceptions.ConnectionError exception is occasionally thrown and not caught.  This causes the operation to fail and beet to exit with a stacktrace.


Changed files:
beetsplug/discogs.py

diff --git a/beetsplug/discogs.py b/beetsplug/discogs.py
index 2cb2c0b15..f859e48f7 100644
--- a/beetsplug/discogs.py
+++ b/beetsplug/discogs.py
@@ -19,6 +19,7 @@ from beets.autotag.hooks import AlbumInfo, TrackInfo, Distance
 from beets.plugins import BeetsPlugin
 from discogs_client import Release, Client
 from discogs_client.exceptions import DiscogsAPIError
+from requests.exceptions import ConnectionError
 import beets
 import logging
 import re
@@ -62,6 +63,9 @@ class DiscogsPlugin(BeetsPlugin):
         except DiscogsAPIError as e:
             log.debug(u'Discogs API Error: {0} (query: {1})'.format(e, query))
             return []
+        except ConnectionError as e:
+            log.debug(u'HTTP Connection Error: {0}'.format(e))
+            return []
 
     def album_for_id(self, album_id):
         """Fetches an album by its Discogs ID and returns an AlbumInfo object
@@ -85,6 +89,9 @@ class DiscogsPlugin(BeetsPlugin):
                 log.debug(u'Discogs API Error: {0} (query: {1})'
                           .format(e, result._uri))
             return None
+        except ConnectionError as e:
+            log.debug(u'HTTP Connection Error: {0}'.format(e))
+            return None
         return self.get_album_info(result)
 
     def get_albums(self, query):
Repository: beetbox/beets
Issue: Spotifysync crashes when tracks do not have audio-features

Some tracks in Spotify do not have audio-features. This causes `spotifysync` to crash:

```
spotify: Processing 48/471 tracks - Arijit Singh, Arko - OM - Rashtra Kavach - Seher
error: Spotify API error:
{
  "error" : {
    "status" : 404,
    "message" : "analysis not found"
  }
}
URL:
https://api.spotify.com/v1/audio-features/5pjSpt2mstf5JTf46FbT48
params:
None
```

`spotifysync` should gracefully handle such errors. Maybe we need a try-except block [here](https://github.com/beetbox/beets/blob/master/beetsplug/spotify.py#L622). Thoughts?

Changed files:
beetsplug/spotify.py

diff --git a/beetsplug/spotify.py b/beetsplug/spotify.py
index 42e20c828..a9c8368b0 100644
--- a/beetsplug/spotify.py
+++ b/beetsplug/spotify.py
@@ -35,6 +35,10 @@ from beets.plugins import BeetsPlugin, MetadataSourcePlugin
 DEFAULT_WAITING_TIME = 5
 
 
+class SpotifyAPIError(Exception):
+    pass
+
+
 class SpotifyPlugin(MetadataSourcePlugin, BeetsPlugin):
     data_source = 'Spotify'
 
@@ -189,6 +193,9 @@ class SpotifyPlugin(MetadataSourcePlugin, BeetsPlugin):
                     seconds.', seconds)
                 time.sleep(int(seconds) + 1)
                 return self._handle_response(request_type, url, params=params)
+            elif response.status_code == 404:
+                raise SpotifyAPIError("API Error {0.status_code} for {1}"
+                                      .format(response, url))
             else:
                 raise ui.UserError(
                     '{} API error:\\n{}\\nURL:\\n{}\\nparams:\\n{}'.format(
@@ -621,6 +628,9 @@ class SpotifyPlugin(MetadataSourcePlugin, BeetsPlugin):
             item['spotify_track_popularity'] = popularity
             audio_features = \\
                 self.track_audio_features(spotify_track_id)
+            if audio_features is None:
+                self._log.info('No audio features found for: {}', item)
+                continue
             for feature in audio_features.keys():
                 if feature in self.spotify_audio_features.keys():
                     item[self.spotify_audio_features[feature]] = \\
@@ -639,7 +649,9 @@ class SpotifyPlugin(MetadataSourcePlugin, BeetsPlugin):
 
     def track_audio_features(self, track_id=None):
         """Fetch track audio features by its Spotify ID."""
-        track_data = self._handle_response(
-            requests.get, self.audio_features_url + track_id
-        )
-        return track_data
+        try:
+            return self._handle_response(
+                requests.get, self.audio_features_url + track_id)
+        except SpotifyAPIError as e:
+            self._log.debug('Spotify API error: {}', e)
+            return None
Repository: beetbox/beets
Issue: mbsync: Double output

Recently the `mbsync` plugin has started to produce double output.
 ```
$ beet mbsync -p black sabbath vol 4
Black Sabbath - Vol 4 - Wheels of Confusion
  script:  -> Latn
  language:  -> eng
  script:  -> Latn
  language:  -> eng

...snip...

Black Sabbath - Vol 4 - Under the Sun
  script:  -> Latn
  language:  -> eng
  script:  -> Latn
  language:  -> eng
```

A quick `git bisect` reveals this was introduced in 72515448ad505ef0b73c0d0aa4aa424419b55755.

It looks like this could just be a cosmetic issue but I haven't had time to dive into the new code from #2988 to confirm yet.

Changed files:
beets/library.py

diff --git a/beets/library.py b/beets/library.py
index 3ba79d069..00cf00c79 100644
--- a/beets/library.py
+++ b/beets/library.py
@@ -623,7 +623,9 @@ class Item(LibModel):
         """
         keys = super(Item, self).keys(computed=computed)
         if with_album and self._cached_album:
-            keys += self._cached_album.keys(computed=computed)
+            keys = set(keys)
+            keys.update(self._cached_album.keys(computed=computed))
+            keys = list(keys)
         return keys
 
     def get(self, key, default=None, with_album=True):
Repository: mikubill/sd-webui-controlnet
Issue: Add Multi-ControlNet to Metadata

I like to write down of my prompts / settings when generating images, or refer back to them via an image's metadata, but Multi-ControlNet doesn't seem to add the data for any additional ControlNets after the first. 

This means that if I go to a Multi-ControlNet image and copy its metadata to use in future generations, it will be missing the data I'd need to recreate it or create other images using the same settings. So it would be really nice if the data for ControlNet-1 and above were included. 

Changed files:
scripts/controlnet.py

diff --git a/scripts/controlnet.py b/scripts/controlnet.py
index 88f6077..310def4 100644
--- a/scripts/controlnet.py
+++ b/scripts/controlnet.py
@@ -464,7 +464,10 @@ class Script(scripts.Script):
             if not enabled:
                 continue
             control_groups.append((module, model, params))
-            prefix = f"ControlNet-{idx}" if idx > 1 else "ControlNet"
+            if len(params_group) != 1:
+                prefix = f"ControlNet-{idx}"
+            else:
+                prefix = "ControlNet"
             p.extra_generation_params.update({
                 f"{prefix} Enabled": True,
                 f"{prefix} Module": module,
Repository: microsoft/qlib
Issue: yahoo collector returns error

command:
`python .qlib/scripts/data_collector/yahoo/collector.py download_data --source_dir .csv-yahoo --region US --start 2021-03-18 --end 2021-03-19 --delay 0.1 --interval 1d`
Bug report:


```
Traceback (most recent call last):
  File ".qlib/scripts/data_collector/yahoo/collector.py", line 642, in <module>
    fire.Fire(Run)
  File "xxxxxxxxxxxxxxxx/.venv/lib/python3.7/site-packages/fire-0.3.1-py3.7.egg/fire/core.py", line 138, in Fire
    component_trace = _Fire(component, args, parsed_flag_args, context, name)
  File "xxxxxxxxxxxxxxxx/.venv/lib/python3.7/site-packages/fire-0.3.1-py3.7.egg/fire/core.py", line 468, in _Fire
    target=component.__name__)
  File "xxxxxxxxxxxxxxxx/.venv/lib/python3.7/site-packages/fire-0.3.1-py3.7.egg/fire/core.py", line 672, in _CallAndUpdateTrace
    component = fn(*varargs, **kwargs)
  File ".qlib/scripts/data_collector/yahoo/collector.py", line 622, in download_data
    super(Run, self).download_data(max_collector_count, delay, start, end, interval, check_data_length, limit_nums)
  File "xxxxxxxxxxxxxxxx/.qlib/scripts/data_collector/base.py", line 404, in download_data
    limit_nums=limit_nums,
TypeError: Can't instantiate abstract class YahooCollectorUS1d with abstract methods get_instrument_list

```

Changed files:
scripts/data_collector/yahoo/collector.py

diff --git a/scripts/data_collector/yahoo/collector.py b/scripts/data_collector/yahoo/collector.py
index eadc381e..f0e11069 100644
--- a/scripts/data_collector/yahoo/collector.py
+++ b/scripts/data_collector/yahoo/collector.py
@@ -185,7 +185,7 @@ class YahooCollector(BaseCollector):
 
 
 class YahooCollectorCN(YahooCollector, ABC):
-    def get_stock_list(self):
+    def get_instrument_list(self):
         logger.info("get HS stock symbos......")
         symbols = get_hs_stock_symbols()
         logger.info(f"get {len(symbols)} symbols.")
@@ -249,7 +249,7 @@ class YahooCollectorCN1min(YahooCollectorCN):
 
 
 class YahooCollectorUS(YahooCollector, ABC):
-    def get_stock_list(self):
+    def get_instrument_list(self):
         logger.info("get US stock symbols......")
         symbols = get_us_stock_symbols() + [
             "^GSPC",