audio_addict 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e16f2df339290b35db4fa973f59acad53659d70d24221d922d5f393ceb9e18be
4
+ data.tar.gz: 99b1ec5b9da37677b7b8766c1c78de6ce4ea8d1344cfd8738a0aa4e17fa77ffc
5
+ SHA512:
6
+ metadata.gz: b4ba61aa84cb4732f986fe190a075d6318e56d10d7314f9796be032020e1a3e1c20018f203ea6bb1eab6c9bd8bd005e3debbeb110e6f99f91642885e76325205
7
+ data.tar.gz: e78661eb554bcc03bb151ca4098e3710b3acfb85fe94137a3e35ac3058bcf674969fa5a8dbb4ed30c45541166444c7597449a06cf22c97124bbbb7b76d218d24
data/README.md ADDED
@@ -0,0 +1,8 @@
1
+ AudioAddict Command Line
2
+ ==================================================
3
+
4
+ Work in progress, documentation soon.
5
+
6
+ [API Reference (Archived)][api_reference]
7
+
8
+ [api_reference]: https://web.archive.org/web/20140426192326/http://tobiass.eu/api-doc.html#trackinfo
data/bin/radio ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ require 'audio_addict'
3
+
4
+ router = AudioAddict::CLI.router
5
+
6
+ begin
7
+ exit router.run ARGV
8
+ rescue AudioAddict::Interrupt, TTY::Reader::InputInterrupt
9
+ say "\nGoodbye"
10
+ exit 1
11
+ rescue => e
12
+ say ""
13
+ if ENV['DEBUG']
14
+ puts e.backtrace.reverse
15
+ say ""
16
+ end
17
+ say "!undred!ERROR: #{e.class}"
18
+ say e.message
19
+ exit 1
20
+ end
@@ -0,0 +1,9 @@
1
+ require 'requires'
2
+ require 'byebug' if ENV['BYEBUG']
3
+ requires \
4
+ 'audio_addict/exceptions',
5
+ 'audio_addict/cache',
6
+ 'audio_addict/inspectable',
7
+ 'audio_addict/auto_properties',
8
+ 'audio_addict/commands/base',
9
+ 'audio_addict'
@@ -0,0 +1,75 @@
1
+ require 'httparty'
2
+
3
+ module AudioAddict
4
+ class API
5
+ include HTTParty
6
+ include Cache
7
+
8
+ base_uri 'https://api.audioaddict.com/v1'
9
+
10
+ attr_accessor :network
11
+
12
+ def initialize(network)
13
+ @network = network
14
+ end
15
+
16
+ def login(username, password)
17
+ session = session(username, password)
18
+ Config.session_key = session['key']
19
+ Config.listen_key = session['member']['listen_key']
20
+ Config.save
21
+ end
22
+
23
+ def get(path, args={})
24
+ response http.get "/#{network}/#{path}", headers: headers, body: args
25
+ end
26
+
27
+ def post(path, args={})
28
+ response http.post "/#{network}/#{path}", headers: headers, body: args
29
+ end
30
+
31
+ def delete(path, args={})
32
+ response http.delete "/#{network}/#{path}", headers: headers, body: args
33
+ end
34
+
35
+ def logged_in?
36
+ session_key and listen_key
37
+ end
38
+
39
+ def basic_auth
40
+ http.basic_auth 'streams', 'diradio'
41
+ end
42
+
43
+ def session(username, password)
44
+ params = { member_session: { username: username, password: password } }
45
+ cache.get "/#{network}/member_sessions" do
46
+ basic_auth
47
+ response http.post "/#{network}/member_sessions", body: params
48
+ end
49
+ end
50
+
51
+ def session_key
52
+ Config.session_key
53
+ end
54
+
55
+ def listen_key
56
+ Config.listen_key
57
+ end
58
+
59
+ private
60
+
61
+ def response(httparty_response)
62
+ raise APIError.new httparty_response unless httparty_response.success?
63
+ JSON.parse httparty_response.body
64
+ end
65
+
66
+ def http
67
+ @http ||= self.class
68
+ end
69
+
70
+ def headers
71
+ { "X-Session-Key" => session_key }
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,13 @@
1
+ module AudioAddict
2
+ module AutoProperties
3
+ attr_reader :properties
4
+
5
+ def method_missing(method_sym, *args, &block)
6
+ respond_to?(method_sym) ? properties[method_sym.to_s] : super
7
+ end
8
+
9
+ def respond_to_missing?(method_sym, _include_private = false)
10
+ properties.has_key?(method_sym.to_s) || super
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ require 'lightly'
2
+
3
+ module AudioAddict
4
+ module Cache
5
+
6
+ def cache
7
+ @cache ||= Lightly.new life: '6h', dir: cache_dir
8
+ end
9
+
10
+ def cache_dir
11
+ @cache_dir ||= ENV.fetch('AUDIO_ADDICT_CACHE_PATH', "#{Dir.home}/.audio_addict/cache")
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,60 @@
1
+ module AudioAddict
2
+ class Channel
3
+ include Cache
4
+ include AutoProperties
5
+ include Inspectable
6
+
7
+ attr_reader :radio
8
+
9
+ def initialize(radio, properties)
10
+ @radio, @properties = radio, properties
11
+ end
12
+
13
+ def inspectable
14
+ [:key, :name, :id]
15
+ end
16
+
17
+ def active?
18
+ # Seems like each network has a different way of marking inactive channels.
19
+ # This is where we normalize it
20
+ return false if !properties['asset_id']
21
+ return false if name[0] == 'X' and key[0] != 'x'
22
+ return true
23
+ end
24
+
25
+ def track_history
26
+ @track_history ||= track_history!
27
+ end
28
+
29
+ def track_history!
30
+ response = radio.api.get "track_history/channel/#{id}"
31
+ response.map { |track| Track.new self, track }
32
+ end
33
+
34
+ def current_track
35
+ track_history.first
36
+ end
37
+
38
+ def vote(direction = :up)
39
+ track_id = current_track.id
40
+ endpoint = "tracks/#{track_id}/vote/#{id}"
41
+
42
+ if direction == :delete
43
+ radio.api.delete endpoint
44
+ else
45
+ radio.api.post "#{endpoint}/#{direction}"
46
+ end
47
+
48
+ log_like if direction == :up and Config.like_log
49
+ end
50
+
51
+ private
52
+
53
+ def log_like
54
+ message = "#{radio.name} :: #{name} :: #{current_track.artist} :: #{current_track.title}"
55
+ file = Config.like_log
56
+ File.append file, message unless File.contains? file, message
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,19 @@
1
+ module AudioAddict
2
+ class CLI
3
+ def self.router
4
+ router = MisterBin::Runner.new version: VERSION,
5
+ header: "AudioAddict Radio Utilities"
6
+
7
+ router.route 'login', to: Commands::LoginCmd
8
+ router.route 'status', to: Commands::StatusCmd
9
+ router.route 'set', to: Commands::SetCmd
10
+ router.route 'channels', to: Commands::ChannelsCmd
11
+ router.route 'now', to: Commands::NowCmd
12
+ router.route 'vote', to: Commands::VoteCmd
13
+ router.route 'playlist', to: Commands::PlaylistCmd
14
+
15
+ router
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,39 @@
1
+ require 'mister_bin'
2
+ require 'colsole'
3
+ require 'tty/prompt'
4
+
5
+ module AudioAddict
6
+ module Commands
7
+ class Base < MisterBin::Command
8
+
9
+ def needs(*config_keys)
10
+ missing = []
11
+ config_keys.each do |key|
12
+ missing.push key unless Config.has_key? key
13
+ end
14
+
15
+ if missing.any?
16
+ missing_keys = missing.map { |k| "- !txtblu!#{k}" }.join "\n"
17
+ raise ConfigError, "This operation requires some config parameters that are missing:\n#{missing_keys}"
18
+ end
19
+ end
20
+
21
+ def radio
22
+ @radio ||= Radio.new current_network
23
+ end
24
+
25
+ def current_network
26
+ Config.network
27
+ end
28
+
29
+ def current_channel
30
+ @current_channel ||= radio[Config.channel]
31
+ end
32
+
33
+ def prompt
34
+ @prompt ||= TTY::Prompt.new
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,33 @@
1
+ module AudioAddict
2
+ module Commands
3
+ class ChannelsCmd < Base
4
+ summary "Show list of channels"
5
+
6
+ help "List and search channels in the currently set radio network"
7
+
8
+ usage "radio channels [SEARCH]"
9
+ usage "radio channels --help"
10
+
11
+ param "SEARCH", "Channel name or a partial name to search for."
12
+
13
+ example "radio channels"
14
+ example "radio channels metal"
15
+
16
+ def run(args)
17
+ needs :network
18
+
19
+ say "!undgrn!#{radio.name}\n"
20
+
21
+ search = args['SEARCH']
22
+
23
+ channels = search ? radio.search(search) : radio.channels
24
+
25
+ channels = channels.values
26
+ channels.each do |channel|
27
+ say "!txtgrn!#{channel.key.rjust 25} !txtblu!#{channel.name.strip}"
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,39 @@
1
+ module AudioAddict
2
+ module Commands
3
+ class LoginCmd < Base
4
+ summary "Save login credentials"
5
+
6
+ help "Get a session key from AudioAddict API. This operation should only be done once."
7
+
8
+ usage "radio login"
9
+ usage "radio login --help"
10
+
11
+ def run(args)
12
+ proceed = true
13
+
14
+ if radio.api.logged_in?
15
+ say "!txtylw!You are already logged in"
16
+ proceed = prompt.yes? "Login again?"
17
+ end
18
+
19
+ login_prompt if proceed
20
+ end
21
+
22
+ private
23
+
24
+ def login_prompt
25
+ user = prompt.ask "Username :"
26
+ pass = prompt.mask "Password :"
27
+
28
+ if user and pass
29
+ say "Logging in... "
30
+ radio.api.login user, pass
31
+ resay "!txtgrn!Saved"
32
+ else
33
+ say "!txtred!Aborted"
34
+ end
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,25 @@
1
+ module AudioAddict
2
+ module Commands
3
+ class NowCmd < Base
4
+ summary "Show network, channel and playing track"
5
+
6
+ help "Display the configured network and channel, as well as the currently playing track."
7
+
8
+ usage "radio now"
9
+ usage "radio now --help"
10
+
11
+ def run(args)
12
+ needs :network, :channel
13
+
14
+ say "!txtblu! Network !txtrst!: !txtgrn!#{radio.name}!txtrst! # #{radio.network}"
15
+ say "!txtblu! Channel !txtrst!: !txtgrn!#{current_channel.name}!txtrst! # #{current_channel.key}"
16
+ say "!txtblu! Track !txtrst!: ... "
17
+
18
+ track = current_channel.current_track
19
+ resay "!txtblu! Track !txtrst!: !txtgrn!#{track.title.strip}"
20
+ say "!txtblu! By !txtrst!: !txtgrn!#{track.artist.strip}"
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,107 @@
1
+ module AudioAddict
2
+ module Commands
3
+ class PlaylistCmd < Base
4
+ summary "Generate playlists"
5
+
6
+ usage "radio playlist init NAME"
7
+ usage "radio playlist generate NAME"
8
+ usage "radio playlist --help"
9
+
10
+ command "init", "Create a playlist configuration file. This step is required prior to using the generate command."
11
+ command "generate", "Generate a playlist file based on the configuration file."
12
+
13
+ param "NAME", "The name of the playlist without any extension"
14
+
15
+ example "radio playlist init MyRockMusic"
16
+ example "radio playlist generate MyRockMusic"
17
+
18
+ def run(args)
19
+ needs :network, :channel, :listen_key
20
+
21
+ @args = args
22
+ @filename = "#{@args['NAME']}"
23
+
24
+ init_command if args['init']
25
+ generate_command if args['generate']
26
+ end
27
+
28
+ private
29
+
30
+ def init_command
31
+ @outfile = "#{@filename}.yml"
32
+
33
+ say "!txtred!Warning!txtrst!: !txtgrn!#{@outfile}!txtrst! already exists!" if File.exist? @outfile
34
+ proceed = prompt.yes? "Create #{@outfile}?"
35
+ if proceed
36
+ generate_config
37
+ say ""
38
+ generate_command # we also generate the playlist
39
+ end
40
+ end
41
+
42
+ def generate_command
43
+ @infile = "#{@filename}.yml"
44
+ @outfile = "#{@filename}.pls"
45
+
46
+ if !File.exist? @infile
47
+ say "!txtred!Cannot find #{@infile}"
48
+ else
49
+ say "!txtred!Warning!txtrst!: !txtgrn!#{@outfile}!txtrst! already exists!" if File.exist? @outfile
50
+ proceed = prompt.yes? "Create #{@outfile}?"
51
+ generate_playlist if proceed
52
+ end
53
+ end
54
+
55
+ def generate_playlist
56
+ data = YAML.load_file @infile
57
+ template = data[:template]
58
+ channels = data[:channels].select { |c| c[:active] }
59
+
60
+ output = []
61
+ output << "[playlist]"
62
+ output << "NumberOfEntries=#{channels.count}"
63
+
64
+ index = 0
65
+
66
+ channels.each do |channel|
67
+ index += 1
68
+ output << "File#{index}=#{template}" % template_params(channel[:key])
69
+ output << "Title#{index}=#{channel[:name]}"
70
+ output << "Length#{index}=0"
71
+ end
72
+
73
+ output = output.join("\n") + "\n"
74
+
75
+ File.write @outfile, output
76
+ say "Saved !txtgrn!#{@outfile}"
77
+ end
78
+
79
+ def template_params(channel_key)
80
+ { listen_key: listen_key, channel_key: channel_key }
81
+ end
82
+
83
+ def generate_config
84
+ data = {
85
+ template: "http://prem2.#{radio.domain}:80/%{channel_key}?%{listen_key}"
86
+ }
87
+
88
+ channels = []
89
+
90
+ radio.channels.each do |key, channel|
91
+ channel_data = { name: channel.name, key: key, active: true }
92
+ channels << channel_data
93
+ end
94
+
95
+ data[:channels] = channels
96
+
97
+ File.write @outfile, data.to_yaml
98
+ say "Saved !txtgrn!#{@outfile}"
99
+ end
100
+
101
+ def listen_key
102
+ Config.listen_key
103
+ end
104
+
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,113 @@
1
+ module AudioAddict
2
+ module Commands
3
+ class SetCmd < Base
4
+ summary "Set the radio network and channel"
5
+
6
+ help "Save the channel and network to the config file for future use"
7
+
8
+ usage "radio set [CHANNEL NETWORK]"
9
+ usage "radio set --help"
10
+
11
+ param "CHANNEL", "AudioAddict channel key. You can use a partial key here or leave empty for an interactive prompt."
12
+ param "NETWORK", "AudioAddict network key. You can use a partial key here or leave empty for an interactive prompt."
13
+
14
+ example "radio set"
15
+ example "radio set punk"
16
+ example "radio set dance digitally"
17
+ example "radio set dance di"
18
+ example "radio set metal rockradio"
19
+
20
+ def run(args)
21
+ channel = args['CHANNEL']
22
+ network = args['NETWORK']
23
+
24
+ full_set = (channel and network) || !(channel or network)
25
+
26
+ if full_set
27
+ set_both channel, network
28
+ else
29
+ set_channel channel
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def set_both(channel, network)
36
+ needs :session_key
37
+
38
+ if !network or !Radio.valid_network? network
39
+ network_menu network
40
+ elsif Radio.valid_network? network
41
+ save_network network
42
+ end
43
+
44
+ set_channel channel
45
+ end
46
+
47
+ def set_channel(channel)
48
+ needs :network, :session_key
49
+
50
+ if !channel
51
+ channel_menu
52
+
53
+ elsif radio.valid_channel? channel
54
+ save_channel channel
55
+
56
+ elsif radio.search(channel).any?
57
+ channel_menu channel
58
+
59
+ else
60
+ say "!txtred!Invalid channel: #{radio.name} > #{channel}"
61
+
62
+ end
63
+ end
64
+
65
+ def channel_menu(channel = nil)
66
+ list = channel ? radio.search(channel).values : radio.channels.values
67
+
68
+ if list.count == 1
69
+ save_channel list.first.key
70
+ else
71
+ answer = channel_prompt list
72
+ save_channel(answer, echo: false) unless answer == :abort
73
+ end
74
+ end
75
+
76
+ def network_menu(network = nil)
77
+ list = Radio.networks network
78
+
79
+ if list.count == 1
80
+ save_network list.keys.first
81
+ else
82
+ answer = network_prompt list
83
+ save_network(answer, echo: false) unless answer == :abort
84
+ end
85
+ end
86
+
87
+ def channel_prompt(channels)
88
+ options = channels.map { |channel| ["#{channel.name.ljust 20} # #{channel.key}", channel.key] }.to_h
89
+ options = { "Abort" => :abort }.merge options
90
+ prompt.select "Channel :", options, marker: '>', filter: true
91
+ end
92
+
93
+ def network_prompt(networks)
94
+ options = networks.invert
95
+ options["Abort"] = :abort
96
+ prompt.select "Network :", options, marker: '>', filter: true
97
+ end
98
+
99
+ def save_channel(channel, echo: true)
100
+ Config.channel = channel
101
+ Config.save
102
+ say "Channel : !txtgrn!#{radio.name} > #{current_channel.name}!txtrst! # #{channel}" if echo
103
+ end
104
+
105
+ def save_network(network, echo: true)
106
+ Config.network = network
107
+ Config.save
108
+ say "Network : !txtgrn!#{radio.name}!txtrst! # #{network}" if echo
109
+ end
110
+
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,49 @@
1
+ module AudioAddict
2
+ module Commands
3
+ class StatusCmd < Base
4
+ summary "Show configuration status"
5
+
6
+ usage "radio status [--unsafe]"
7
+ usage "radio status --help"
8
+
9
+ option "-u --unsafe", "Show the full session and listen keys"
10
+
11
+ def run(args)
12
+ say "!txtblu! Config Path !txtrst!: !txtgrn!#{Config.path}"
13
+
14
+ say "!txtblu! Session Key !txtrst!: "
15
+ if Config.session_key
16
+ key = Config.session_key
17
+ display_key = args['--unsafe'] ? key : "***#{key[-4, 4]}"
18
+ say "!txtgrn!#{display_key}"
19
+ else
20
+ say "!txtred!<Unset>!txtrst! - run !txtpur!radio login!txtrst! to fix"
21
+ end
22
+
23
+ say "!txtblu! Listen Key !txtrst!: "
24
+ if Config.listen_key
25
+ key = Config.listen_key
26
+ display_key = args['--unsafe'] ? key : "***#{key[-4, 4]}"
27
+ say "!txtgrn!#{display_key}"
28
+ else
29
+ say "!txtred!<Unset>!txtrst! - run !txtpur!radio login!txtrst! to fix"
30
+ end
31
+
32
+ say "!txtblu! Network !txtrst!: "
33
+ if Config.network
34
+ say "!txtgrn!#{Config.network}"
35
+ else
36
+ say "!txtred!<Unset>!txtrst! - run !txtpur!radio set!txtrst! to fix"
37
+ end
38
+
39
+ say "!txtblu! Channel !txtrst!: "
40
+ if Config.channel
41
+ say "!txtgrn!#{Config.channel}"
42
+ else
43
+ say"!txtred!<Unset>!txtrst! - run !txtpur!radio channel!txtrst! to fix"
44
+ end
45
+
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,34 @@
1
+ module AudioAddict
2
+ module Commands
3
+ class VoteCmd < Base
4
+ summary "Vote on the currently playing track"
5
+
6
+ help "Start an interactive voting prompt for the currently playing track."
7
+
8
+ usage "radio vote"
9
+ usage "radio vote --help"
10
+
11
+ def run(args)
12
+ needs :network, :channel, :session_key
13
+
14
+ NowCmd.new.run args
15
+ puts ""
16
+ answer = get_user_vote
17
+ unless answer == :abort
18
+ say "Voting... "
19
+ current_channel.vote answer
20
+ resay "!txtgrn!Voted"
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def get_user_vote
27
+ options = { "Like" => :up, "Dislike" => :down,
28
+ "Unvote" => :delete, "Abort" => :abort }
29
+ prompt.select "Your Vote :", options, marker: '>'
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,58 @@
1
+ require 'yaml'
2
+
3
+ module AudioAddict
4
+ class Config
5
+ class << self
6
+ attr_writer :path
7
+
8
+ def method_missing(name, *args, &_blk)
9
+ if name.to_s.end_with? '='
10
+ name = name[0..-2].to_sym
11
+ properties[name] = args.first
12
+ else
13
+ properties[name]
14
+ end
15
+ end
16
+
17
+ def delete(key)
18
+ properties.delete key
19
+ end
20
+
21
+ def save
22
+ File.deep_write path, properties.to_yaml
23
+ end
24
+
25
+ def valid?
26
+ required_keys.each do |key|
27
+ return false unless has_key? key
28
+ end
29
+ true
30
+ end
31
+
32
+ def required_keys
33
+ [:network, :channel, :session_key]
34
+ end
35
+
36
+ def has_key?(key)
37
+ properties.has_key? key
38
+ end
39
+
40
+ def properties
41
+ @properties ||= properties!
42
+ end
43
+
44
+ def properties!
45
+ File.exist?(path) ? YAML.load_file(path) : {}
46
+ end
47
+
48
+ def path
49
+ @path ||= ENV.fetch('AUDIO_ADDICT_CONFIG_PATH', default_path)
50
+ end
51
+
52
+ def default_path
53
+ "#{Dir.home}/.audio_addict/config"
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,22 @@
1
+ module AudioAddict
2
+ class Error < StandardError; end
3
+
4
+ class Interrupt < Error; end
5
+ class ArgumentError < Error; end
6
+ class ConfigError < Error; end
7
+
8
+ class LoginError < Error
9
+ def initialize(msg="This operation requires logging in")
10
+ super
11
+ end
12
+ end
13
+
14
+ class APIError < Error
15
+ attr_reader :response
16
+
17
+ def initialize(response)
18
+ @response = response
19
+ super "#{response.code} #{response.message}:\n#{response.body}"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ require 'fileutils'
2
+
3
+ class File
4
+ def self.contains?(file, content)
5
+ foreach file do |line|
6
+ return true if line.chomp == content
7
+ end
8
+ return false
9
+ end
10
+
11
+ def self.append(file, content)
12
+ open(file, 'a') { |f| f.puts content }
13
+ end
14
+
15
+ def self.deep_write(file, content)
16
+ dir = File.dirname file
17
+ FileUtils.mkdir_p dir unless Dir.exist? dir
18
+ File.write file, content
19
+ end
20
+ end
@@ -0,0 +1,8 @@
1
+ module AudioAddict
2
+ module Inspectable
3
+ def inspect
4
+ keys = inspectable.map { |k| %Q[@#{k}="#{send k}"] }
5
+ "#<#{self.class} #{keys.join ', '}>"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,95 @@
1
+ module AudioAddict
2
+ class Radio
3
+ include Cache
4
+ include Inspectable
5
+
6
+ attr_reader :network
7
+
8
+ NETWORKS = {
9
+ di: "Digitally Imported",
10
+ rockradio: "Rock Radio",
11
+ radiotunes: "Radio Tunes",
12
+ jazzradio: "Jazz Radio",
13
+ classicalradio: "Classical Radio"
14
+ }
15
+
16
+ DOMAINS = {
17
+ di: "di.fm",
18
+ rockradio: "rockradio.com",
19
+ radiotunes: "radiotunes.com",
20
+ jazzradio: "jazzradio.com",
21
+ classicalradio: "classicalradio.com"
22
+ }
23
+
24
+ def self.networks(search = nil)
25
+ if search
26
+ result = NETWORKS.select { |k, v| "#{k} #{v}".downcase.include? search.downcase }
27
+ result.any? ? result : NETWORKS
28
+ else
29
+ NETWORKS
30
+ end
31
+ end
32
+
33
+ def self.valid_network?(network)
34
+ NETWORKS.keys.include? network.to_sym
35
+ end
36
+
37
+ def initialize(network)
38
+ @network = network
39
+ end
40
+
41
+ def inspectable
42
+ [:network]
43
+ end
44
+
45
+ def name
46
+ NETWORKS[network.to_sym]
47
+ end
48
+
49
+ def domain
50
+ DOMAINS[network.to_sym]
51
+ end
52
+
53
+ def channels
54
+ @channels ||= channels!
55
+ end
56
+
57
+ def search(query)
58
+ channels.select do |key, channel|
59
+ "#{key} #{channel.name.downcase}".include? query.downcase
60
+ end
61
+ end
62
+
63
+ def [](channel_key)
64
+ channels[channel_key]
65
+ end
66
+
67
+ def valid_channel?(channel)
68
+ channels.keys.include? channel
69
+ end
70
+
71
+ def favorites
72
+ api.member['network_favorite_channels']
73
+ end
74
+
75
+ def api
76
+ @api ||= API.new network
77
+ end
78
+
79
+ private
80
+
81
+ def channels!
82
+ response = cache.get "#{network}/channels" do
83
+ api.get 'channels'
84
+ end
85
+
86
+ result = {}
87
+ response.map do |channel|
88
+ key = channel['key']
89
+ candidate = Channel.new self, channel
90
+ result[key] = candidate if candidate.active?
91
+ end
92
+ result.sort_by { |key, channel| channel.name }.to_h
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,25 @@
1
+ module AudioAddict
2
+ class Track
3
+ include AutoProperties
4
+ include Inspectable
5
+
6
+ attr_reader :channel
7
+
8
+ def initialize(channel, properties)
9
+ @channel, @properties = channel, properties
10
+ end
11
+
12
+ def inspectable
13
+ [:title, :artist, :id]
14
+ end
15
+
16
+ def id
17
+ properties['track_id']
18
+ end
19
+
20
+ # This is only here due to the global Runfile method with the same name
21
+ def title
22
+ properties['title']
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module AudioAddict
2
+ VERSION = "0.0.2"
3
+ end
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: audio_addict
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Danny Ben Shitrit
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-11-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colsole
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: httparty
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.16'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.16'
41
+ - !ruby/object:Gem::Dependency
42
+ name: lightly
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.3'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: mister_bin
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.4'
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: 0.4.1
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - "~>"
70
+ - !ruby/object:Gem::Version
71
+ version: '0.4'
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 0.4.1
75
+ - !ruby/object:Gem::Dependency
76
+ name: requires
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.1'
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '0.1'
89
+ - !ruby/object:Gem::Dependency
90
+ name: tty-prompt
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0.17'
96
+ type: :runtime
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '0.17'
103
+ description: Command line for playlist management and voting for AudioAddict radio
104
+ networks
105
+ email: db@dannyben.com
106
+ executables:
107
+ - radio
108
+ extensions: []
109
+ extra_rdoc_files: []
110
+ files:
111
+ - README.md
112
+ - bin/radio
113
+ - lib/audio_addict.rb
114
+ - lib/audio_addict/api.rb
115
+ - lib/audio_addict/auto_properties.rb
116
+ - lib/audio_addict/cache.rb
117
+ - lib/audio_addict/channel.rb
118
+ - lib/audio_addict/cli.rb
119
+ - lib/audio_addict/commands/base.rb
120
+ - lib/audio_addict/commands/channels.rb
121
+ - lib/audio_addict/commands/login.rb
122
+ - lib/audio_addict/commands/now.rb
123
+ - lib/audio_addict/commands/playlist.rb
124
+ - lib/audio_addict/commands/set.rb
125
+ - lib/audio_addict/commands/status.rb
126
+ - lib/audio_addict/commands/vote.rb
127
+ - lib/audio_addict/config.rb
128
+ - lib/audio_addict/exceptions.rb
129
+ - lib/audio_addict/extensions/file.rb
130
+ - lib/audio_addict/inspectable.rb
131
+ - lib/audio_addict/radio.rb
132
+ - lib/audio_addict/track.rb
133
+ - lib/audio_addict/version.rb
134
+ homepage: https://github.com/dannyben/audio_addict
135
+ licenses:
136
+ - MIT
137
+ metadata: {}
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: 2.4.0
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubyforge_project:
154
+ rubygems_version: 2.7.6
155
+ signing_key:
156
+ specification_version: 4
157
+ summary: AudioAddict Command Line
158
+ test_files: []