media_player 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +40 -0
- data/Rakefile +8 -0
- data/bin/player +13 -0
- data/lib/input_manager.rb +58 -0
- data/lib/media_player.rb +9 -0
- data/lib/media_player/version.rb +4 -0
- data/lib/player.rb +66 -0
- data/lib/playlist.rb +48 -0
- data/lib/process_manager.rb +42 -0
- data/media_player.gemspec +25 -0
- data/spec/lib/input_manager_spec.rb +87 -0
- data/spec/lib/media_player/version_spec.rb +8 -0
- data/spec/lib/player_spec.rb +104 -0
- data/spec/lib/playlist_spec.rb +72 -0
- data/spec/lib/process_manager_spec.rb +44 -0
- data/spec/spec_helper.rb +4 -0
- metadata +105 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b823e5a0ea79b9cd97a918873cee8a6acfb0d127
|
4
|
+
data.tar.gz: f780d09c5fa8069164f9162caa9b28d907f18bab
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ce976d3e1ea6fdc21a54285996c138e80ac1d0fe0f0f3c9affbcec27bb453b4eda92f794815a41cf5b3fe5a4e04488de759cc30ba1cfaff0f346f932c5adaf1d
|
7
|
+
data.tar.gz: 57da31743fed05e8530aae44a25a82d32f58582141680ab7eeaa4f678bd65214850553847b19098c27476a60dc4537fef5972266909f8aa2242ebe4b5c227ce5
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
jruby-1.7.13
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 oozzal
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# MediaPlayer (Beta)
|
2
|
+
|
3
|
+
All in one command Line Media Player.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Dependencies: `jruby`, [sox]( http://sox.sourceforge.net/ )
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'media_player'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install media_player
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
* Make sure you're using `jruby`.
|
23
|
+
* Start the player: `player`
|
24
|
+
* Add media: `add <filename>`
|
25
|
+
* Play media: `play`
|
26
|
+
* Currently supported instructions: `add shuffle play stop pause next prev exit`
|
27
|
+
|
28
|
+
## TODO
|
29
|
+
* Tab complete instructions
|
30
|
+
* Add more function
|
31
|
+
|
32
|
+
## Contributing
|
33
|
+
|
34
|
+
Feel free to contribute.
|
35
|
+
|
36
|
+
1. Fork it
|
37
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
38
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
39
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
40
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/player
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require_relative '../lib/process_manager'
|
4
|
+
require_relative '../lib/playlist'
|
5
|
+
require_relative '../lib/player'
|
6
|
+
require_relative '../lib/input_manager'
|
7
|
+
|
8
|
+
pm = MediaPlayer::ProcessManager.new
|
9
|
+
pl = MediaPlayer::PlayList.new
|
10
|
+
p = MediaPlayer::Player.new(process_manager: pm, playlist: pl)
|
11
|
+
|
12
|
+
MediaPlayer::InputManager.new(player: p).start
|
13
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module MediaPlayer
|
4
|
+
class InputManager
|
5
|
+
extend Forwardable
|
6
|
+
# Shortcuts? maybe
|
7
|
+
# SHORTCUTS = %w(a sh pl st pa n pr e)
|
8
|
+
# SHORTCUT_MAPPINGS = {
|
9
|
+
# 'a' => 'add',
|
10
|
+
# 'sh' => 'shuffle',
|
11
|
+
# 'pl' => 'play',
|
12
|
+
# 'st' => 'stop',
|
13
|
+
# 'pa' => 'pause',
|
14
|
+
# 'n' => 'next',
|
15
|
+
# 'pr' => 'prev',
|
16
|
+
# 'e' => 'exit'
|
17
|
+
# }
|
18
|
+
INSTRUCTIONS = %w(add shuffle play stop pause next prev exit)
|
19
|
+
attr_reader :player
|
20
|
+
def_delegators :@player, :shuffle, :play, :stop, :pause, :next
|
21
|
+
def_delegator :@player, :previous, :prev
|
22
|
+
|
23
|
+
def initialize(args={})
|
24
|
+
@player = args.fetch(:player)
|
25
|
+
end
|
26
|
+
|
27
|
+
def process(instruction)
|
28
|
+
if INSTRUCTIONS.include? instruction.split(' ').first
|
29
|
+
begin
|
30
|
+
if instruction.match(/add/)
|
31
|
+
path = (instruction.split(' ') - ['add']).join(' ')
|
32
|
+
type = File.ftype(path)
|
33
|
+
if type == 'directory'
|
34
|
+
Dir.glob("#{path}/*.mp3") { |file| @player.add_media(file) }
|
35
|
+
elsif type == 'file'
|
36
|
+
@player.add_media(path)
|
37
|
+
end
|
38
|
+
else
|
39
|
+
send instruction
|
40
|
+
end
|
41
|
+
rescue => e
|
42
|
+
return e.message
|
43
|
+
end
|
44
|
+
else
|
45
|
+
return 'Invalid Instruction'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# TODO: The only thing not covered by test
|
50
|
+
def start
|
51
|
+
while (input = gets.chomp) != 'exit'
|
52
|
+
p process(input)
|
53
|
+
end
|
54
|
+
p process('stop')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
data/lib/media_player.rb
ADDED
data/lib/player.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require_relative './media_player/version'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module MediaPlayer
|
5
|
+
class Player
|
6
|
+
extend Forwardable
|
7
|
+
attr_reader :process_manager, :playlist, :is_playing, :paused
|
8
|
+
def_delegator :@process_manager, :current_process_id
|
9
|
+
def_delegators :@playlist, :media, :current_media, :next_media, :previous_media, :shuffle
|
10
|
+
|
11
|
+
def initialize(args = {})
|
12
|
+
@process_manager = args.fetch(:process_manager)
|
13
|
+
@playlist = args.fetch(:playlist)
|
14
|
+
@is_playing = false
|
15
|
+
@paused = false
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_media(media_file)
|
19
|
+
@playlist.add(media_file)
|
20
|
+
end
|
21
|
+
|
22
|
+
def play
|
23
|
+
return if @is_playing
|
24
|
+
if @paused
|
25
|
+
@paused = false
|
26
|
+
@process_manager.resume
|
27
|
+
else
|
28
|
+
@is_playing = true
|
29
|
+
@process_manager.execute(current_media)
|
30
|
+
end
|
31
|
+
observe
|
32
|
+
end
|
33
|
+
|
34
|
+
def stop
|
35
|
+
@is_playing = false
|
36
|
+
@process_manager.stop
|
37
|
+
end
|
38
|
+
|
39
|
+
def pause
|
40
|
+
@is_playing = false
|
41
|
+
@paused = true
|
42
|
+
@process_manager.pause
|
43
|
+
end
|
44
|
+
|
45
|
+
def next
|
46
|
+
@process_manager.stop
|
47
|
+
@process_manager.execute(next_media)
|
48
|
+
end
|
49
|
+
|
50
|
+
def previous
|
51
|
+
@process_manager.stop
|
52
|
+
@process_manager.execute(previous_media)
|
53
|
+
end
|
54
|
+
|
55
|
+
def observe
|
56
|
+
Thread.new do
|
57
|
+
while @is_playing
|
58
|
+
self.next unless @process_manager.is_current_process_alive?
|
59
|
+
sleep 2
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
data/lib/playlist.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module MediaPlayer
|
2
|
+
class PlayList
|
3
|
+
attr_reader :media, :current_index
|
4
|
+
|
5
|
+
def initialize(media = [])
|
6
|
+
@media = media
|
7
|
+
@current_index = 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def filter_input(raw_input)
|
11
|
+
# weird :-O
|
12
|
+
raw_input = raw_input.gsub("'", "\\\\'")
|
13
|
+
raw_input = raw_input.gsub('"', '\"')
|
14
|
+
raw_input = raw_input.gsub('&', '\\\&')
|
15
|
+
regex = /[()\s]/
|
16
|
+
raw_input.gsub regex do |match|
|
17
|
+
match.gsub("#{match}", "\\#{match}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def add(raw_media_file)
|
22
|
+
media_file = filter_input(raw_media_file)
|
23
|
+
@media.push(media_file)
|
24
|
+
end
|
25
|
+
|
26
|
+
def current_media
|
27
|
+
@media[@current_index]
|
28
|
+
end
|
29
|
+
|
30
|
+
def next_media
|
31
|
+
@current_index += 1
|
32
|
+
@current_index = 0 if @media.size == @current_index
|
33
|
+
current_media
|
34
|
+
end
|
35
|
+
|
36
|
+
def previous_media
|
37
|
+
@current_index -= 1
|
38
|
+
@current_index = @media.size - 1 if @current_index < 0
|
39
|
+
current_media
|
40
|
+
end
|
41
|
+
|
42
|
+
def shuffle
|
43
|
+
old_current_media = current_media
|
44
|
+
@media.shuffle!
|
45
|
+
@current_index = @media.index(old_current_media)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module MediaPlayer
|
2
|
+
# execute, stop, pause, resume
|
3
|
+
class ProcessManager
|
4
|
+
attr_accessor :current_process_id
|
5
|
+
|
6
|
+
def build_process(media_file)
|
7
|
+
Process.spawn("play #{media_file}")
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute(media_file)
|
11
|
+
@current_process_id = build_process(media_file)
|
12
|
+
end
|
13
|
+
|
14
|
+
def pause
|
15
|
+
Process.kill(:STOP, @current_process_id)
|
16
|
+
end
|
17
|
+
|
18
|
+
def resume
|
19
|
+
Process.kill(:CONT, @current_process_id)
|
20
|
+
end
|
21
|
+
|
22
|
+
def stop
|
23
|
+
begin
|
24
|
+
Process.kill(:INT, @current_process_id)
|
25
|
+
Process.waitpid(@current_process_id)
|
26
|
+
rescue
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def is_current_process_alive?
|
31
|
+
begin
|
32
|
+
Process.getpgid(@current_process_id)
|
33
|
+
true
|
34
|
+
rescue Errno::ESRCH
|
35
|
+
false
|
36
|
+
rescue
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'media_player/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "media_player"
|
8
|
+
spec.version = MediaPlayer::VERSION
|
9
|
+
spec.authors = ["oozzal"]
|
10
|
+
spec.email = ["theoozzal@gmail.com"]
|
11
|
+
spec.description = %q{Command line media player.}
|
12
|
+
spec.summary = %q{Plays media files right from the command line giving some basic media controls.}
|
13
|
+
spec.homepage = "http://github.com/oozzal/media_player"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = ['player']
|
18
|
+
#spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
23
|
+
spec.add_development_dependency "rake"
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe 'An instance of InputManager', MediaPlayer::InputManager do
|
4
|
+
let(:player) { double('player') }
|
5
|
+
subject { MediaPlayer::InputManager.new player: player }
|
6
|
+
|
7
|
+
it 'has instructions and options' do
|
8
|
+
expect(subject.class::INSTRUCTIONS).not_to be_nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it { expect(subject.player).not_to be_nil }
|
12
|
+
|
13
|
+
describe '#process' do
|
14
|
+
context 'when instruction is invalid' do
|
15
|
+
it {
|
16
|
+
return_value = subject.process('hawa jpt')
|
17
|
+
expect(subject.process 'jpt').to eql 'Invalid Instruction'
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when instruction is valid' do
|
22
|
+
context 'when add' do
|
23
|
+
context 'when adding directory' do
|
24
|
+
it {
|
25
|
+
dir = '/Users/uzzaldevkota/Music/Selected Music'
|
26
|
+
expect(File).to receive(:ftype).and_return 'directory'
|
27
|
+
search = "#{dir}/*.mp3"
|
28
|
+
expect(Dir).to receive(:glob).with search
|
29
|
+
subject.process("add #{dir}")
|
30
|
+
}
|
31
|
+
end
|
32
|
+
context 'when adding file' do
|
33
|
+
it {
|
34
|
+
file = '/Users/uzzaldevkota/Music/John Lennon imagine.mp3'
|
35
|
+
expect(File).to receive(:ftype).and_return 'file'
|
36
|
+
expect(subject.player).to receive(:add_media).with file
|
37
|
+
subject.process("add #{file}")
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when shuffle' do
|
43
|
+
it {
|
44
|
+
expect(subject.player).to receive(:shuffle).with no_args
|
45
|
+
subject.process('shuffle')
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'when play' do
|
50
|
+
it {
|
51
|
+
expect(subject.player).to receive(:play).with no_args
|
52
|
+
subject.process('play')
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'when stop' do
|
57
|
+
it {
|
58
|
+
expect(player).to receive(:stop).with no_args
|
59
|
+
subject.process('stop')
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when pause' do
|
64
|
+
it {
|
65
|
+
expect(player).to receive(:pause).with no_args
|
66
|
+
subject.process('pause')
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'when next' do
|
71
|
+
it {
|
72
|
+
expect(player).to receive(:next).with no_args
|
73
|
+
subject.process('next')
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'when prev' do
|
78
|
+
it {
|
79
|
+
expect(player).to receive(:previous).with no_args
|
80
|
+
subject.process('prev')
|
81
|
+
}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe 'An Instance of Player', MediaPlayer::Player do
|
4
|
+
let(:sample_media) { 'imagine.mp3' }
|
5
|
+
let(:media) { [] }
|
6
|
+
let(:process_manager) { double('process_manager') }
|
7
|
+
let(:playlist) {
|
8
|
+
double(
|
9
|
+
'playlist',
|
10
|
+
media: media,
|
11
|
+
current_media: nil, next_media: nil,
|
12
|
+
previous_media: nil, shuffle: nil
|
13
|
+
)
|
14
|
+
}
|
15
|
+
|
16
|
+
# Beware, error in testing observe method
|
17
|
+
# before { MediaPlayer::Player.any_instance.stub(:observe) }
|
18
|
+
subject {
|
19
|
+
MediaPlayer::Player.new(
|
20
|
+
process_manager: process_manager,
|
21
|
+
playlist: playlist
|
22
|
+
)
|
23
|
+
}
|
24
|
+
|
25
|
+
it { expect(subject.is_playing).to eql false }
|
26
|
+
it { expect(subject.paused).to eql false }
|
27
|
+
|
28
|
+
# Delegated methods
|
29
|
+
it 'responds to delegated methods' do
|
30
|
+
expect(subject).to respond_to(:current_process_id)
|
31
|
+
expect(subject).to respond_to(:media)
|
32
|
+
expect(subject).to respond_to(:current_media)
|
33
|
+
expect(subject).to respond_to(:next_media)
|
34
|
+
expect(subject).to respond_to(:previous_media)
|
35
|
+
expect(subject).to respond_to(:shuffle)
|
36
|
+
end
|
37
|
+
|
38
|
+
it { expect(subject.media).to eql [] }
|
39
|
+
|
40
|
+
describe '#add_media' do
|
41
|
+
it 'pushes media to the playlist' do
|
42
|
+
expect(subject.playlist).to receive(:add).with sample_media
|
43
|
+
subject.add_media sample_media
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'when initialized with some media' do
|
48
|
+
before { allow(subject.playlist).to receive(:current_media).and_return sample_media }
|
49
|
+
let(:media) { ['a.mp3', 'b.wav'] }
|
50
|
+
it { expect(subject.playlist).not_to eql nil }
|
51
|
+
|
52
|
+
describe '#play' do
|
53
|
+
context 'when player is not playing any media' do
|
54
|
+
it 'plays the media in the playlist' do
|
55
|
+
expect(subject.process_manager).to receive(:execute).with sample_media
|
56
|
+
expect(subject).to receive(:observe).with no_args
|
57
|
+
subject.play
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when player has paused some media' do
|
61
|
+
before { subject.instance_variable_set('@paused', true) }
|
62
|
+
it 'resumes the paused media' do
|
63
|
+
expect(subject.process_manager).to receive(:resume).with no_args
|
64
|
+
subject.play
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#stop' do
|
71
|
+
it 'stops the current media' do
|
72
|
+
expect(subject.process_manager).to receive(:stop).with no_args
|
73
|
+
subject.stop
|
74
|
+
expect(subject.is_playing).to eql false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#pause' do
|
79
|
+
it 'pauses the currently playing media from the playlist' do
|
80
|
+
expect(subject.process_manager).to receive(:pause).with no_args
|
81
|
+
subject.pause
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#next' do
|
86
|
+
it 'plays the next media from the playlist' do
|
87
|
+
expect(subject.playlist).to receive(:next_media).and_return sample_media
|
88
|
+
expect(subject.process_manager).to receive(:stop).with no_args
|
89
|
+
expect(subject.process_manager).to receive(:execute).with sample_media
|
90
|
+
subject.next
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe '#previous' do
|
95
|
+
it 'plays the previous media in the playlist' do
|
96
|
+
expect(subject.playlist).to receive(:previous_media).and_return sample_media
|
97
|
+
expect(subject.process_manager).to receive(:stop).with no_args
|
98
|
+
expect(subject.process_manager).to receive(:execute).with sample_media
|
99
|
+
subject.previous
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe 'An Instance of Playlist', MediaPlayer::PlayList do
|
4
|
+
subject { MediaPlayer::PlayList.new(['a.mp3', 'b.wav', 'c.mp3', 'd.wav']) }
|
5
|
+
context 'when initialized' do
|
6
|
+
it { expect(subject.media).not_to be_empty }
|
7
|
+
it { expect(subject.current_index).not_to be_nil }
|
8
|
+
end
|
9
|
+
|
10
|
+
context '#add' do
|
11
|
+
let(:sample_media) { 'imagine.mp3' }
|
12
|
+
before { subject.add(sample_media) }
|
13
|
+
|
14
|
+
it { expect(subject.media.include?(sample_media)).to eql true }
|
15
|
+
|
16
|
+
context 'when illegal characters occur in the path to media' do
|
17
|
+
let(:malicious_media) { '(02) - John Mayer Assassin.mp3' }
|
18
|
+
let(:malicious_media2) { "Free' Falling.mp3" }
|
19
|
+
let(:malicious_media3) { 'Free" Falling.mp3' }
|
20
|
+
let(:malicious_media4) { 'Pink & Nate Reus.mp3' }
|
21
|
+
|
22
|
+
it 'escapes them' do
|
23
|
+
subject.add(malicious_media)
|
24
|
+
expect(subject.media.last).to eql '\(02\)\ -\ John\ Mayer\ Assassin.mp3'
|
25
|
+
subject.add(malicious_media2)
|
26
|
+
expect(subject.media.last).to eql "Free\\'\\ Falling.mp3"
|
27
|
+
subject.add(malicious_media3)
|
28
|
+
expect(subject.media.last).to eql 'Free\"\\ Falling.mp3'
|
29
|
+
subject.add(malicious_media4)
|
30
|
+
expect(subject.media.last).to eql 'Pink\ \&\ Nate\ Reus.mp3'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context '#shuffle' do
|
36
|
+
it 're-adjusts the current_index to the current_media' do
|
37
|
+
current_media = subject.current_media
|
38
|
+
subject.shuffle
|
39
|
+
expect(subject.current_media).to eql current_media
|
40
|
+
end
|
41
|
+
|
42
|
+
it {
|
43
|
+
expect(subject.media).to receive(:shuffle!).with no_args
|
44
|
+
subject.shuffle
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
context '#current_media' do
|
49
|
+
it { expect(subject.current_media).to eql 'a.mp3' }
|
50
|
+
end
|
51
|
+
|
52
|
+
context '#next_media' do
|
53
|
+
it { expect(subject.next_media).to eql 'b.wav' }
|
54
|
+
|
55
|
+
context 'when end of playlist is reached' do
|
56
|
+
before { subject.instance_variable_set('@current_index', subject.media.size - 1) }
|
57
|
+
# TODO: shuffle automatically on next loop?
|
58
|
+
it { expect(subject.next_media).to eql 'a.mp3' }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context '#previous_media' do
|
63
|
+
before { subject.instance_variable_set('@current_index', 1) }
|
64
|
+
it { expect(subject.previous_media).to eql 'a.mp3' }
|
65
|
+
|
66
|
+
context 'when beginning of playlist is reached' do
|
67
|
+
before { subject.instance_variable_set('@current_index', 0) }
|
68
|
+
it { expect(subject.previous_media).to eql 'd.wav' }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
|
3
|
+
describe 'An Instance of ProcessManager', MediaPlayer::ProcessManager do
|
4
|
+
let(:sample_media) { 'imagine.mp3' }
|
5
|
+
let(:sample_pid) { 123 }
|
6
|
+
let(:play_command) { "play #{sample_media}" }
|
7
|
+
subject { MediaPlayer::ProcessManager.new }
|
8
|
+
it { expect(subject.current_process_id).to be_nil }
|
9
|
+
|
10
|
+
context '#execute' do
|
11
|
+
before { allow(Process).to receive(:spawn).and_return(sample_pid) }
|
12
|
+
it 'plays the given media' do
|
13
|
+
expect(Process).to receive(:spawn).with(play_command)
|
14
|
+
subject.execute(sample_media)
|
15
|
+
expect(subject.current_process_id).to eql sample_pid
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'when manipulating current process' do
|
20
|
+
before { subject.current_process_id = sample_pid }
|
21
|
+
context '#pause' do
|
22
|
+
it 'pauses the given media' do
|
23
|
+
expect(Process).to receive(:kill).with(:STOP, sample_pid)
|
24
|
+
subject.pause
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context '#resume' do
|
29
|
+
it 'resumes the paused media' do
|
30
|
+
expect(Process).to receive(:kill).with(:CONT, sample_pid)
|
31
|
+
subject.resume
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context '#stop' do
|
36
|
+
it 'stops the current media' do
|
37
|
+
expect(Process).to receive(:kill).with(:INT, sample_pid)
|
38
|
+
expect(Process).to receive(:waitpid).with(sample_pid)
|
39
|
+
subject.stop
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: media_player
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- oozzal
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-10-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
version_requirements: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
requirement: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ~>
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '1.3'
|
25
|
+
prerelease: false
|
26
|
+
type: :development
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
prerelease: false
|
40
|
+
type: :development
|
41
|
+
description: Command line media player.
|
42
|
+
email:
|
43
|
+
- theoozzal@gmail.com
|
44
|
+
executables:
|
45
|
+
- player
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- .gitignore
|
50
|
+
- .rspec
|
51
|
+
- .ruby-version
|
52
|
+
- .travis.yml
|
53
|
+
- Gemfile
|
54
|
+
- LICENSE.txt
|
55
|
+
- README.md
|
56
|
+
- Rakefile
|
57
|
+
- bin/player
|
58
|
+
- lib/.DS_Store
|
59
|
+
- lib/input_manager.rb
|
60
|
+
- lib/media_player.rb
|
61
|
+
- lib/media_player/version.rb
|
62
|
+
- lib/player.rb
|
63
|
+
- lib/playlist.rb
|
64
|
+
- lib/process_manager.rb
|
65
|
+
- media_player.gemspec
|
66
|
+
- spec/.DS_Store
|
67
|
+
- spec/lib/input_manager_spec.rb
|
68
|
+
- spec/lib/media_player/version_spec.rb
|
69
|
+
- spec/lib/player_spec.rb
|
70
|
+
- spec/lib/playlist_spec.rb
|
71
|
+
- spec/lib/process_manager_spec.rb
|
72
|
+
- spec/spec_helper.rb
|
73
|
+
homepage: http://github.com/oozzal/media_player
|
74
|
+
licenses:
|
75
|
+
- MIT
|
76
|
+
metadata: {}
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options: []
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubyforge_project:
|
93
|
+
rubygems_version: 2.1.9
|
94
|
+
signing_key:
|
95
|
+
specification_version: 4
|
96
|
+
summary: Plays media files right from the command line giving some basic media controls.
|
97
|
+
test_files:
|
98
|
+
- spec/.DS_Store
|
99
|
+
- spec/lib/input_manager_spec.rb
|
100
|
+
- spec/lib/media_player/version_spec.rb
|
101
|
+
- spec/lib/player_spec.rb
|
102
|
+
- spec/lib/playlist_spec.rb
|
103
|
+
- spec/lib/process_manager_spec.rb
|
104
|
+
- spec/spec_helper.rb
|
105
|
+
has_rdoc:
|