Apple TV for Music
I wanted to play my music through my Apple TV. Apple have a service for that they think I should use instead.
DAAP
iTunes music sharing uses the Digital Audio Access Protocol (DAAP). To summarise, DAAP is DLNA for Apple. I don't really like either. A DAAP server is really just a HTTP server that returns XML. iTunes discovers local (exclusively local) DAAP servers using zeroconf. My hope was that my Apple TV would be able to play my music library from my NAS, if my NAS could run a DAAP server.
ALAC and FLAC
In my case, all my music is FLAC. So it needs transcoding. To do that, I rigged a quick-and-dirty script in ruby:
#!/usr/bin/env ruby require 'shellwords' require 'pry' WORKER_COUNT=10 FLAC_ROOT="/mnt/H2C/music/flac" ALAC_ROOT="/mnt/H2C/music/alac" TranscoderOpts = Struct.new(:codec, :container, :bitrate) class Album attr_accessor :src_root, :dst_root, :genre, :album attr_accessor :in_files, :out_files, :src_art, :dst_art def initialize src_root, dst_root, genre, album @src_root = src_root @dst_root = dst_root @genre = genre @album = album @files = Dir.open("#{src_root}/#{genre}/#{album}").children.select { |f| f.end_with? ".flac" } @in_files = @files.map { |in_file| "#{src_root}/#{genre}/#{album}/#{in_file}" } @out_files = @in_files.map { |in_file| in_file.gsub(".flac", ".m4a").gsub("/flac/", "/alac/") } @src_art = "#{src_root}/#{genre}/#{album}/folder.jpg" @dst_art = "#{dst_root}/#{genre}/#{album}/folder.jpg" end def flac_to_alac opts, queue puts "Creating #{@dst_root}/#{@genre}/#{@album}" `mkdir -p "#{@dst_root}/#{@genre}/#{@album}"` unless File.exist? @dst_art puts "Copying #{@src_art}" `cp "#{@src_art}" "#{@dst_art}"` end @in_files.each_with_index do |src, i| next if File.exist? @out_files[i] src = Shellwords.escape(src) dst = Shellwords.escape(@out_files[i]) queue.push "ffmpeg -i #{src} -acodec #{opts.codec} -map a #{dst}" end puts "Appended #{@in_files.count} tracks to queue for #{@genre}/#{@album}" end def flac_to_aac opts, queue unless Dir.exist? "#{@dst_root}/#{@genre}/#{@album}" puts "Creating #{@dst_root}/#{@genre}/#{@album}" `mkdir -p "#{@dst_root}/#{@genre}/#{@album}"` end unless File.exist? @dst_art puts "Copying #{@src_art}" `cp "#{@src_art}" "#{@dst_art}"` end @in_files.each_with_index do |src, i| next if File.exist? @out_files[i] src = Shellwords.escape(src) dst = Shellwords.escape(@out_files[i]) queue.push "afconvert -d #{opts.codec} -f #{opts.container} -b #{opts.bitrate} #{src} -o #{dst}" end puts "Appended #{@in_files.count} tracks to queue for #{@genre}/#{@album}" end end class Library attr_accessor :queue, :genres, :albums def initialize src, dst @src_root = src @dst_root = dst @conversion_queue = Queue.new @genres = Dir.open(src).children @albums = [] @genres.each do |genre| genre_albums = Dir.open(@src_root + "/" + genre).children genre_albums.select! { |a| not a.start_with? "." } genre_albums.each { |album| @albums.push Album.new(@src_root, @dst_root, genre, album) } end end def convert_all opts case opts.codec when "alac" then @albums.each { |album| album.flac_to_alac(opts, @conversion_queue) } when "aac" then @albums.each { |album| album.flac_to_aac(opts, @conversion_queue) } else exit 1 end workers = [] WORKER_COUNT.times do |i| workers.push Thread.new do puts "Starting worker #{i}" until @conversion_queue.empty? command = @conversion_queue.pop(true) rescue nil if command then `#{command}` end end end end workers.each { |worker| worker.join } puts "Complete" end end library = Library.new(FLAC_ROOT, ALAC_ROOT) library.convert_all TranscoderOpts.new("alac", "m4af", nil)
Owntone
So, with my now-Apple-certified library, I needed a DAAP server. Owntone is such a DAAP server. Highly portable (it's just C), and a doddle to set up. In my case I built it from source with the web interface disabled:
./configure --disable-spotify --disable-webinterface --without-libwebsockets
After which, I configured it to look at my new ALAC library:
directories = { "/mnt/H2C/music/alac" }
Disappointment
Of course, it doesn't work. Owntone works. The files are fine. I can play from Owntone on a Mac without issue. But the geniuses at Apple decided that if you want to stream music from an iTunes Library (well, yes, that's what Owntone emulates) to tvOS - you should buy an Apple Music subscription.
Oh do sod off.
I suppose I'll end up writing an article about knocking up a Jellyfin client for tvOS at some point. I don't know swift, nor have any particular desire to know it. But I really do just want a basic genre/album/track selection. With a bit of album art taken from folder.jpg. Jellyfin is already running on my network, so I might as well use that API. We'll see.