Skip to content

Instantly share code, notes, and snippets.

@SwagDevOps
Last active April 8, 2021 22:17
Show Gist options
  • Save SwagDevOps/87aff6f0b44c7779f97a5fd9eaf12c28 to your computer and use it in GitHub Desktop.
Save SwagDevOps/87aff6f0b44c7779f97a5fd9eaf12c28 to your computer and use it in GitHub Desktop.
transistor.rb
#!/usr/bin/env ruby
# -*- coding: utf-8 -*-
autoload(:YAML, 'yaml')
autoload(:Pathname, 'pathname')
autoload(:Etc, 'etc')
# Sample of use:
#
# ```sh
# transistor.rb # Play default (first)
# transistor.rb pulsradio # Play station by name
# transistor.rb 0 # Play station by index
# transistor -\# # Print bash completion
# eval "$(transistor -\#)" # Load completion
# ```
class Transistor
# @return [Hash{Symbol => Object}]
attr_reader :config
def initialize(config: nil)
load_config(config).tap do |c|
@config = {
screensaver: c.fetch(:screensaver, false),
stations: c.fetch('stations') do
{ pulsradio: 'http://www.pulsradio.com/pls/puls-adsl.m3u' }
end.transform_keys(&:to_sym),
msg: {
color: true,
level: 'all=v',
}.merge(c['msg'].to_h.transform_keys(&:to_sym)),
cache: {
enabled: false,
default: 100,
initial: 50 }.merge(c['cache'].to_h.transform_keys(&:to_sym)),
'user-agent': c['user-agent'] || [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0)',
'Gecko/20100101 Firefox/76.0'
].join(' ')
}.transform_values(&:freeze).freeze
end
end
# @type [Hash{Symbol => String}]
def stations
config.fetch(:stations)
end
def call(station: nil)
command_for(station).yield_self { |command| exec(*command) }
end
# @return [Pathname]
def executable
Pathname.new(__FILE__).realpath
end
# Provide bash completion.
#
# Sample of use:
# ```sh
# eval "$(transistor -\#)"
# ```
#
# @return [String]
def completion
[
'_%{prog_name}_complete() {',
' local curr="${COMP_WORDS[COMP_CWORD]}"',
' local size="${#COMP_WORDS[@]}"',
' test "$size" -lt 3 && {',
' COMPREPLY=($(compgen -W "$(%{executable} -?)" -- "$curr"))',
' }',
' return 0',
'}',
'complete -F _%{prog_name}_complete %{prog_name}',
].join("\n").yield_self do |s|
(s % {
executable: executable,
prog_name: executable.basename('.*').to_s
}).freeze
end
end
class << self
def config_name
'transistor'
end
end
protected
# @type [Hash{Symbol => Object}]
attr_reader :config
# @param [String|Symbol|nil] station
#
# @return [Symbol]
# @raise [RuntimeError]
def resolve(station)
station.tap do
return stations.keys.fetch(0) if station.nil?
if station.is_a?(Integer) or station.match(/^[0-9]+$/)
return stations.keys.fetch(station.to_i)
end
unless stations.key?(station.to_sym)
raise "Can not resolve #{station.inspect}"
end
end.to_sym
end
# @return [Array<String>]
def command_for(station)
[
'mpv',
'--no-video',
'--ytdl=no',
config[:screensaver].yield_self do |v|
"--stop-screensaver=#{v ? 'no' : 'yes'}"
end,
config[:msg][:color].yield_self do |v|
"--msg-color=#{v ? 'yes' : 'no'}"
end,
config[:msg][:level].yield_self do |v|
v ? "--msg-level=#{v}" : nil
end,
config.fetch(:'user-agent').yield_self do |v|
v ? "--user-agent=#{v}" : nil
end,
config[:cache][:enabled].yield_self do |v|
"--cache=#{v ? 'yes' : 'no'}"
end,
config[:cache][:default].yield_self do |v|
v ? "--cache-default=#{v}" : nil
end,
config[:cache][:initial].yield_self do |v|
v ? "--cache-initial=#{v}" : nil
end,
resolve(station).yield_self { |k| stations.fetch(k.to_sym) },
].compact.map(&:freeze)
end
def load_config(filepath = nil)
lambda do
return Pathname.new(filepath) unless filepath.nil?
ENV.fetch('XDG_CONFIG') do
Pathname.new(Etc.getpwnam(Etc.getlogin).dir).join('.config')
end.yield_self { |fp| Pathname.new(fp) }.join("#{self.class.config_name}.yml")
end.call.yield_self do |file|
file.file? and file.readable? ? YAML.safe_load(file.read) : {}
end
end
end
# Execution ----------------------------------------------------------
if __FILE__ == $0
Transistor.new.yield_self do |radio|
case ARGV[0]
when '-#'
radio.completion.tap { |s| puts(s) }
when '-?'
radio.stations.keys.map(&:to_s).sort.join("\n").tap { |s| puts(s) }
else
radio.call(station: ARGV[0]) if __FILE__ == $0
end
end
end
# ~/.config/transistor.yml
---
cache:
enabled: true
default: 100
initial: 50
msg:
level: statusline=status
stations:
pulsradio: 'http://www.pulsradio.com/pls/puls-adsl.m3u'
q-dance: 'https://19983.live.streamtheworld.com/Q_DANCE.mp3'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment