audio_addict 0.2.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e5425d1af3034b2836feb21165d62910d1498abe58debb6a3845b210f8ea214e
4
- data.tar.gz: a15802a7ce577221ff14a972c4d23e875c0f912b3cb87cfd8b3c38d65133a228
3
+ metadata.gz: 0b9ee1765ac530791d434e0ae9166dcbbd6c78af0331e6744b798b042a4855cc
4
+ data.tar.gz: 36572839fec8085f9cdd3e55c0bf092319dac42f707c404097123454fd3f8a59
5
5
  SHA512:
6
- metadata.gz: ca64d5708f21dc7509232c61e5cb5a8f18d5fc797f5b843ac690d60e731ac82a6a357f8a125b777ef5e686bcec02817611be8db054c159c368beb6f7b66b17d7
7
- data.tar.gz: cb9098e36ab1ecf46c9cdcd170b952fc9a66c69b52cf2a29751522a0d505753c61a5d1f1f86e5d5211b5e11d4f7501e8f2322309d62a6c7d0d8337d64ac5790d
6
+ metadata.gz: 44e14f1f07236555a67735e407c6aa9ae2849fb7db87b67168f9c1ddb233ee8007cfcd0e77c7462fe26df2a92c2cc4e780513e7a2ab4f522e9aa28f9d3886370
7
+ data.tar.gz: c5ae90ef4ae1304a684f92679e62f66e8adaf75e65a94410b4259cbefec4aba7983e829dde112f2192564c9ce5fd242eeb0ee1fccb0185dd634e4f14e8a2398d
data/README.md CHANGED
@@ -2,7 +2,7 @@ AudioAddict Command Line
2
2
  ==================================================
3
3
 
4
4
  [![Gem Version](https://badge.fury.io/rb/audio_addict.svg)](https://badge.fury.io/rb/audio_addict)
5
- [![Build Status](https://travis-ci.com/DannyBen/audio_addict.svg?branch=master)](https://travis-ci.com/DannyBen/audio_addict)
5
+ [![Build Status](https://github.com/DannyBen/audio_addict/workflows/Test/badge.svg)](https://github.com/DannyBen/audio_addict/actions?query=workflow%3ATest)
6
6
  [![Maintainability](https://api.codeclimate.com/v1/badges/91e1a8251b771881bf6b/maintainability)](https://codeclimate.com/github/DannyBen/audio_addict/maintainability)
7
7
 
8
8
  ---
@@ -41,11 +41,13 @@ Features
41
41
  - [RadioTunes]
42
42
  - [JazzRadio]
43
43
  - [ClassicalRadio]
44
+ - [ZenRadio]
44
45
  - View list of channels
45
46
  - View currently playing track
46
47
  - Vote on the currently playing track
47
48
  - Save a log of a all your liked tracks
48
49
  - Generate playlists (requires a premium account)
50
+ - Download songs from YouTube (requires [youtube-dl][youtube-dl])
49
51
 
50
52
 
51
53
  Usage
@@ -68,6 +70,7 @@ Commands:
68
70
  playlist Generate playlists
69
71
  config Manage local configuration
70
72
  log Manage local like log
73
+ download Download songs from YouTube
71
74
  api Make direct calls to the AudioAddict API
72
75
 
73
76
  ```
@@ -80,3 +83,5 @@ Commands:
80
83
  [RadioTunes]: http://www.radiotunes.com
81
84
  [JazzRadio]: http://www.jazzradio.com
82
85
  [ClassicalRadio]: http://www.classicalradio.com
86
+ [ZenRadio]: http://www.zenradio.com
87
+ [youtube-dl]: https://github.com/ytdl-org/youtube-dl/
data/bin/radio CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'audio_addict'
3
+ require 'colsole'
3
4
  include Colsole
4
5
 
5
6
  router = AudioAddict::CLI.router
@@ -7,7 +8,7 @@ router = AudioAddict::CLI.router
7
8
  begin
8
9
  exit router.run ARGV
9
10
 
10
- rescue AudioAddict::Interrupt, TTY::Reader::InputInterrupt
11
+ rescue Interrupt, AudioAddict::Interrupt, TTY::Reader::InputInterrupt
11
12
  say "\nGoodbye"
12
13
  exit 1
13
14
 
@@ -29,4 +30,5 @@ rescue => e
29
30
  say "!undred!ERROR: #{e.class}"
30
31
  say e.message
31
32
  exit 1
33
+
32
34
  end
@@ -1,9 +1,9 @@
1
- require 'requires'
2
- require 'byebug' if ENV['BYEBUG']
1
+ require "requires"
2
+ require "byebug" if ENV["BYEBUG"]
3
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'
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"
@@ -1,11 +1,12 @@
1
- require 'httparty'
1
+ require "httparty"
2
2
 
3
3
  module AudioAddict
4
4
  class API
5
5
  include HTTParty
6
6
  include Cache
7
7
 
8
- base_uri 'https://api.audioaddict.com/v1'
8
+ base_uri "https://api.audioaddict.com/v1"
9
+ debug_output $stderr if ENV['AUDIO_ADDICT_DEBUG']
9
10
 
10
11
  attr_accessor :network
11
12
 
@@ -15,22 +16,22 @@ module AudioAddict
15
16
 
16
17
  def login(username, password)
17
18
  session = session(username, password)
18
- Config.session_key = session['key']
19
- Config.listen_key = session['member']['listen_key']
20
- Config.email = session['member']['email']
21
- Config.premium = session['member']['user_type'] == 'premium'
19
+ Config.session_key = session["key"]
20
+ Config.listen_key = session["member"]["listen_key"]
21
+ Config.email = session["member"]["email"]
22
+ Config.premium = session["member"]["user_type"] == "premium"
22
23
  Config.save
23
24
  end
24
25
 
25
- def get(path, args={})
26
+ def get(path, args = {})
26
27
  response http.get "/#{network}/#{path}", headers: headers, body: args
27
28
  end
28
29
 
29
- def post(path, args={})
30
+ def post(path, args = {})
30
31
  response http.post "/#{network}/#{path}", headers: headers, body: args
31
32
  end
32
33
 
33
- def delete(path, args={})
34
+ def delete(path, args = {})
34
35
  response http.delete "/#{network}/#{path}", headers: headers, body: args
35
36
  end
36
37
 
@@ -39,13 +40,13 @@ module AudioAddict
39
40
  end
40
41
 
41
42
  def basic_auth
42
- http.basic_auth 'streams', 'diradio'
43
+ http.basic_auth "streams", "diradio"
43
44
  end
44
45
 
45
46
  def session(username, password)
46
47
  params = { member_session: { username: username, password: password } }
47
48
  basic_auth
48
- response http.post "/#{network || 'di'}/member_sessions", body: params
49
+ response http.post "/#{network || "di"}/member_sessions", body: params
49
50
  end
50
51
 
51
52
  def session_key
@@ -56,7 +57,7 @@ module AudioAddict
56
57
  Config.listen_key
57
58
  end
58
59
 
59
- private
60
+ private
60
61
 
61
62
  def response(httparty_response)
62
63
  raise APIError.new httparty_response unless httparty_response.success?
@@ -70,6 +71,5 @@ module AudioAddict
70
71
  def headers
71
72
  { "X-Session-Key" => session_key }
72
73
  end
73
-
74
74
  end
75
75
  end
@@ -5,7 +5,7 @@ module AudioAddict
5
5
  def method_missing(method_sym, *args, &block)
6
6
  respond_to?(method_sym) ? properties[method_sym.to_s] : super
7
7
  end
8
-
8
+
9
9
  def respond_to_missing?(method_sym, _include_private = false)
10
10
  properties.has_key?(method_sym.to_s) || super
11
11
  end
@@ -1,8 +1,7 @@
1
- require 'lightly'
1
+ require "lightly"
2
2
 
3
3
  module AudioAddict
4
4
  module Cache
5
-
6
5
  def cache
7
6
  @cache ||= Lightly.new life: cache_life, dir: cache_dir
8
7
  end
@@ -12,7 +11,7 @@ module AudioAddict
12
11
  end
13
12
 
14
13
  def cache_life!
15
- Config.cache_life || '6h'
14
+ Config.cache_life || "6h"
16
15
  end
17
16
 
18
17
  def cache_dir
@@ -22,6 +21,5 @@ module AudioAddict
22
21
  def cache_dir!
23
22
  Config.cache_dir || "#{Dir.home}/.audio_addict/cache"
24
23
  end
25
-
26
24
  end
27
25
  end
@@ -3,11 +3,11 @@ module AudioAddict
3
3
  include Cache
4
4
  include AutoProperties
5
5
  include Inspectable
6
-
6
+
7
7
  attr_reader :radio
8
8
 
9
9
  def initialize(radio, properties)
10
- @radio, @properties = radio, properties
10
+ @radio, @properties = radio, properties
11
11
  end
12
12
 
13
13
  def inspectable
@@ -17,8 +17,8 @@ module AudioAddict
17
17
  def active?
18
18
  # Seems like each network has a different way of marking inactive channels.
19
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'
20
+ return false if !properties["asset_id"]
21
+ return false if name[0] == "X" and key[0] != "x"
22
22
  return true
23
23
  end
24
24
 
@@ -36,15 +36,15 @@ module AudioAddict
36
36
  end
37
37
 
38
38
  def similar_channels
39
- similar = properties['similar_channels']
39
+ similar = properties["similar_channels"]
40
40
  return [] unless similar
41
- ids = similar.map { |s| s['similar_channel_id'] }
41
+ ids = similar.map { |s| s["similar_channel_id"] }
42
42
  radio.search_by_id ids
43
43
  end
44
44
 
45
45
  def vote(direction = :up, track: nil)
46
- track ||= current_track.id
47
- endpoint = "tracks/#{track}/vote/#{id}"
46
+ track ||= current_track
47
+ endpoint = "tracks/#{track.id}/vote/#{id}"
48
48
 
49
49
  if direction == :delete
50
50
  radio.api.delete endpoint
@@ -52,16 +52,16 @@ module AudioAddict
52
52
  radio.api.post "#{endpoint}/#{direction}"
53
53
  end
54
54
 
55
- log_like if direction == :up and Config.like_log
55
+ log_like track if direction == :up and Config.like_log
56
56
  end
57
57
 
58
- private
58
+ private
59
59
 
60
- def log_like
61
- message = "#{radio.name} :: #{name} :: #{current_track.artist} :: #{current_track.title}"
60
+ def log_like(track = nil)
61
+ track ||= current_track
62
+ message = "#{radio.name} :: #{name} :: #{track.artist} :: #{track.title}"
62
63
  file = Config.like_log
63
64
  File.append file, message unless File.contains? file, message
64
65
  end
65
-
66
66
  end
67
67
  end
@@ -4,19 +4,19 @@ module AudioAddict
4
4
  router = MisterBin::Runner.new version: VERSION,
5
5
  header: "AudioAddict Radio Utilities"
6
6
 
7
- router.route 'login', to: Commands::LoginCmd
8
- router.route 'set', to: Commands::SetCmd
9
- router.route 'channels', to: Commands::ChannelsCmd
10
- router.route 'now', to: Commands::NowCmd
11
- router.route 'history', to: Commands::HistoryCmd
12
- router.route 'vote', to: Commands::VoteCmd
13
- router.route 'playlist', to: Commands::PlaylistCmd
14
- router.route 'config', to: Commands::ConfigCmd
15
- router.route 'log', to: Commands::LogCmd
16
- router.route 'api', to: Commands::APICmd
7
+ router.route "login", to: Commands::LoginCmd
8
+ router.route "set", to: Commands::SetCmd
9
+ router.route "channels", to: Commands::ChannelsCmd
10
+ router.route "now", to: Commands::NowCmd
11
+ router.route "history", to: Commands::HistoryCmd
12
+ router.route "vote", to: Commands::VoteCmd
13
+ router.route "playlist", to: Commands::PlaylistCmd
14
+ router.route "config", to: Commands::ConfigCmd
15
+ router.route "log", to: Commands::LogCmd
16
+ router.route "download", to: Commands::DownloadCmd
17
+ router.route "api", to: Commands::APICmd
17
18
 
18
19
  router
19
20
  end
20
21
  end
21
-
22
22
  end
@@ -10,9 +10,9 @@ module AudioAddict
10
10
 
11
11
  param "ENDPOINT", "API endpoint path"
12
12
 
13
- example "radio channels"
14
- example "radio get track_history/channel/1"
15
- example "radio post tracks/1/vote/2/up"
13
+ example "radio api channels"
14
+ example "radio api get track_history/channel/1"
15
+ example "radio api post tracks/1/vote/2/up"
16
16
 
17
17
  def run
18
18
  needs :network
@@ -20,22 +20,21 @@ module AudioAddict
20
20
  puts response.to_yaml
21
21
  end
22
22
 
23
- private
23
+ private
24
24
 
25
25
  def api_method
26
- return :post if args['post']
27
- return :delete if args['delete']
26
+ return :post if args["post"]
27
+ return :delete if args["delete"]
28
28
  return :get
29
29
  end
30
30
 
31
31
  def endpoint
32
- args['ENDPOINT']
32
+ args["ENDPOINT"]
33
33
  end
34
34
 
35
35
  def api
36
36
  @api ||= API.new current_network
37
37
  end
38
-
39
38
  end
40
39
  end
41
- end
40
+ end
@@ -1,11 +1,10 @@
1
- require 'mister_bin'
2
- require 'colsole'
3
- require 'tty/prompt'
1
+ require "mister_bin"
2
+ require "colsole"
3
+ require "tty/prompt"
4
4
 
5
5
  module AudioAddict
6
6
  module Commands
7
7
  class Base < MisterBin::Command
8
-
9
8
  def needs(*config_keys)
10
9
  missing = []
11
10
  config_keys.each do |key|
@@ -34,7 +33,6 @@ module AudioAddict
34
33
  def prompt
35
34
  @prompt ||= TTY::Prompt.new
36
35
  end
37
-
38
36
  end
39
37
  end
40
- end
38
+ end
@@ -22,19 +22,19 @@ module AudioAddict
22
22
 
23
23
  say "!undgrn!#{radio.name}\n"
24
24
 
25
- search = args['SEARCH']
25
+ search = args["SEARCH"]
26
26
 
27
27
  channels = search ? radio.search(search) : radio.channels
28
-
28
+
29
29
  channels = channels.values
30
- if args['--info']
30
+ if args["--info"]
31
31
  show_verbose channels
32
32
  else
33
33
  show_compact channels
34
34
  end
35
35
  end
36
36
 
37
- private
37
+ private
38
38
 
39
39
  def show_verbose(channels)
40
40
  channels.each do |channel|
@@ -61,7 +61,6 @@ module AudioAddict
61
61
  say "!txtblu!#{channel.key.rjust 25} !txtgrn!#{channel.name.strip}"
62
62
  end
63
63
  end
64
-
65
64
  end
66
65
  end
67
- end
66
+ end
@@ -34,21 +34,21 @@ module AudioAddict
34
34
  example "radio config get listen_key"
35
35
 
36
36
  def get_command
37
- key = args['KEY'].to_sym
37
+ key = args["KEY"].to_sym
38
38
  value = Config.properties[key]
39
39
  say value ? "!txtgrn!#{value}" : "!txtred!<Unset>"
40
40
  end
41
41
 
42
42
  def set_command
43
- key = args['KEY'].to_sym
44
- value = args['VALUE']
43
+ key = args["KEY"].to_sym
44
+ value = args["VALUE"]
45
45
  Config.properties[key] = value
46
46
  Config.save
47
47
  say "!txtgrn!#{key}=#{value}"
48
48
  end
49
49
 
50
50
  def del_command
51
- key = args['KEY'].to_sym
51
+ key = args["KEY"].to_sym
52
52
  Config.delete key
53
53
  Config.save
54
54
  say "!txtgrn!Deleted"
@@ -64,7 +64,7 @@ module AudioAddict
64
64
  end
65
65
 
66
66
  def edit_command
67
- editor = ENV['EDITOR'] || 'vi'
67
+ editor = ENV["EDITOR"] || "vi"
68
68
  system "#{editor} #{Config.path}"
69
69
  end
70
70
 
@@ -77,14 +77,14 @@ module AudioAddict
77
77
  end
78
78
 
79
79
  def check_command
80
- errors = verify_and_show_keys required_keys, critical: true
80
+ errors = verify_and_show_keys required_keys, critical: true
81
81
  warnings = verify_and_show_keys optional_keys
82
82
 
83
83
  say "Done. #{errors} errors, #{warnings} warnings."
84
84
  errors > 0 ? 1 : 0
85
85
  end
86
86
 
87
- private
87
+ private
88
88
 
89
89
  def verify_and_show_keys(keys, critical: false)
90
90
  problems = 0
@@ -115,20 +115,19 @@ module AudioAddict
115
115
 
116
116
  def required_keys
117
117
  {
118
- email: 'login',
119
- session_key: 'login',
120
- listen_key: 'login',
121
- network: 'set',
122
- channel: 'set',
118
+ email: "login",
119
+ session_key: "login",
120
+ listen_key: "login",
121
+ network: "set",
122
+ channel: "set",
123
123
  }
124
124
  end
125
125
 
126
126
  def optional_keys
127
127
  {
128
- like_log: 'config set like_log PATH',
128
+ like_log: "config set like_log PATH",
129
129
  }
130
130
  end
131
-
132
131
  end
133
132
  end
134
- end
133
+ end
@@ -0,0 +1,73 @@
1
+ module AudioAddict
2
+ module Commands
3
+ class DownloadCmd < Base
4
+ summary "Download songs from YouTube"
5
+
6
+ help "This command uses youtube-dl to download the currently playing song, or songs from your like-log."
7
+
8
+ usage "radio download current [--count N]"
9
+ usage "radio download log [--lines N --count N]"
10
+ usage "radio download search QUERY [--count N]"
11
+ usage "radio download --help"
12
+
13
+ option "-l --lines N", "Number of log lines to download [default: 1]"
14
+ option "-c --count N", "Number of YouTube search results to download\nDefaults to the value of the AUDIO_ADDICT_DOWNLOAD_COUNT environment variable, or 1"
15
+
16
+ param "QUERY", "YouTube search query"
17
+
18
+ command "current", "Download the currently playing song"
19
+ command "log", "Download the last N songs from the like-log"
20
+ command "search", "Download any song matching the Youtube search query"
21
+
22
+ environment "AUDIO_ADDICT_DOWNLOAD_COUNT", "Set the default download count (--count)"
23
+
24
+ example "radio download current"
25
+ example "radio download current --count 3"
26
+ example "radio download log --lines 2 --count 3"
27
+ example "radio download search 'Brimstone, Bright Shadow' -c2"
28
+
29
+ def current_command
30
+ needs :network, :channel
31
+
32
+ say "!txtblu!Downloading !txtrst!: ... "
33
+
34
+ track = current_channel.current_track
35
+ query = track.search_string
36
+
37
+ resay "!txtblu!Downloading !txtgrn!: #{query}"
38
+
39
+ Youtube.new(query).get count
40
+ end
41
+
42
+ def log_command
43
+ needs :like_log
44
+ lines = args['--lines']&.to_i
45
+
46
+ data = log.data[-lines..-1]
47
+ data.each do |line|
48
+ network, channel, artist, song = line.split(" :: ")
49
+ query = "#{artist}, #{song}"
50
+ say "\n!txtblu!Downloading !txtgrn!: #{query}"
51
+ Youtube.new(query).get count
52
+ end
53
+ end
54
+
55
+ def search_command
56
+ query = args['QUERY']
57
+
58
+ say "\n!txtblu!Downloading !txtgrn!: #{query}"
59
+ Youtube.new(query).get count
60
+ end
61
+
62
+ private
63
+
64
+ def count
65
+ args['--count']&.to_i || ENV['AUDIO_ADDICT_DOWNLOAD_COUNT']&.to_i || 1
66
+ end
67
+
68
+ def log
69
+ @log ||= Log.new
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,7 +1,7 @@
1
1
  module AudioAddict
2
2
  module Commands
3
3
  class HistoryCmd < Base
4
- summary "Show track history for the current channel"
4
+ summary "Show track history for the current channel"
5
5
 
6
6
  help "This command shows the last few tracks that were playing on the currently active channel in reverse order (top track is the most recent)."
7
7
 
@@ -11,13 +11,13 @@ module AudioAddict
11
11
  def run
12
12
  needs :network, :channel
13
13
  say "!undgrn!#{radio.name} > #{current_channel.name}"
14
- say ''
14
+ say ""
15
15
  tracks.each do |track|
16
16
  say "!txtgrn! #{track.artist.rjust max_artist_len}!txtrst! : !txtblu!#{track.title}"
17
17
  end
18
18
  end
19
19
 
20
- private
20
+ private
21
21
 
22
22
  def tracks
23
23
  @tracks ||= current_channel.track_history
@@ -28,4 +28,4 @@ module AudioAddict
28
28
  end
29
29
  end
30
30
  end
31
- end
31
+ end
@@ -32,13 +32,13 @@ module AudioAddict
32
32
 
33
33
  def show_command
34
34
  needs :like_log
35
- query = args['SEARCH']
35
+ query = args["SEARCH"]
36
36
  puts query ? log.search(query) : log.data
37
37
  end
38
38
 
39
39
  def tail_command
40
40
  needs :like_log
41
- lines = args['--lines'].to_i
41
+ lines = args["--lines"].to_i
42
42
  puts log.data[-lines..-1]
43
43
  end
44
44
 
@@ -52,10 +52,10 @@ module AudioAddict
52
52
  tree = log.tree
53
53
 
54
54
  say ""
55
- network = prompt.select "Network:", tree.keys, marker: '>', filter: true
56
- channel = prompt.select "Channel:", tree[network].keys, marker: '>', filter: true, per_page: page_size
57
- artist = prompt.select "Artist:", tree[network][channel].keys, marker: '>', filter: true, per_page: page_size
58
-
55
+ network = prompt.select "Network:", tree.keys, symbols: { marker: ">" }, filter: true, per_page: 10
56
+ channel = prompt.select "Channel:", tree[network].keys, symbols: { marker: ">" }, filter: true, per_page: page_size
57
+ artist = prompt.select "Artist:", tree[network][channel].keys, symbols: { marker: ">" }, filter: true, per_page: page_size
58
+
59
59
  say "Songs:"
60
60
  tree[network][channel][artist].each { |song| say "- !txtgrn!#{song}" }
61
61
  say ""
@@ -65,8 +65,8 @@ module AudioAddict
65
65
 
66
66
  def tree_command
67
67
  yaml = log.tree.to_yaml
68
- filename = args['--save']
69
-
68
+ filename = args["--save"]
69
+
70
70
  if filename
71
71
  File.write filename, yaml
72
72
  say "!txtgrn!Saved #{filename}"
@@ -75,7 +75,7 @@ module AudioAddict
75
75
  end
76
76
  end
77
77
 
78
- private
78
+ private
79
79
 
80
80
  def log
81
81
  @log ||= Log.new
@@ -84,7 +84,6 @@ module AudioAddict
84
84
  def page_size
85
85
  @page_size ||= detect_terminal_size[1] - 4
86
86
  end
87
-
88
87
  end
89
88
  end
90
- end
89
+ end
@@ -15,16 +15,16 @@ module AudioAddict
15
15
  say "!txtylw!You are already logged in as !undylw!#{Config.email}"
16
16
  proceed = prompt.yes? "Login again?"
17
17
  end
18
-
18
+
19
19
  login_prompt if proceed
20
20
  end
21
21
 
22
- private
22
+ private
23
23
 
24
24
  def login_prompt
25
25
  user = prompt.ask "Username :", default: Config.email
26
26
  pass = prompt.mask "Password :"
27
-
27
+
28
28
  if user and pass
29
29
  say "Logging in... "
30
30
  radio.api.login user, pass
@@ -33,7 +33,6 @@ module AudioAddict
33
33
  say "!txtred!Cancelled"
34
34
  end
35
35
  end
36
-
37
36
  end
38
37
  end
39
- end
38
+ end
@@ -1,7 +1,7 @@
1
1
  module AudioAddict
2
2
  module Commands
3
3
  class NowCmd < Base
4
- summary "Show network, channel and playing track"
4
+ summary "Show network, channel and playing track"
5
5
 
6
6
  help "This command displays the active network and channel, as well as the currently playing track."
7
7
 
@@ -11,15 +11,14 @@ module AudioAddict
11
11
  def run
12
12
  needs :network, :channel
13
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!: ... "
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
17
 
18
18
  track = current_channel.current_track
19
19
  resay "!txtblu! Track !txtrst!: !txtgrn!#{track.title.strip}"
20
- say "!txtblu! By !txtrst!: !txtgrn!#{track.artist.strip}"
20
+ say "!txtblu! By !txtrst!: !txtgrn!#{track.artist.strip}"
21
21
  end
22
-
23
22
  end
24
23
  end
25
- end
24
+ end
@@ -1,7 +1,7 @@
1
1
  module AudioAddict
2
2
  module Commands
3
3
  class PlaylistCmd < Base
4
- summary "Generate playlists"
4
+ summary "Generate playlists"
5
5
 
6
6
  help "This command lets you generate playlists for the active network. In order to allow configuration, the process is done in two stages: 'init' and 'generate'."
7
7
 
@@ -22,7 +22,7 @@ module AudioAddict
22
22
 
23
23
  require_premium_account
24
24
 
25
- name = args['NAME']
25
+ name = args["NAME"]
26
26
  outfile = "#{name}.yml"
27
27
 
28
28
  say "!txtred!Warning!txtrst!: !txtgrn!#{outfile}!txtrst! already exists!" if File.exist? outfile
@@ -34,12 +34,12 @@ module AudioAddict
34
34
  end
35
35
  end
36
36
 
37
- def generate_command(name=nil)
37
+ def generate_command(name = nil)
38
38
  needs :network, :channel, :listen_key
39
39
 
40
40
  require_premium_account
41
41
 
42
- name ||= args['NAME']
42
+ name ||= args["NAME"]
43
43
 
44
44
  infile = "#{name}.yml"
45
45
  outfile = "#{name}.pls"
@@ -53,15 +53,12 @@ module AudioAddict
53
53
  end
54
54
  end
55
55
 
56
- private
56
+ private
57
57
 
58
58
  def generate_config(outfile)
59
- data = {
60
- template: "http://prem2.#{radio.domain}:80/%{channel_key}?%{listen_key}"
61
- }
62
-
59
+ data = { template: radio.url_template }
63
60
  channels = {}
64
-
61
+
65
62
  radio.channels.each do |key, channel|
66
63
  key = fix_key key.to_sym
67
64
  channels[key] = channel.name
@@ -107,10 +104,9 @@ module AudioAddict
107
104
 
108
105
  # This is a patch to circumvent some anomalies in the AudioAddict API
109
106
  def fix_key(key)
110
- key = :electrohouse if current_network == 'di' and key == :electro
107
+ key = :electrohouse if current_network == "di" and key == :electro
111
108
  key
112
109
  end
113
-
114
110
  end
115
111
  end
116
- end
112
+ end
@@ -19,8 +19,8 @@ module AudioAddict
19
19
  example "radio set - rockradio"
20
20
 
21
21
  def run
22
- channel = args['CHANNEL']
23
- network = args['NETWORK']
22
+ channel = args["CHANNEL"]
23
+ network = args["NETWORK"]
24
24
 
25
25
  full_set = (channel and network) || !(channel or network)
26
26
 
@@ -31,7 +31,7 @@ module AudioAddict
31
31
  end
32
32
  end
33
33
 
34
- private
34
+ private
35
35
 
36
36
  def set_both(channel, network)
37
37
  needs :session_key
@@ -50,19 +50,14 @@ module AudioAddict
50
50
 
51
51
  if !channel
52
52
  channel_menu
53
-
54
- elsif channel == '-'
53
+ elsif channel == "-"
55
54
  save_channel radio.channels.keys.first
56
-
57
55
  elsif radio.valid_channel? channel
58
56
  save_channel channel
59
-
60
57
  elsif radio.search(channel).any?
61
58
  channel_menu channel
62
-
63
59
  else
64
60
  say "!txtred!Invalid channel: #{radio.name} > #{channel}"
65
-
66
61
  end
67
62
  end
68
63
 
@@ -91,13 +86,13 @@ module AudioAddict
91
86
  def channel_prompt(channels)
92
87
  options = channels.map { |channel| ["#{channel.name.ljust 20} # #{channel.key}", channel.key] }.to_h
93
88
  options = { "Cancel" => :cancel }.merge options
94
- prompt.select "Channel :", options, marker: '>', filter: true
89
+ prompt.select "Channel :", options, symbols: { marker: ">" }, filter: true
95
90
  end
96
91
 
97
92
  def network_prompt(networks)
98
93
  options = networks.invert
99
94
  options["Skip"] = :cancel
100
- prompt.select "Network :", options, marker: '>', filter: true
95
+ prompt.select "Network :", options, symbols: { marker: ">" }, filter: true, per_page: 10
101
96
  end
102
97
 
103
98
  def save_channel(channel, echo: true)
@@ -111,7 +106,6 @@ module AudioAddict
111
106
  Config.save
112
107
  say "Network : !txtgrn!#{radio.name}!txtrst! # #{network}" if echo
113
108
  end
114
-
115
109
  end
116
110
  end
117
- end
111
+ end
@@ -1,7 +1,7 @@
1
1
  module AudioAddict
2
2
  module Commands
3
3
  class VoteCmd < Base
4
- summary "Vote on a recently played track"
4
+ summary "Vote on a recently played track"
5
5
 
6
6
  help "This command starts an interactive voting prompt for the currently playing track or for previously played tracks."
7
7
 
@@ -22,7 +22,7 @@ module AudioAddict
22
22
  vote_mode == :now ? vote_now : vote_past
23
23
  end
24
24
 
25
- private
25
+ private
26
26
 
27
27
  def vote_past
28
28
  track = get_user_track
@@ -54,9 +54,9 @@ module AudioAddict
54
54
  end
55
55
 
56
56
  def get_user_track
57
- options = tracks.map { |t| ["#{t.artist.ljust max_artist_len} > #{t.title}", t.id]}.to_h
57
+ options = tracks.map { |t| ["#{t.artist.ljust max_artist_len} > #{t.title}", t] }.to_h
58
58
  options = { "Cancel" => :cancel }.merge options
59
- prompt.select "Track:", options, marker: '>'
59
+ prompt.select "Track:", options, symbols: { marker: ">" }
60
60
  end
61
61
 
62
62
  def get_user_vote
@@ -64,9 +64,9 @@ module AudioAddict
64
64
  end
65
65
 
66
66
  def menu_prompt
67
- options = { "Like" => :up, "Dislike" => :down,
68
- "Unvote" => :delete, "Cancel" => :cancel }
69
- prompt.select "Vote:", options, marker: '>'
67
+ options = { "Like" => :up, "Dislike" => :down,
68
+ "Unvote" => :delete, "Cancel" => :cancel }
69
+ prompt.select "Vote:", options, symbols: { marker: ">" }
70
70
  end
71
71
 
72
72
  def simple_prompt
@@ -75,13 +75,12 @@ module AudioAddict
75
75
  end
76
76
 
77
77
  def vote_style
78
- args['--all'] ? :menu : :simple
78
+ args["--all"] ? :menu : :simple
79
79
  end
80
80
 
81
81
  def vote_mode
82
- args['--past'] ? :past : :now
82
+ args["--past"] ? :past : :now
83
83
  end
84
-
85
84
  end
86
85
  end
87
86
  end
@@ -1,4 +1,4 @@
1
- require 'yaml'
1
+ require "yaml"
2
2
 
3
3
  module AudioAddict
4
4
  class Config
@@ -6,7 +6,7 @@ module AudioAddict
6
6
  attr_writer :path
7
7
 
8
8
  def method_missing(name, *args, &_blk)
9
- if name.to_s.end_with? '='
9
+ if name.to_s.end_with? "="
10
10
  name = name[0..-2].to_sym
11
11
  properties[name] = args.first
12
12
  else
@@ -39,13 +39,12 @@ module AudioAddict
39
39
  end
40
40
 
41
41
  def path
42
- @path ||= ENV.fetch('AUDIO_ADDICT_CONFIG_PATH', default_path)
42
+ @path ||= ENV.fetch("AUDIO_ADDICT_CONFIG_PATH", default_path)
43
43
  end
44
44
 
45
45
  def default_path
46
46
  "#{Dir.home}/.audio_addict/config"
47
47
  end
48
-
49
48
  end
50
49
  end
51
50
  end
@@ -1,25 +1,24 @@
1
1
  module AudioAddict
2
2
  class Error < StandardError; end
3
-
4
3
  class Interrupt < Error; end
5
-
6
4
  class ArgumentError < Error; end
5
+ class DependencyError < Error; end
7
6
 
8
7
  class ConfigError < Error
9
8
  attr_reader :missing_keys
10
9
 
11
10
  def initialize(missing_keys)
12
11
  @missing_keys = missing_keys
13
- super "Some parameters required by this operation are missing"
12
+ super "Some parameters required by this operation are missing"
14
13
  end
15
14
  end
16
-
15
+
17
16
  class PremiumAccount < Error
18
- def initialize(message="This operation requires a premium account")
17
+ def initialize(message = "This operation requires a premium account")
19
18
  super
20
19
  end
21
20
  end
22
-
21
+
23
22
  class APIError < Error
24
23
  attr_reader :response
25
24
 
@@ -28,4 +27,4 @@ module AudioAddict
28
27
  super "#{response.code} #{response.message}:\n#{response.body}"
29
28
  end
30
29
  end
31
- end
30
+ end
@@ -1,4 +1,4 @@
1
- require 'fileutils'
1
+ require "fileutils"
2
2
 
3
3
  class File
4
4
  def self.contains?(file, content)
@@ -10,7 +10,7 @@ class File
10
10
  end
11
11
 
12
12
  def self.append(file, content)
13
- open(file, 'a') { |f| f.puts content }
13
+ open(file, "a") { |f| f.puts content }
14
14
  end
15
15
 
16
16
  def self.deep_write(file, content)
@@ -2,7 +2,7 @@ module AudioAddict
2
2
  module Inspectable
3
3
  def inspect
4
4
  keys = inspectable.map { |k| %Q[@#{k}="#{send k}"] }
5
- "#<#{self.class} #{keys.join ', '}>"
5
+ "#<#{self.class} #{keys.join ", "}>"
6
6
  end
7
7
  end
8
8
  end
@@ -31,9 +31,9 @@ module AudioAddict
31
31
 
32
32
  def tree!
33
33
  result = {}
34
-
34
+
35
35
  data.each do |line|
36
- network, channel, artist, song = line.split(' :: ')
36
+ network, channel, artist, song = line.split(" :: ")
37
37
  result[network] ||= {}
38
38
  result[network][channel] ||= {}
39
39
  result[network][channel][artist] ||= []
@@ -42,6 +42,5 @@ module AudioAddict
42
42
 
43
43
  result
44
44
  end
45
-
46
45
  end
47
46
  end
@@ -10,7 +10,8 @@ module AudioAddict
10
10
  rockradio: "Rock Radio",
11
11
  radiotunes: "Radio Tunes",
12
12
  jazzradio: "Jazz Radio",
13
- classicalradio: "Classical Radio"
13
+ classicalradio: "Classical Radio",
14
+ zenradio: "Zen Radio",
14
15
  }
15
16
 
16
17
  DOMAINS = {
@@ -18,7 +19,8 @@ module AudioAddict
18
19
  rockradio: "rockradio.com",
19
20
  radiotunes: "radiotunes.com",
20
21
  jazzradio: "jazzradio.com",
21
- classicalradio: "classicalradio.com"
22
+ classicalradio: "classicalradio.com",
23
+ zenradio: "zenradio.com",
22
24
  }
23
25
 
24
26
  def self.networks(search = nil)
@@ -50,6 +52,11 @@ module AudioAddict
50
52
  DOMAINS[network.to_sym]
51
53
  end
52
54
 
55
+ def url_template
56
+ channel_path = network == "zenradio" ? "zr%{channel_key}_aac" : "%{channel_key}"
57
+ "http://prem2.#{domain}:80/#{channel_path}?%{listen_key}"
58
+ end
59
+
53
60
  def channels
54
61
  @channels ||= channels!
55
62
  end
@@ -57,7 +64,7 @@ module AudioAddict
57
64
  def search(query)
58
65
  channels.select do |key, channel|
59
66
  "#{key} #{channel.name.downcase}".include? query.downcase
60
- end
67
+ end
61
68
  end
62
69
 
63
70
  def search_by_id(ids)
@@ -77,16 +84,16 @@ module AudioAddict
77
84
  @api ||= API.new network
78
85
  end
79
86
 
80
- private
87
+ private
81
88
 
82
89
  def channels!
83
90
  response = cache.get "#{network}/channels" do
84
- api.get 'channels'
91
+ api.get "channels"
85
92
  end
86
93
 
87
94
  result = {}
88
95
  response.map do |channel|
89
- key = channel['key']
96
+ key = channel["key"]
90
97
  candidate = Channel.new self, channel
91
98
  result[key] = candidate if candidate.active?
92
99
  end
@@ -6,7 +6,7 @@ module AudioAddict
6
6
  attr_reader :channel
7
7
 
8
8
  def initialize(channel, properties)
9
- @channel, @properties = channel, properties
9
+ @channel, @properties = channel, properties
10
10
  end
11
11
 
12
12
  def inspectable
@@ -14,11 +14,15 @@ module AudioAddict
14
14
  end
15
15
 
16
16
  def id
17
- properties['track_id']
17
+ properties["track_id"]
18
18
  end
19
19
 
20
20
  def title
21
- properties['title'].strip
21
+ properties["title"].strip
22
+ end
23
+
24
+ def search_string
25
+ "#{artist}, #{title}"
22
26
  end
23
27
  end
24
28
  end
@@ -1,3 +1,3 @@
1
1
  module AudioAddict
2
- VERSION = "0.2.0"
3
- end
2
+ VERSION = "0.4.1"
3
+ end
@@ -0,0 +1,41 @@
1
+ module AudioAddict
2
+ class Youtube
3
+ include Inspectable
4
+ include Colsole
5
+
6
+ attr_reader :query
7
+
8
+ def initialize(query)
9
+ @query = query
10
+ end
11
+
12
+ def inspectable
13
+ [:query]
14
+ end
15
+
16
+ def get(count = 1)
17
+ raise DependencyError, "This command requires youtube-dl" unless command_exist? 'youtube-dl'
18
+ success = execute command(count: count, query: query)
19
+ raise DependencyError, "youtube-dl exited with an error" unless success
20
+ end
21
+
22
+ def command(args)
23
+ command_template % args
24
+ end
25
+
26
+ private
27
+
28
+ def execute(command)
29
+ if ENV['AUDIO_ADDICT_DOWNLOAD_DRY_RUN']
30
+ puts "DRY RUN: #{command}"
31
+ true
32
+ else
33
+ system command
34
+ end
35
+ end
36
+
37
+ def command_template
38
+ @command_template ||= %Q[youtube-dl --extract-audio --audio-format mp3 ytsearch%{count}:"%{query}"]
39
+ end
40
+ end
41
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: audio_addict
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Danny Ben Shitrit
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-18 00:00:00.000000000 Z
11
+ date: 2021-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colsole
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0.17'
89
+ version: '0.19'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0.17'
96
+ version: '0.19'
97
97
  description: Command line for playlist management and voting for AudioAddict radio
98
98
  networks
99
99
  email: db@dannyben.com
@@ -114,6 +114,7 @@ files:
114
114
  - lib/audio_addict/commands/base.rb
115
115
  - lib/audio_addict/commands/channels.rb
116
116
  - lib/audio_addict/commands/config.rb
117
+ - lib/audio_addict/commands/download.rb
117
118
  - lib/audio_addict/commands/history.rb
118
119
  - lib/audio_addict/commands/log.rb
119
120
  - lib/audio_addict/commands/login.rb
@@ -129,6 +130,7 @@ files:
129
130
  - lib/audio_addict/radio.rb
130
131
  - lib/audio_addict/track.rb
131
132
  - lib/audio_addict/version.rb
133
+ - lib/audio_addict/youtube.rb
132
134
  homepage: https://github.com/dannyben/audio_addict
133
135
  licenses:
134
136
  - MIT
@@ -148,8 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
150
  - !ruby/object:Gem::Version
149
151
  version: '0'
150
152
  requirements: []
151
- rubyforge_project:
152
- rubygems_version: 2.7.8
153
+ rubygems_version: 3.2.3
153
154
  signing_key:
154
155
  specification_version: 4
155
156
  summary: AudioAddict Command Line