archseer/ruby-mpd

Songs with nil file

Closed this issue · 6 comments

Just imported my song library. There are many Songs returned with no file.

[ M.stats[:songs], M.songs.length, M.songs.reject(&:file).length ]
#=> [17103, 22892, 5789]

Some of them are real zombies:

M.songs.reject(&:file).first
#=> #<MPD::Song:0x000000028e18e8 @mpd=…, @data={:"last-modified"=>2015-11-08 07:57:01 UTC}, @time=nil, @file=nil, @title=nil, @artist=nil, @album=nil, @albumartist=nil>

Some of them are files that exist on disk (perhaps the Unicode characters in the path are tricky?):

M.songs.reject(&:file).last
#=> #<MPD::Song:0x00000003704178 @mpd=…, @data={:"last-modified"=>2008-06-01 03:16:00 UTC, :track=>1, :date=>2003, :genre=>"Soundtrack"}, @time=[nil, 160], @file=nil, @title="Bang Bang (My Baby Shot Me Down)", @artist="Nancy Sinatra", @album="Kill Bill: Vol.1", @albumartist="群星">

Dir['**/*'].grep /Bang Bang/
#=> ["群星/Kill Bill_ Vol.1/01 Bang Bang (My Baby Shot Me Down).mp3"]

puts `ls -l "群星/Kill Bill_ Vol.1/"`
#=> total 3744
#=> -rw-r--r-- 1 gkistner gkistner 3833856 May 31  2008 01 Bang Bang (My Baby Shot Me Down).mp3

How do I fix it so that all songs have a file?

Does the file get listed correctly in another player, for example MPC, the official frontend for MPD?

Probably best if you add some sort of log out puts response.inspect after this line so that we can see what the data coming from MPD looks like for that specific file.

@archseer

Interesting results. When I use where, I get back a valid Song with file:

bb = M.where(title:'Bang Bang')

# p response
"file: gavin/\xE7\xBE\xA4\xE6\x98\x9F/Kill Bill_ Vol.1/01 Bang Bang (My Baby Shot Me Down).mp3\nLast-Modified: 2008-06-01T03:16:00Z\nTime: 160\nArtist: Nancy Sinatra\nAlbumArtist: \xE7\xBE\xA4\xE6\x98\x9F\nTitle: Bang Bang (My Baby Shot Me Down)\nAlbum: Kill Bill: Vol.1\nTrack: 1\nDate: 2003\nGenre: Soundtrack\n"

bb.length      #=> 1
bb.first.file  #=> "gavin/群星/Kill Bill_ Vol.1/01 Bang Bang (My Baby Shot Me Down).mp3"

However, when I use songs instead I get the nil:

ss = M.songs.select{ |f| f.title[/Bang Bang/] }
# (p response moved to below)
ss.length      #=> 1
ss.first.file  #=> nil

The last ~500 bytes of the response.inspect end with data including the file name:

"…ate: 1977\nDisc: 1/1\ndirectory: gavin/\xE7\xBE\xA4\xE6\x98\x9F\nLast-Modified: 2009-01-04T00:25:22Z\ndirectory: gavin/\xE7\xBE\xA4\xE6\x98\x9F/Kill Bill_ Vol.1\nLast-Modified: 2009-01-04T00:25:22Z\nfile: gavin/\xE7\xBE\xA4\xE6\x98\x9F/Kill Bill_ Vol.1/01 Bang Bang (My Baby Shot Me Down).mp3\nLast-Modified: 2008-06-01T03:16:00Z\nTime: 160\nArtist: Nancy Sinatra\nAlbumArtist: \xE7\xBE\xA4\xE6\x98\x9F\nTitle: Bang Bang (My Baby Shot Me Down)\nAlbum: Kill Bill: Vol.1\nTrack: 1\nDate: 2003\nGenre: Soundtrack\n"

The above includes the only instances of "Bang Bang" in the entire 4.8MB response text.

@archseer To help debug, the full response is available at:
https://gist.github.com/Phrogz/a8c46afc3aac50a1182b

The encoding for the response string is ASCII-8BIT.

Looks like this could be related to #38

When you use where ruby-mpd sends :findadd or :find. When you call songs it sends :listallinfo.

http://www.musicpd.org/doc/protocol/database.html

Found it (and I have a fix that I need to package up). The problem is that listallinfo includes directories in the results (and starts off with them). Here are the first few lines of the response:

directory: bijan
Last-Modified: 2016-01-22T16:23:13Z
file: bijan/CabinFeverSessionsDec13_Fubari_Janis.mp3
Last-Modified: 2016-01-13T17:13:19Z
Time: 11256

The code in parse_response tries to handle this by stripping out Directory (and Playlist) lines:

string = filter_lines(string, [:directory, :playlist])

However, this leaves the Last-Modified: line as the first line. Then, make_chunks splits the results based on Last-Modified: instead of the desired file: separator. And things go downhill.

I'm getting rid of filter_lines—because this is the only case where it's used, and it's very inefficient, and doesn't work as desired—and replacing it with:

string.gsub! /^(?:directory|playlist): .+?\n(?:last-modified: .+?\n)?/i, ''

With this one-line change @mpd.songs works correctly.

And yes, this looks to be exactly the issue described in #38; the fix for this bug should fix 38 as well.