Duplicates and singletons

Hi!
I’m trying to get all the duplicates in my library, but I can’t get it to work on singletons.
My duplicate keys are [mb_trackid, mb_releasegroupid, track]
As you can see, two of these keys are album-specific, however, beet info -l on the culprits shows that while they are singletons, these tags are nontheless populated, and this causes the recordings to not be seen as duplicates.
Could there be a way to adapt the duplicates plugin so that for singletons it doesn’t consider album-specific tags?
As a temporary workaround, I thought of using the inline plugin and create a tag this way:

dupe: |
        if singleton:
            return mb_trackid
        else: 
            return mb_trackid+track+mb_releasegroupid

However, upon trying it out I got this error message:

beetsplug.inline.InlineError: error in inline path field code:
if singleton:
    return mb_trackid
else: 
    return mb_trackid+track+mb_releasegroupid

NameError: global name 'singleton' is not defined

So what’s the name of the singleton tag in inline? How could I find out?
Edit:
after looking at all the variables available for inline, there is nothing corresponding to the singleton tag
I did it by by trying

for x in globals():
            print(x)

which gives

discogs_artistid
lyrics
disctitle
lyricist
month
channels
disc
mb_trackid
composer
mb_parentworkid
albumartist_sort
style
bitdepth
parentwork_disambig
title
mb_albumid
acoustid_fingerprint
rg_album_gain
mb_releasegroupid
rg_album_peak
discogs_albumid
albumartist_credit
catalognum
format
encoder
rg_track_gain
day
original_year
data_source
r128_album_gain
mb_albumartistid
releasegroupdisambig
work
bpm
r128_track_gain
mb_workid
arranger
artist_credit
artist
parent_composer
mb_workid_current
grouping
mb_releasetrackid
disctotal
album_id
albumstatus
alt.by-work
track_alt
original_day
albumartist
year
albumdisambig
samplerate
id
album
mb_artistid
parentwork
media
artist_sort
comments
tracktotal
rg_track_peak
track
mtime
acoustid_id
work_date
added
original_month
discogs_labelid
__builtins__
comp
composer_sort
initial_key
genre
path
bitrate
language
country
script
parent_composer_sort
label
length
albumtype
__INLINE_FUNC__
work_disambig
asin

Cheers,
Dorian

Hi! Indeed, looks like we don’t make computed fields available to inline code—and singleton is a computed field. (We should probably do that! Maybe consider filing a feature request?)

One workaround might be to use the album_id field. If the album ID is null, that means it’s a singleton.

1 Like

That’t the whole problem: if a recording was previously tagged as part on an album by whatever program (beets or picard in my case) then the tag is taken over when beets imports the file - meaning that even if it is a singleton, it will have a populated album_id and other field. As an example, one of my singletons’ beet info -l output:

/media/soergeld/My Passport/by-album/Watts, Andre/Liszt, Franz/Les Jeux d'eau a la Villa d'Este.mp3
                  aa: André Watts Plays Liszt, Album 1
                 aaa: Watts, André
acoustid_fingerprint: AQADtEpCJWEmJVHQH18IT1py4bB17DriNKxw8YGePOjhExWeJ_h0HM0k9MEVTM8Flj0qJRdWHv2hRWXQo3GKX_iUD83SoT8eHX7w4If14DuOptQFi-mh6TG-Ez5yHHZznA12gTFFfIgk_dA7xIm4o0m1I1ck-BH04D0eZceL8DueozqmqFGCnJIh5TnCRGyIoxKDTQxVoc_w40WYNBq05VegLk8Q8oOf4HzQPmGCC8mUI0wWhVFxPkRxPU7wzFICcx_6oCca7A_-HFnKh9AXSlCvBGm-HH-OTduDV4FzfMfx4OOL42OJ5MhVo6lyXHgzXBExJU68o3UugU-U4zwiQaOSWsiTHfiNMCHZI9nYKMQTJTneycZ1nELVhIejnC1KncS34-lhcrjwII1zXIPO4TzyHz3StAk6HT_eQ8t5hE-Go3FJlCaPR1fgODOe40c_NNGN93ikIDeuQ8cf5MJ7HOXF4TuchFMoPEiOEOVh3ujTyKhyOM4KrT9O2WieBr_wh4N3Hj8cZ0EvC-EO1Uc6y_jxVjh-bC-Od_iP5jyOB9SlAx--JeiF80hnDZrSHmH_BFSEHh8q_eCPB--G5kI-B8nR_CjD4WSKR13RP0iINKLQLAz-oLmUBN1z5BTkhDmiP2j0UEFv4s9QJkp0JMuvIEeXRoezrcL14CWaZMoeXEeyl0jHDGde9MF14FzxvKhEPtiWrD4On8ETDa8Oz8SPLzjS83imQPwxqUXOovfhB-5xpHniQCfyEM0tXLnQ72i-II-CH1p1wcOPH34r2DVCSse34NFx1jjV40eLZsxKYE_Mgw8TeJKE6NB5xGqXDD2FRumEL9KJKw-u4ymaH-Gh88jjY14u_CXO6Ej-GWG2-fjQWItA5hSuPHhu-Fm4EDeJPcgfQWNdIs_RF7UIuyl6IUeyxDq67MGTJ6h8wg-eIT9E5IbPoF-KW8Skp4yQJuHGIVmPP8d9fEcpHQ2XLVCTMsao7mgvwdPxSMYVvEov_BIq7dCzB3cjPGxQ_cGP7-gWvEguZUf6HG-YBY8XTOqkI-U-6EIu5DkSHifuHae8BKY99EL5wMGPjzIeVDquCx96Ch589DyO4wq8o8R1eL1Q3UhcHdn24B2FH71i-D36HD_i60imPDl8o-H04xeuC3mOZD-a4TmeJcTRXEN1HvfwK0Ty6uhF_IB_4niDL3Be-PjR4z6O5kcOP-iPH80lG30_XErQvKhuXBm6VAc_tjheGMk-hB2a57iLB4f34zzI48KF38aP0oIf9Mfxo32CfIf4HSF54-mRpwo05UN-Aj98M3iOlqHx4yRyJDuaOCz-437QVh5-XBDj47iDPcKPIzny40RzR3h8CQfy4xfCO4LfEdVX_MF94AgSvjj-4ENf_HA-4f4B58Y_-PiPoz9eXPiRH8Cx7-ijVDh8wi9OKEyzw3F35Dn8Dw-OH_7R43iOH-UpNHEW48ePkzh-HMd15IuRfNNwM3hyXEdP-MdJ_DgqP8fxVEjeKA7y48yFWx6eo9bRPEcuAmdO9FJ4NDxiHz--4zysp8KdHQfyLEdyElfwH0fzlcHEJ0Ly7EJ-3IWPv4qCi4k15MyRLCOL_mhQ3piD3-jRxA-eW8jzQz9y5FlwCP-hHc3HDPnaBPdxjM_wGz_iI0HPDYYfovwxh0B1pkgWVUf8HMfxEMfx7EST5A-q_7iiI9egI_rxEOfBJBSyzAtUkSei30P7HM2_ID-So4-PppqIC9dxPDl6PONRXUW3JaExJcwVnA_YkMWOIFk6PIKPKj9iKz9048iVfGijfMhvJMfhs0Ft4zmOqrnhMT-eHC6VLvjhTjJ-JPuD3BHQH1Nu9EfjBscx5eiNHs0kXIlQqYePHkeuEHqKXPCFXGoEHfmLNzX-Hn2eYddxvEgfBtp3IsfzDOXxwLmCp1HQ63iOH_mhHyGzwLeRXiGTgg8JMflROUQeHMcZAH2-CJ7p4LnhXMePH8fRfDqSIyhD_HDi48QHHz_8I0TyH2eK4z8e3DqeH8nk6Eh_HHjxIYJeIYUfRsOFw2-MfhqRHLkEPPjxCtXhpC2OF8eP_NCRfjs-olEe3Plx4fCL_mguPMOTIw-P5DyOnkXzHMdx76h-9MKBklmO5hbyI86D6mge_OiXUfCjBx559Eoa4jj84Udi5E_ww19x3Mfx3TiO8Dv8D3cgH32In9Cv4kcuVD6cPPCP_hHyrND7IujhHz9-9PiPC3kuJBWPc0QTdSn6C32P5ovx40NzhN_xGidxHM2VoycXaAwXNCOOB-hLaPmRZ9dw4rmE6TrCRPwgJwzyi2jyKkT54dlxoTlzTB-e_TjC5EIy5Xie48dxHPmRHP0R5j2Sk00IIBChRCCggAJCQMAsgoIIpZQhTAiEABNIOkOcY0QYBJRxgGoDiAQAAMiAAYA4SqwQzgggHQBKAKIA2IQAASQRzBkAAAIAAEKMQc5BaoQpzFCgBBBEECQQI4IYghADQCChAAKFAUMME8oBQAggiBSDhATGCOEEBJIYyQnhAhgGJVBMAIKIMkAYxoxQRjLRrALHEQKEYYKBo4gCAAhHiCGAAAgCJEwQRpgQwkAgBBECGAUYJAJIy4BGjgIjAAJMCoQMAwAooIAQzgKpkGBAAAakosopQIGwAgDiDQBGAAUMcY5QgAhARBkAiBBMeAGYEgApZwRzAipGlEFMGQMIAoQIgYQimCCmJCCCMMAQc0AQxAgyRggGCBUeUIcIcYAyQSVCTluCqIxSCmMMIM5YAAQgTgDhrCDEQeAAEwQwsYwRQABjQADAMW6gIBggUgQ5hBgHhCAKIWiMGABYZgB0gkIIkASYINAQYMAJw5RzQhgGgLCOSSoIBIgRJwTQjpCslEUAAGWZUQAICAwJjhmwmEPCAOSIAsCqJQQHYhF0iDADMGOUUYCJKBRRpCkDhQABMFYBc8woBoQCUAhoATVFWAgIIwQAJ5AwBAgCkKDGEEaAEoMAyIiQwAHBBMBCAAUsII6YBYgTggmpKCNGKUIFBYJIIQgSyhkhQGeIEyWEEAIJBY0gxAgAAANCOEgQ5AIh45kRiCCHqBSAMC8MEAI1oRwngAjIsENMAQUUIcgBiwXz2oFiAGMMCAQIMELIsIgRBTowgBDMMCcBEAAYAtlVTSACkGIIDDSFYVQQgoIjgiJDsGoAWaAEQU4QN8IEGgSCNGMKG8chU8p4BYAACIgSiCHUQWEAIQsxBgQwRDqHBHQGMUGEgASkBYAyThEHFDqKgEIUAEYAo4RjCgkiBBHAcAWAE1IAxKADzBChiGACBEIsE5UI4RoTSAjCBAKCAcQI4gARIgyRFgoABbNOEbcEEAI
         acoustid_id: bba38585-8c96-4de8-8cd8-2202455878cd
               added: 2020-03-04 00:08:40
               album: André Watts Plays Liszt, Album 1
            album_id: 
         albumartist: Franz Liszt; André Watts
  albumartist_credit: Liszt; André Watts
    albumartist_sort: Liszt, Franz; Watts, André
       albumdisambig: 
         albumstatus: Official
           albumtype: album
            arranger: 
              artist: André Watts
       artist_credit: André Watts
         artist_sort: Watts, André
                asin: B000002S4G
            bitdepth: 0
             bitrate: 320kbps
                 bpm: 0
          catalognum: 
            channels: 2
            comments: 
                comp: False
            composer: Franz Liszt
       composer_sort: Liszt, Franz
             country: US
         data_source: MusicBrainz
                 day: 00
                disc: 01
     discogs_albumid: 0
    discogs_artistid: 0
     discogs_labelid: 0
           disctitle: 
           disctotal: 01
                dupe: 8f29d068-6d2c-49a0-9fd8-fad73f098dd3
             encoder: 
            filesize: 18807067
              format: MP3
               genre: Classical
            grouping: 
                  id: 53180
         initial_key: 
               label: EMI Classics
            language: eng
              length: 7:48
            lyricist: 
              lyrics: 
    mb_albumartistid: 2cd475bb-1abd-40c4-9904-6d4b691c752c
          mb_albumid: 529778db-8943-488d-bf6c-3be1fee53617
         mb_artistid: 558bb0fe-6cff-4ab5-926d-1d699e141829
     mb_parentworkid: 29954628-3a98-41f9-87a1-14dc00bcfbe6
   mb_releasegroupid: 691a72eb-e669-37fa-b284-2ef1443ee1aa
   mb_releasetrackid: 
          mb_trackid: 8f29d068-6d2c-49a0-9fd8-fad73f098dd3
           mb_workid: bdd04666-9c32-3f15-b7e3-23883c67cfe2
   mb_workid_current: bdd04666-9c32-3f15-b7e3-23883c67cfe2
               media: CD
               month: 00
               mtime: 2020-03-04 01:16:44
        original_day: 00
      original_month: 00
       original_year: 1992
     parent_composer: Franz Liszt
parent_composer_sort: Liszt, Franz
          parentwork: Années de pèlerinage
                  pc: Liszt, Franz
                  pw: Années de pèlerinage
     r128_album_gain: 000000
     r128_track_gain: 000000
releasegroupdisambig: 
       rg_album_gain: 0.0
       rg_album_peak: 0.0
       rg_track_gain: 0.0
       rg_track_peak: 0.0
          samplerate: 44kHz
              script: Latn
           singleton: True
               style: 
               title: Les Jeux d'eau à la Villa d'Este
                  tr: 1-9 Les Jeux d'eau à la Villa d'Este
               track: 09
          tracktotal: 10
                type: classical
                work: 
           work_date: 1877
       work_disambig: 
         work_prefix: None
                year: 1992

As can be seen, the album- specific fields are populated, even if it is a singleton. beet scrub doesn’t help and these tags are used by the duplicates plugin, resulting in non-detection of some duplicates.

I found another workaround, based on the fact that I name singletons differently to albums, and then I read the path field to detect that, but that’s quite ugly.

Upon verification: All my singletons have an empty album_id field, so your workaround should work, however I’m not sure why this album-related tag got cleaned and not all the others.

That field is the SQL foreign key that associates items with albums. So it’s not just any field—it literally defines whether a track is a singleton or not.

Other album-level fields are allowed to exist on items as well. We wouldn’t want to prevent tracks from having years just because they’re singletons, for example.

On inline: Is it possible to create the whole path in python and then just do

paths: 
    default:  path_inline

?
I tried it but I run into unicode-related error messages:

<string>:335: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
<string>:335: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
<string>:335: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
<string>:335: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
<string>:335: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
+/media/soergeld/My Passport/by-work/[anonymous].flac
<string>:335: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
Error: file exists while renaming /media/soergeld/My Passport/by-album/Capella Reial de Catalunya, La, Savall, Jordi/Alfons V el Magnanim_ El Cancionero de Montecassino/01-02 Adoramus te, antifona para 4 voces, CM 4_ II. Adoramus te Domine.flac to /media/soergeld/My Passport/by-work/[anonymous].flac

(here tested with the alternatives plugin). Is creating entire paths with inline a no-go, or is there a workaround?

Those Python errors would come from your inline definition, I think… maybe check that out. Also, it seems like you might be on Python 2?

I think that approach might not work because we escape path separators (/) in fields, even if they’re computed by inline.

Ok thanks, I think that covers all the questions I had.