Skip to content

Instantly share code, notes, and snippets.

@cybniv
Created March 24, 2019 15:23
Show Gist options
  • Save cybniv/9c813813a7e7945ef0cc150a481fdf28 to your computer and use it in GitHub Desktop.
Save cybniv/9c813813a7e7945ef0cc150a481fdf28 to your computer and use it in GitHub Desktop.
this script extracts unencrypted FLACs from an Android device running Tidal.
#!/usr/bin/env ruby
require 'bundler/inline'
gemfile do
ruby '>=2.5'
source 'https://rubygems.org'
gem 'rb-inotify'
gem 'pry-byebug'
end
require 'time'
require 'fileutils'
# create logfile
## must be playing on any bluetooth device for this to work
logfile = "#{Dir.mktmpdir}/tidal.log"
pid = spawn("adb logcat -e currsong", out: logfile)
Process.detach(pid)
sleep 1
# setup watcher
notifier = INotify::Notifier.new
n = 0
notifier.watch(logfile, :modify) { |event|
n += 1
next unless n.even?
string = `tail -n 1 #{logfile}`
title = string.match(/title="([^"]*)"/)[1]
title_c = clean_name(title)
artist = string.match(/artist="([^"]*)"/)[1]
artist_c = clean_name(artist)
album = string.match(/album="([^"]*)"/)[1]
album_c = clean_name(album)
track = string.match(/trackPosition=(\d\/\d)/)[1]
track_c = track.dup
track_c.gsub!('/', '-')
files = `adb shell "su -c 'ls --full-time /mnt/runtime/default/emulated/0/Android/data/com.aspiro.tidal/files/offline'"`
## unfortunately, ls output on android's busybox doesn't respect nanoseconds in sort order, so we have to sort ourselves
output_array = files.split("\n").collect{|n|
next if n.length < 100
time_string = n.split.drop(5)[0..2].join(' ')
# this will return [float_time, filename]
[Time.parse(time_string).to_f, n.split[-1]]
}.compact.sort
# get name of newest file out of sorted array
file = output_array[-1][-1]
puts "get decrypted #{file}"
`adb shell su -c 'cp /mnt/runtime/default/emulated/0/Android/data/com.aspiro.tidal/files/offline/#{file} /data/local/tmp/'`
`adb shell su -c 'chmod 644 /data/local/tmp/#{file}'`
download_dir = "tidal_download/#{album_c}"
FileUtils.mkdir_p(download_dir) unless File.exists?(download_dir)
# format = "#{track_c}-#{artist_c}-#{title_c}"
format = "#{artist_c}-#{title_c}"
Dir.chdir(download_dir) do
system("adb", "pull", "/data/local/tmp/#{file}", "#{format}.flac")
system("adb", "shell", "rm", "/data/local/tmp/#{file}")
puts "wrote #{format}.flac"
system( "metaflac", "--set-tag=TITLE=#{title}", "--set-tag=ARTIST=#{artist}", "--set-tag=ALBUM=#{album}", "#{format}.flac" )
puts "wrote tags to #{format}.flac"
puts "#####################################"
end
puts "\n\nplaying next file\n\n"
spawn("adb", "shell", "media", "dispatch", "next")
}
def clean_name(string)
dupe = string.dup
dupe.gsub!(' / ', '_')
dupe.gsub!('/', '_')
dupe.gsub!(' ', '_')
dupe
end
notifier.run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment