cultome_player 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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