qtunes 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in qtunes.gemspec
4
+ gemspec
data/README ADDED
@@ -0,0 +1,4 @@
1
+ Qtunes let's you control the queue of a local cmus-player via a web-interface.
2
+
3
+ # start server
4
+ $> qtunes s[erver]
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,6 @@
1
+ require "qtunes/player"
2
+ require "qtunes/version"
3
+ require "qtunes/server"
4
+
5
+ module Qtunes
6
+ end
@@ -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 %>
@@ -0,0 +1,3 @@
1
+ module Qtunes
2
+ VERSION = "0.0.1"
3
+ 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: []