qtunes 0.0.1
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.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/README +4 -0
- data/Rakefile +1 -0
- data/bin/qtunes +24 -0
- data/lib/qtunes.rb +6 -0
- data/lib/qtunes/player.rb +88 -0
- data/lib/qtunes/server.rb +76 -0
- data/lib/qtunes/server/public/base.css +129 -0
- data/lib/qtunes/server/views/layout.erb +22 -0
- data/lib/qtunes/server/views/songs.erb +31 -0
- data/lib/qtunes/version.rb +3 -0
- data/qtunes.gemspec +27 -0
- metadata +105 -0
data/Gemfile
ADDED
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/qtunes
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "rubygems"
|
4
|
+
require "qtunes"
|
5
|
+
require "thor"
|
6
|
+
|
7
|
+
class QtunesCLI < Thor
|
8
|
+
include Thor::Actions
|
9
|
+
|
10
|
+
map 's' => :server
|
11
|
+
desc 'server', 'Serve Qtunes frontend'
|
12
|
+
def server
|
13
|
+
puts "== Starting Qtunes server"
|
14
|
+
Qtunes::Server.run!
|
15
|
+
end
|
16
|
+
|
17
|
+
map '-v' => :version
|
18
|
+
desc 'version', 'Display installed Qtunes version'
|
19
|
+
def version
|
20
|
+
puts "Qtunes #{Qtunes::VERSION}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
QtunesCLI.start
|
data/lib/qtunes.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'cocaine'
|
2
|
+
|
3
|
+
module Qtunes
|
4
|
+
class Player
|
5
|
+
INFO_KEYS = %w(status file duration position)
|
6
|
+
|
7
|
+
def info_raw
|
8
|
+
execute("status").split("\n")
|
9
|
+
end
|
10
|
+
|
11
|
+
def info
|
12
|
+
info_raw.inject({}) do |res,i|
|
13
|
+
k,v = i.split(" ", 2)
|
14
|
+
res[k] = v if INFO_KEYS.include?(k)
|
15
|
+
res
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# The current state of the player.
|
20
|
+
#
|
21
|
+
# Examples
|
22
|
+
#
|
23
|
+
# player = Qtunes::Player.new
|
24
|
+
# player.status
|
25
|
+
# # => 'playing'
|
26
|
+
#
|
27
|
+
# returns String, being one of: 'playing', 'paused', 'stopped'.
|
28
|
+
def status
|
29
|
+
info['status']
|
30
|
+
end
|
31
|
+
|
32
|
+
def file
|
33
|
+
info['file']
|
34
|
+
end
|
35
|
+
|
36
|
+
def view_queue
|
37
|
+
execute('view queue')
|
38
|
+
end
|
39
|
+
|
40
|
+
def win_top
|
41
|
+
execute('win-top')
|
42
|
+
end
|
43
|
+
|
44
|
+
def win_down
|
45
|
+
execute('win-down')
|
46
|
+
end
|
47
|
+
|
48
|
+
def win_remove
|
49
|
+
execute('win-remove')
|
50
|
+
end
|
51
|
+
|
52
|
+
def play
|
53
|
+
execute('player-play')
|
54
|
+
end
|
55
|
+
|
56
|
+
def stop
|
57
|
+
execute('player-stop')
|
58
|
+
end
|
59
|
+
|
60
|
+
def pause
|
61
|
+
execute('player-pause')
|
62
|
+
end
|
63
|
+
|
64
|
+
def next
|
65
|
+
execute('player-next')
|
66
|
+
end
|
67
|
+
|
68
|
+
def prev
|
69
|
+
execute('player-prev')
|
70
|
+
end
|
71
|
+
|
72
|
+
def enqueue(file)
|
73
|
+
Cocaine::CommandLine.new('cmus-remote', "-q '#{file}'").run
|
74
|
+
end
|
75
|
+
|
76
|
+
def queue
|
77
|
+
execute('save -q -').split("\n")
|
78
|
+
end
|
79
|
+
|
80
|
+
def library
|
81
|
+
execute('save -l -').split("\n")
|
82
|
+
end
|
83
|
+
|
84
|
+
def execute(command)
|
85
|
+
Cocaine::CommandLine.new('cmus-remote', '-C :command', :command => command).run
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'digest/sha2'
|
3
|
+
require 'audioinfo'
|
4
|
+
|
5
|
+
module Qtunes
|
6
|
+
class Server < Sinatra::Base
|
7
|
+
dir = File.dirname(File.expand_path(__FILE__))
|
8
|
+
|
9
|
+
set :views, "#{dir}/server/views"
|
10
|
+
set :public, "#{dir}/server/public"
|
11
|
+
set :static, true
|
12
|
+
|
13
|
+
get '/' do
|
14
|
+
@song = PLAYER.file
|
15
|
+
@songs = queue
|
16
|
+
|
17
|
+
erb :songs
|
18
|
+
end
|
19
|
+
|
20
|
+
get '/library' do
|
21
|
+
@song = PLAYER.file
|
22
|
+
@songs = library
|
23
|
+
|
24
|
+
erb :songs
|
25
|
+
end
|
26
|
+
|
27
|
+
get '/add/:id' do
|
28
|
+
PLAYER.enqueue(library[params[:id]][:path])
|
29
|
+
|
30
|
+
redirect '/'
|
31
|
+
end
|
32
|
+
|
33
|
+
get '/remove/:id' do
|
34
|
+
ix = library.keys.index(params[:id])
|
35
|
+
|
36
|
+
# badass!
|
37
|
+
PLAYER.view_queue
|
38
|
+
PLAYER.win_top
|
39
|
+
ix.times{ PLAYER.win_down }
|
40
|
+
PLAYER.win_remove
|
41
|
+
|
42
|
+
redirect '/'
|
43
|
+
end
|
44
|
+
|
45
|
+
configure do
|
46
|
+
puts "Configure"
|
47
|
+
|
48
|
+
PLAYER = Qtunes::Player.new
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.queue
|
52
|
+
songs_to_hash{ PLAYER.queue }
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.library
|
56
|
+
@library ||= songs_to_hash{ PLAYER.library }
|
57
|
+
end
|
58
|
+
|
59
|
+
protected
|
60
|
+
def self.songs_to_hash
|
61
|
+
yield.inject({}) do |res,path|
|
62
|
+
begin
|
63
|
+
song = {:path => path}.merge(AudioInfo.open(path).to_h)
|
64
|
+
rescue AudioInfoError
|
65
|
+
next res
|
66
|
+
end
|
67
|
+
res[song_id(path)] = song
|
68
|
+
res
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.song_id(file)
|
73
|
+
Digest::SHA256.hexdigest(file)[0,10]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
/*
|
2
|
+
|
3
|
+
dark: 485460
|
4
|
+
3D72AA
|
5
|
+
5E7894
|
6
|
+
438FDF
|
7
|
+
D0E4FA
|
8
|
+
*/
|
9
|
+
|
10
|
+
html{
|
11
|
+
font-family: Helvetica;
|
12
|
+
}
|
13
|
+
body{
|
14
|
+
margin: 0;
|
15
|
+
padding: 0;
|
16
|
+
color: #5E7894;
|
17
|
+
}
|
18
|
+
h1{
|
19
|
+
margin: 1em 10px 0 10px;
|
20
|
+
padding: 0;
|
21
|
+
letter-spacing: -3px;
|
22
|
+
color: #000;
|
23
|
+
}
|
24
|
+
a{
|
25
|
+
color: #5E7894;
|
26
|
+
text-decoration: none;
|
27
|
+
}
|
28
|
+
a:hover{
|
29
|
+
color: #000;
|
30
|
+
}
|
31
|
+
|
32
|
+
.navigation{
|
33
|
+
text-align: right;
|
34
|
+
padding: 2px 10px;
|
35
|
+
font-weight: bold;
|
36
|
+
letter-spacing: -1px;
|
37
|
+
font-size: 1.4em;
|
38
|
+
}
|
39
|
+
.navigation a{
|
40
|
+
color: #485460;
|
41
|
+
padding-left: 10px;
|
42
|
+
text-decoration: none;
|
43
|
+
}
|
44
|
+
.navigation form{
|
45
|
+
display: inline;
|
46
|
+
}
|
47
|
+
.navigation input{
|
48
|
+
vertical-align: top;
|
49
|
+
width: 250px;
|
50
|
+
}
|
51
|
+
.content{
|
52
|
+
padding: 10px;
|
53
|
+
background-color: #D0E4FA;
|
54
|
+
}
|
55
|
+
.gravatar{
|
56
|
+
-webkit-border-radius: 5px;
|
57
|
+
}
|
58
|
+
|
59
|
+
/*
|
60
|
+
* SONGS
|
61
|
+
*/
|
62
|
+
.songs{
|
63
|
+
|
64
|
+
}
|
65
|
+
.song{
|
66
|
+
letter-spacing: -1px;
|
67
|
+
background-color: #D0E4FA;
|
68
|
+
border-top: 2px solid #fff;
|
69
|
+
padding: .5em 10px;
|
70
|
+
font-size: 1.25em;
|
71
|
+
font-weight: bold;
|
72
|
+
}
|
73
|
+
.song a:hover{
|
74
|
+
color: #000 !important;
|
75
|
+
}
|
76
|
+
.song .artist{
|
77
|
+
font-size: 3em;
|
78
|
+
letter-spacing: -5px;
|
79
|
+
}
|
80
|
+
.song .artist a{
|
81
|
+
color: #485460;
|
82
|
+
}
|
83
|
+
.song .title{
|
84
|
+
padding-left: 10px;
|
85
|
+
font-size: 2.2em;
|
86
|
+
letter-spacing: -4px;
|
87
|
+
}
|
88
|
+
.song .album{
|
89
|
+
margin-left: 10px;
|
90
|
+
font-size: 1.1em;
|
91
|
+
letter-spacing: -2px;
|
92
|
+
}
|
93
|
+
.song .album a{
|
94
|
+
color: #438FDF;
|
95
|
+
}
|
96
|
+
.song .controls{
|
97
|
+
float: right;
|
98
|
+
font-size: .75em;
|
99
|
+
margin: -.5em -5px;
|
100
|
+
text-transform: uppercase;
|
101
|
+
}
|
102
|
+
.votes{
|
103
|
+
background-color: #485460;
|
104
|
+
padding: 5px 5px 0px 5px;
|
105
|
+
}
|
106
|
+
.votes img{
|
107
|
+
border: 1px solid #5E7894;
|
108
|
+
}
|
109
|
+
.votes img:hover{
|
110
|
+
border: 1px solid #D0E4FA;
|
111
|
+
}
|
112
|
+
|
113
|
+
/*
|
114
|
+
* PROFILE
|
115
|
+
*/
|
116
|
+
.profile_box{
|
117
|
+
margin-right: 10px;
|
118
|
+
float: left;
|
119
|
+
font-size: 1.1em;
|
120
|
+
}
|
121
|
+
.bio{
|
122
|
+
padding: 15px 0;
|
123
|
+
}
|
124
|
+
.favorite_artists{
|
125
|
+
margin: 0 15px;
|
126
|
+
}
|
127
|
+
.favorite_artists h2{
|
128
|
+
color: #485460;
|
129
|
+
}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Qtunes</title>
|
5
|
+
<link rel="stylesheet" href="/base.css" type="text/css" />
|
6
|
+
</head>
|
7
|
+
|
8
|
+
<body>
|
9
|
+
<h1>Title</h1>
|
10
|
+
|
11
|
+
<div class="navigation">
|
12
|
+
<form method="get" action="/search">
|
13
|
+
<input type="search" name="q" />
|
14
|
+
</form>
|
15
|
+
|
16
|
+
<a href="/">Queue</a>
|
17
|
+
<a href="/library">Library</a>
|
18
|
+
</div>
|
19
|
+
|
20
|
+
<%= yield %>
|
21
|
+
</body>
|
22
|
+
</html>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<p>Now playing: '<%= @song %>'</p>
|
2
|
+
|
3
|
+
<% @songs.each do |id, song| %>
|
4
|
+
<div class="song">
|
5
|
+
<span class="controls">
|
6
|
+
<% if queue[id] %>
|
7
|
+
<a href="/remove/<%= id %>" title="remove from queue">-</a>
|
8
|
+
<% else %>
|
9
|
+
<a href="/add/<%= id %>" title="add to queue">+</a>
|
10
|
+
<% end %>
|
11
|
+
</span>
|
12
|
+
|
13
|
+
|
14
|
+
<span class="artist">
|
15
|
+
<a href="/artist/{{artist_name}}"><%= song['artist'] %></a>
|
16
|
+
</span>
|
17
|
+
|
18
|
+
<span class="title">
|
19
|
+
<a href="/song/{{id}}"><%= song['title'] %></a>
|
20
|
+
</span>
|
21
|
+
<span class="album">
|
22
|
+
<a href="/artist/{{artist_name}}/album/{{album_name}}"><%= song['album'] %></a>
|
23
|
+
</span>
|
24
|
+
</div>
|
25
|
+
<% end %>
|
26
|
+
|
27
|
+
<% if @songs.empty? %>
|
28
|
+
<div class="content">
|
29
|
+
The queue is empty. Quick, play some <a href="/artist/Ace+of+Base">Ace of Base</a>; no one's looking.
|
30
|
+
</div>
|
31
|
+
<% end %>
|
data/qtunes.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "qtunes/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "qtunes"
|
7
|
+
s.version = Qtunes::VERSION
|
8
|
+
s.authors = ["Gert Goet"]
|
9
|
+
s.email = ["gert@thinkcreate.nl"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{add songs to queue, let cmus eat it}
|
12
|
+
s.description = %q{add songs to queue, let cmus eat it}
|
13
|
+
|
14
|
+
s.rubyforge_project = "qtunes"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency "rspec"
|
23
|
+
s.add_runtime_dependency "sinatra"
|
24
|
+
s.add_runtime_dependency "thor"
|
25
|
+
s.add_runtime_dependency "cocaine"
|
26
|
+
s.add_runtime_dependency "ruby-audioinfo"
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: qtunes
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Gert Goet
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-08-23 00:00:00.000000000 +02:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: sinatra
|
17
|
+
requirement: &8511880 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *8511880
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: thor
|
28
|
+
requirement: &8511460 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *8511460
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: cocaine
|
39
|
+
requirement: &8511040 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
type: :runtime
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *8511040
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: ruby-audioinfo
|
50
|
+
requirement: &8510620 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
type: :runtime
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: *8510620
|
59
|
+
description: add songs to queue, let cmus eat it
|
60
|
+
email:
|
61
|
+
- gert@thinkcreate.nl
|
62
|
+
executables:
|
63
|
+
- qtunes
|
64
|
+
extensions: []
|
65
|
+
extra_rdoc_files: []
|
66
|
+
files:
|
67
|
+
- .gitignore
|
68
|
+
- Gemfile
|
69
|
+
- README
|
70
|
+
- Rakefile
|
71
|
+
- bin/qtunes
|
72
|
+
- lib/qtunes.rb
|
73
|
+
- lib/qtunes/player.rb
|
74
|
+
- lib/qtunes/server.rb
|
75
|
+
- lib/qtunes/server/public/base.css
|
76
|
+
- lib/qtunes/server/views/layout.erb
|
77
|
+
- lib/qtunes/server/views/songs.erb
|
78
|
+
- lib/qtunes/version.rb
|
79
|
+
- qtunes.gemspec
|
80
|
+
has_rdoc: true
|
81
|
+
homepage: ''
|
82
|
+
licenses: []
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ! '>='
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
requirements: []
|
100
|
+
rubyforge_project: qtunes
|
101
|
+
rubygems_version: 1.6.2
|
102
|
+
signing_key:
|
103
|
+
specification_version: 3
|
104
|
+
summary: add songs to queue, let cmus eat it
|
105
|
+
test_files: []
|