cultome_player 2.0.0

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.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +24 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +7 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +325 -0
  9. data/Rakefile +8 -0
  10. data/bin/cultome_player +39 -0
  11. data/config/environment.yml +28 -0
  12. data/cultome_player.gemspec +35 -0
  13. data/db/001_create_schema.rb +58 -0
  14. data/lib/cultome_player.rb +107 -0
  15. data/lib/cultome_player/command.rb +11 -0
  16. data/lib/cultome_player/command/language.rb +61 -0
  17. data/lib/cultome_player/command/processor.rb +165 -0
  18. data/lib/cultome_player/command/reader.rb +86 -0
  19. data/lib/cultome_player/environment.rb +130 -0
  20. data/lib/cultome_player/events.rb +29 -0
  21. data/lib/cultome_player/media.rb +47 -0
  22. data/lib/cultome_player/objects.rb +15 -0
  23. data/lib/cultome_player/objects/album.rb +21 -0
  24. data/lib/cultome_player/objects/artist.rb +18 -0
  25. data/lib/cultome_player/objects/command.rb +37 -0
  26. data/lib/cultome_player/objects/drive.rb +26 -0
  27. data/lib/cultome_player/objects/genre.rb +16 -0
  28. data/lib/cultome_player/objects/parameter.rb +37 -0
  29. data/lib/cultome_player/objects/response.rb +42 -0
  30. data/lib/cultome_player/objects/song.rb +38 -0
  31. data/lib/cultome_player/player.rb +13 -0
  32. data/lib/cultome_player/player/adapter.rb +14 -0
  33. data/lib/cultome_player/player/adapter/mpg123.rb +143 -0
  34. data/lib/cultome_player/player/interactive.rb +56 -0
  35. data/lib/cultome_player/player/interface.rb +13 -0
  36. data/lib/cultome_player/player/interface/basic.rb +96 -0
  37. data/lib/cultome_player/player/interface/builtin_help.rb +368 -0
  38. data/lib/cultome_player/player/interface/extended.rb +199 -0
  39. data/lib/cultome_player/player/interface/helper.rb +300 -0
  40. data/lib/cultome_player/player/playlist.rb +280 -0
  41. data/lib/cultome_player/plugins.rb +23 -0
  42. data/lib/cultome_player/plugins/help.rb +58 -0
  43. data/lib/cultome_player/state_checker.rb +74 -0
  44. data/lib/cultome_player/utils.rb +95 -0
  45. data/lib/cultome_player/version.rb +3 -0
  46. data/spec/config.yml +0 -0
  47. data/spec/cultome_player/command/processor_spec.rb +168 -0
  48. data/spec/cultome_player/command/reader_spec.rb +45 -0
  49. data/spec/cultome_player/cultome_player_spec.rb +17 -0
  50. data/spec/cultome_player/environment_spec.rb +65 -0
  51. data/spec/cultome_player/events_spec.rb +22 -0
  52. data/spec/cultome_player/media_spec.rb +41 -0
  53. data/spec/cultome_player/player/adapter/mpg123_spec.rb +82 -0
  54. data/spec/cultome_player/player/interface/basic_spec.rb +168 -0
  55. data/spec/cultome_player/player/interface/extended/connect_spec.rb +117 -0
  56. data/spec/cultome_player/player/interface/extended/search_spec.rb +90 -0
  57. data/spec/cultome_player/player/interface/extended/show_spec.rb +36 -0
  58. data/spec/cultome_player/player/interface/extended/shuffle_spec.rb +26 -0
  59. data/spec/cultome_player/player/interface/extended_spec.rb +136 -0
  60. data/spec/cultome_player/player/interface/helper_spec.rb +63 -0
  61. data/spec/cultome_player/player/interface_spec.rb +17 -0
  62. data/spec/cultome_player/player/playlist_spec.rb +301 -0
  63. data/spec/cultome_player/plugins/help_spec.rb +21 -0
  64. data/spec/cultome_player/plugins_spec.rb +19 -0
  65. data/spec/cultome_player/utils_spec.rb +15 -0
  66. data/spec/spec_helper.rb +108 -0
  67. data/spec/test/uno/dos/dos.mp3 +0 -0
  68. data/spec/test/uno/dos/tres/tres.mp3 +0 -0
  69. data/spec/test/uno/uno.mp3 +0 -0
  70. data/tasks/console.rake +19 -0
  71. data/tasks/db.rake +19 -0
  72. data/tasks/run.rake +7 -0
  73. metadata +322 -0
@@ -0,0 +1,86 @@
1
+ require 'readline'
2
+
3
+ module CultomePlayer::Command
4
+ module Reader
5
+
6
+ # Display a prompt and read user input.
7
+ #
8
+ # @param prompt [String] The message to display to user for arcking for input.
9
+ # @return [String] The user input readed.
10
+ def read_command(prompt)
11
+ command_reader.readline(prompt, true)
12
+ end
13
+
14
+ # Lazy getter for readline object.
15
+ #
16
+ # @return [Readline] The readline object
17
+ def command_reader
18
+ return Readline if @command_reader_initialized
19
+
20
+ Readline.completion_append_character = ""
21
+ Readline.basic_word_break_characters = Readline.basic_word_break_characters.delete("@")
22
+ Readline.completion_proc = completion_proc
23
+ @command_reader_initialized = true
24
+ return Readline
25
+ end
26
+
27
+ private
28
+
29
+ def completion_proc
30
+ proc do |word|
31
+ if Readline.line_buffer.empty?
32
+ # linea en blanco y no sabe los comandos
33
+ options = semantics.keys #return
34
+ else
35
+ tks = Readline.line_buffer.split
36
+ if tks.length == 1
37
+ options = complete_action(tks[0], word)
38
+ elsif tks.length > 1
39
+ options = complete_parameter(tks[0], word)
40
+ end
41
+ end
42
+
43
+ options = [] if options.nil?
44
+ options << word if options.empty?
45
+ options << " " if options.all?{|o| o.start_with?("<")}
46
+ options # final return
47
+ end # proc
48
+ end
49
+
50
+ def get_command_param_options(cmd, word)
51
+ if word.empty?
52
+ # completa! mostramos los parametros disponibles para el comando
53
+ if semantics.keys.include?(cmd)
54
+ # mostramos las opciones de parametros IFF acepta parametros
55
+ params = semantics[cmd].source.gsub(/^\^literal/, '').gsub(/\[\\s\][+*]/, "").gsub(/[()*$]/, '')
56
+
57
+ params.split(/[| ]/).collect{|p| "<#{p}>"} unless params.empty?
58
+ end
59
+ else
60
+ yield if block_given?
61
+ end
62
+ end
63
+
64
+ def complete_parameter(cmd, word)
65
+ options = get_command_param_options(cmd, word) do
66
+ # esta acompletando un parametro
67
+ if word.start_with?("/") || word.start_with?("~/")
68
+ expanded_path = File.expand_path(word)
69
+ expanded_path += "/" if File.directory?(expanded_path)
70
+ Dir[expanded_path + "*"].grep(/^#{Regexp.escape(expanded_path)}/).collect{|d| "#{d}/"}
71
+ elsif word.start_with?("@")
72
+ %w{@library @search @playlist @history @queue @song @artist @album @drives}.grep(/^#{word}/)
73
+ end
74
+ end
75
+ end
76
+
77
+ def complete_action(cmd, word)
78
+ # escribio una parabra..
79
+ get_command_param_options(cmd, word) do
80
+ # incompleta! require acompletar el action actual
81
+ semantics.keys.grep(/^#{Regexp.escape(Readline.line_buffer)}/).collect{|s| "#{s} "}
82
+ end
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,130 @@
1
+ require 'yaml'
2
+ require 'rake'
3
+
4
+ module CultomePlayer
5
+ module Environment
6
+
7
+ # Get the db_adapter environment configuration value.
8
+ #
9
+ # @return [String] The db_adapter value for teh selected environment.
10
+ def db_adapter
11
+ env_config['db_adapter'] || raise('environment problem:environment information not loaded')
12
+ end
13
+
14
+ # Get the db_file environment configuration value.
15
+ #
16
+ # @return [String] The db_file value for teh selected environment.
17
+ def db_file
18
+ env_config['db_file'] || raise('environment problem:environment information not loaded')
19
+ end
20
+
21
+ # Get the db_log_file environment configuration value.
22
+ #
23
+ # @return [String] The db_log_file value for teh selected environment.
24
+ def db_log_file
25
+ env_config['db_log_file'] || raise('environment problem:environment information not loaded')
26
+ end
27
+
28
+ # Get the file_types environment configuration value.
29
+ #
30
+ # @return [String] The file_types value for teh selected environment.
31
+ def file_types
32
+ env_config['file_types'] || raise('environment problem:environment information not loaded')
33
+ end
34
+
35
+ # Get the config_file environment configuration value.
36
+ #
37
+ # @return [String] The config_file value for teh selected environment.
38
+ def config_file
39
+ env_config['config_file'] || raise('environment problem:environment information not loaded')
40
+ end
41
+
42
+ # Get the mplayer_pipe environment configuration value.
43
+ #
44
+ # @return [String] The mplayer_pipe value for teh selected environment.
45
+ def mplayer_pipe
46
+ env_config['mplayer_pipe'] || raise('environment problem:environment information not loaded')
47
+ end
48
+
49
+ # Get the stdout (not STDOUT) for the player.
50
+ #
51
+ # @return [IO] The stdout for the player.
52
+ def stdout
53
+ STDOUT
54
+ end
55
+
56
+ # Gets the player configurations.
57
+ #
58
+ # @return [Hash] Player configuration.
59
+ def player_config
60
+ @player_config ||= {}
61
+ end
62
+
63
+ # Gets the environment configurations.
64
+ #
65
+ # @return [Hash] Environment configuration.
66
+ def env_config
67
+ @env_config ||= {}
68
+ end
69
+
70
+ # Get the current environment name.
71
+ #
72
+ # @return [Symbol] The current environment name.
73
+ def current_env
74
+ @current_env
75
+ end
76
+
77
+ # Extract the configuration for the environment and setup valriables.
78
+ #
79
+ # @param env [Symbol] The name of the environment to load.
80
+ # @param check_db [Boolean] Flag to decide if the database schema should be checked.
81
+ def prepare_environment(env, check_db=true)
82
+ env_config = YAML.load_file File.expand_path('config/environment.yml')
83
+ @env_config = env_config[env.to_s]
84
+ @current_env = env.to_sym
85
+ raise 'environment problem:environment not found' if @env_config.nil?
86
+ expand_paths @env_config
87
+ create_required_files @env_config
88
+ load_master_config @env_config['config_file']
89
+ check_db_schema if check_db
90
+ end
91
+
92
+ private
93
+
94
+ def check_db_schema
95
+ Rake.load_rakefile 'Rakefile'
96
+ Rake.application.load_imports
97
+ swallow_stdout{ Rake.application.invoke_task("db:create[#{current_env}]") }
98
+ end
99
+
100
+ def load_master_config(config_file)
101
+ @player_config = YAML.load_file(config_file) || {}
102
+ @player_config['main'] ||= {}
103
+ end
104
+
105
+ def create_required_files(env_config)
106
+ env_config.each do |k,v|
107
+ if k.end_with?('_file')
108
+ unless File.exist?(v)
109
+ %x[mkdir -p '#{File.dirname(v)}' && touch '#{v}']
110
+ raise 'environment problem:cannot create required files' unless $?.success?
111
+ end
112
+ elsif k.end_with?('_pipe')
113
+ unless File.exist?(v)
114
+ %x[mkfifo '#{v}']
115
+ raise 'environment problem:cannot create required pipe' unless $?.success?
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ def expand_paths(env_config)
122
+ env_config.each do |k,v|
123
+ if k.end_with?('_file') || k.end_with?('_pipe')
124
+ env_config[k] = File.expand_path(v)
125
+ end
126
+ end
127
+ end
128
+
129
+ end
130
+ end
@@ -0,0 +1,29 @@
1
+ module CultomePlayer
2
+ module Events
3
+
4
+ # Lazy getter of registered event listeners.
5
+ #
6
+ # @return [Hash] With event names as the keys and values are the listeners registered to that event.
7
+ def listeners
8
+ @listeners ||= Hash.new{|h,k| h[k] = [] }
9
+ end
10
+
11
+ # Register a listener to an event.
12
+ #
13
+ # @param event [Symbol] The event name.
14
+ # @param listener [Object] Implements a callback with the name on_<event name>.
15
+ # @return [Object] The registered listener.
16
+ def register_listener(event, listener)
17
+ listeners[event] << listener
18
+ return listener
19
+ end
20
+
21
+ # Broadcast an event to all the registered listeners.
22
+ #
23
+ # @param event [Symbol] The event name.
24
+ # @param data [Array] The information sended to the listeners.
25
+ def emit_event(event, *data)
26
+ listeners[event].collect{|l| l.send("on_#{event}".to_sym, *data) }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,47 @@
1
+ require 'taglib'
2
+
3
+ module CultomePlayer
4
+ module Media
5
+
6
+ # Get information from ID3 tags in a mp3.
7
+ #
8
+ # @param filepath [String] The absolute path to the mp3 file.
9
+ # @param opc [Hash] Additional parameters. Actually only :library_path is supported.
10
+ # @return [Hash] With information extracted from ID3 tags.
11
+ def extract_from_mp3(filepath, opc={})
12
+ info = nil
13
+ TagLib::FileRef.open(filepath) do |mp3|
14
+ unless mp3.nil?
15
+ info = {
16
+ # file information
17
+ file_path: filepath,
18
+ library_path: opc[:library_path],
19
+ # song information
20
+ album: mp3.tag.album,
21
+ artist: mp3.tag.artist,
22
+ genre: mp3.tag.genre,
23
+ name: mp3.tag.title,
24
+ track: mp3.tag.track,
25
+ year: mp3.tag.year,
26
+ duration: mp3.audio_properties.length,
27
+ }
28
+ end
29
+ end
30
+ # si no se encontro nombre de la cancion en las etiquestas, usamos el nombre del archivo
31
+ info[:name] = filepath.split('/').last if info[:name].nil?
32
+ # limpiamos la informacion un poco
33
+ return polish_mp3_info(info)
34
+ end
35
+
36
+ private
37
+
38
+ def polish_mp3_info(info)
39
+ [:genre, :name, :artist, :album].each{|k| info[k] = info[k].downcase.strip.titleize unless info[k].nil? }
40
+ [:track, :year].each{|k| info[k] = info[k].to_i if info[k] =~ /\A[\d]+\Z/ }
41
+ info[:duration] = info[:duration].to_i
42
+
43
+ return info
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,15 @@
1
+ require 'cultome_player/utils'
2
+
3
+ require 'cultome_player/objects/command'
4
+ require 'cultome_player/objects/parameter'
5
+ require 'cultome_player/objects/response'
6
+ require 'cultome_player/objects/song'
7
+ require 'cultome_player/objects/artist'
8
+ require 'cultome_player/objects/album'
9
+ require 'cultome_player/objects/genre'
10
+ require 'cultome_player/objects/drive'
11
+
12
+ module CultomePlayer
13
+ module Objects
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ require 'active_record'
2
+
3
+ module CultomePlayer
4
+ module Objects
5
+ # The ActiveRecord model for Album objects.
6
+ class Album < ActiveRecord::Base
7
+ has_many :songs
8
+ has_many :artists, through: :songs
9
+
10
+ def to_s
11
+ str = c4(":::: Album: ")
12
+ str += c13(self.name)
13
+ str += c4(" \\ Artist: ")
14
+ unless self.artists.nil? || self.artists.empty?
15
+ str += c11(self.artists.uniq.collect{|a| a.name}.join(', '))
16
+ end
17
+ str += c4(" ::::")
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ require 'active_record'
2
+
3
+ module CultomePlayer
4
+ module Objects
5
+ # The ActiveRecord model for Artist objects.
6
+ class Artist < ActiveRecord::Base
7
+ has_many :songs
8
+ has_many :albums, through: :songs
9
+ has_many :similars, as: :similar
10
+
11
+ def to_s
12
+ str = c4(":::: Artist: ")
13
+ str += c11(self.name)
14
+ str += c4(" ::::")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,37 @@
1
+ module CultomePlayer
2
+ module Objects
3
+ class Command
4
+ attr_reader :action
5
+ attr_reader :parameters
6
+
7
+ def initialize(action, parameters)
8
+ @action = action[:value]
9
+ @parameters = parameters.collect{|p| Parameter.new(p) }
10
+ end
11
+
12
+ # Returns the parameters, optionally filtered by type
13
+ #
14
+ # @param type [Symbol] Parameter type to filter the results
15
+ # @return [List<Parameter>] The parameters associated with the command, optionally filtered.
16
+ def params(type=nil)
17
+ return @parameters if type.nil?
18
+ @parameters.select{|p| p.type == type}
19
+ end
20
+
21
+ # Returns a map that contains parameter type as key and a list of the parameters of that type as value.
22
+ #
23
+ # @return [Hash<Symbol, List<Parameter>>] Parameters grouped by type.
24
+ def params_groups
25
+ @parameters.collect{|p| p.type }.each_with_object({}){|type,acc| acc[type] = params(type) }
26
+ end
27
+
28
+ # Returns a list with only the parameters values of certain type.
29
+ #
30
+ # @param type [Symbol] The type of parameters.
31
+ # @return [List<Object>] The values of the parameters.
32
+ def params_values(type)
33
+ params(type).map{|p| p.value }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ require 'active_record'
2
+
3
+ module CultomePlayer
4
+ module Objects
5
+ # The ActiveRecord model for Drive objects.
6
+ class Drive < ActiveRecord::Base
7
+ has_many :songs
8
+
9
+ def connected?
10
+ connected
11
+ end
12
+
13
+ def to_s
14
+ str = c4(":::: Drive: ")
15
+ str += c14(self.name)
16
+ str += c4(" => ")
17
+ str += c14(self.songs.size.to_s)
18
+ str += c4(" songs => ")
19
+ str += c14(self.path)
20
+ str += c4(" => ")
21
+ str += connected ? c3("Online") : c2("Offline")
22
+ str += c4(" ::::")
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ require 'active_record'
2
+
3
+ module CultomePlayer
4
+ module Objects
5
+ # The ActiveRecord model for Genre objects.
6
+ class Genre < ActiveRecord::Base
7
+ has_and_belongs_to_many :songs
8
+
9
+ def to_s
10
+ str = c4(":::: Genre: ")
11
+ str += c15(self.name)
12
+ str += c4(" ::::")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,37 @@
1
+ module CultomePlayer
2
+ module Objects
3
+ class Parameter
4
+ include CultomePlayer::Utils
5
+
6
+ # Initialize a parameter with the data provided.
7
+ #
8
+ # @param data [Hash] Contains the keys :criteria, :value, :type
9
+ def initialize(data)
10
+ @data = data
11
+ end
12
+
13
+ # Get the criteria asocciated with the parameter, if any.
14
+ def criteria
15
+ return nil if @data[:criteria].nil?
16
+ @data[:criteria].to_sym
17
+ end
18
+
19
+ # Returns the value associated with the parameter in its appropiated type.
20
+ #
21
+ # @return [Object] The value of the parameter.
22
+ def value
23
+ return is_true_value?(@data[:value]) if @data[:type] == :boolean
24
+ return @data[:value].to_i if @data[:type] == :number
25
+ return @data[:value].to_sym if @data[:type] == :object
26
+ @data[:value]
27
+ end
28
+
29
+ # Returns the type associated with the parameter.
30
+ #
31
+ # @return [Symbol] The type of the parameter.
32
+ def type
33
+ @data[:type]
34
+ end
35
+ end
36
+ end
37
+ end