rubio-radio 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/LICENSE.txt +1 -1
- data/README.md +20 -4
- data/TODO.md +7 -1
- data/exe/rubio +9 -4
- data/lib/rubio/model/bookmark.rb +54 -0
- data/lib/rubio/model/player.rb +59 -0
- data/lib/rubio/model/radio_browser.rb +28 -0
- data/lib/rubio/model/station.rb +34 -0
- data/lib/rubio/version.rb +1 -1
- data/lib/rubio/view/radio.rb +297 -0
- data/lib/rubio.rb +1 -6
- metadata +9 -8
- data/lib/rubio/player.rb +0 -57
- data/lib/rubio/radio.rb +0 -164
- data/lib/rubio/radio_browser.rb +0 -26
- data/lib/rubio/station.rb +0 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 251dc5095f4659c3598be19ed733cc71ed404957a582071eed209d0ae2d27757
|
4
|
+
data.tar.gz: 8e1583db5b2a5b85838e5c314587a58365b8a74851855dca887a32c2b910a236
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1866d8bd12d24ab570ab149d73e4ad166246f4160f80050de7ac4f2bf9d719befdf8cd04741593dbee5ac333f33a3b5ff4fa055d308747a490fd673137c21b4b
|
7
|
+
data.tar.gz: bc817eda194e723664ab321c0be218856ac700230d70c0be210fcec78e4e615b5038bdc270dd0c2d696500895638f4338d9ba3fa1a86c8f92173cf29abf0913d
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.0.5
|
4
|
+
|
5
|
+
- Upgrade to glimmer-dsl-libui v0.5.22
|
6
|
+
- Add/Remove currently playing radio station to bookmarks (remembered across app restarts by storing station UUID in a YAML file locally)
|
7
|
+
- View menu with "All", "Bookmarks", and "Playing" View menu items
|
8
|
+
- Command option `--[no-]bookmarks` to show/hide bookmarks in table
|
9
|
+
- Command option `--[no-]gradual` to load radio stations gradually (preventing user from waiting for app to start) or avoid loading gradually
|
10
|
+
|
3
11
|
## 0.0.4
|
4
12
|
|
5
13
|
- Upgrade to glimmer-dsl-libui v0.5.18
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -51,6 +51,12 @@ The top 10,000 [Radio Browser](https://www.radio-browser.info/) stations are dis
|
|
51
51
|
rubio --count 20000
|
52
52
|
```
|
53
53
|
|
54
|
+
The stations are fetched gradually (asynchronously) from the [Radio Browser](https://www.radio-browser.info/) web API to have the app start instantly (avoid having the user wait for the app to start) no matter what the total count of stations is. But, you can avoid gradual prefetching if you prefer.
|
55
|
+
|
56
|
+
```
|
57
|
+
rubio --no-gradual
|
58
|
+
```
|
59
|
+
|
54
60
|
Default player is `vlc -I dummy`. But, you can use any command line player that can take URL of radio station as its first argument.
|
55
61
|
|
56
62
|
```
|
@@ -70,10 +76,12 @@ Usage: rubio [options]
|
|
70
76
|
-b, --backend STR command to use as backend player ['vlc -I dummy']
|
71
77
|
-c, --count INT number of stations to fetch from radio-browser [10000]
|
72
78
|
--per-page INT number of stations per page [20]
|
73
|
-
--[no-]page-count show/hide page count
|
74
79
|
-w, --width INT main window width
|
75
80
|
-h, --height INT main window height
|
76
|
-
--[no-]
|
81
|
+
--[no-]page-count show/hide page count [false]
|
82
|
+
--[no-]menu show/hide menu [true]
|
83
|
+
--[no-]bookmarks show/hide bookmarks [true]
|
84
|
+
--[no-]gradual gradually/non-gradually fetch stations [true]
|
77
85
|
--debug output status of monitored threads
|
78
86
|
--help show this help message
|
79
87
|
--version show the rubio version number
|
@@ -87,10 +95,10 @@ rubio --mpg123 # `rubio --backend mpg123`
|
|
87
95
|
rubio --count 1000 # Displays the top 1,000 Radio Browser stations
|
88
96
|
```
|
89
97
|
|
90
|
-
|
98
|
+
Minimalistic Example:
|
91
99
|
|
92
100
|
```
|
93
|
-
rubio --per-page 6 --no-menu
|
101
|
+
rubio --per-page 6 --no-menu --no-bookmarks
|
94
102
|
```
|
95
103
|
|
96
104
|
![small screen linux screenshot](screenshots/rubio-radio-linux-example-small.png)
|
@@ -103,6 +111,14 @@ rubio --page-count
|
|
103
111
|
|
104
112
|
![page count mac screenshot](screenshots/rubio-radio-mac-example-page-count.png)
|
105
113
|
|
114
|
+
### Menus
|
115
|
+
|
116
|
+
You can use the top menu bar to stop the currently playing radio station, bookmark, unbookmark, view only bookmarked stations, view only currently playing station, and read the about dialog.
|
117
|
+
|
118
|
+
![view book marks mac screenshot](screenshots/rubio-radio-mac-example-view-bookmarks.png)
|
119
|
+
|
120
|
+
Bookmarks are stored in `File.join(Dir.home, '.rubio-radio', 'bookmarks.yml')`
|
121
|
+
|
106
122
|
## Links
|
107
123
|
|
108
124
|
* [Ruby](https://github.com/ruby/ruby)
|
data/TODO.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
1
|
# TODO
|
2
2
|
|
3
|
-
|
3
|
+
- Support OR-based FTS (Full-Text-Search) queries by treating multiple words as WORD1 OR WORD2, etc...
|
4
|
+
- Support exact FTS (Full-Text-Search) queries using double-quotes (e.g. "WORD1 WORD2")
|
5
|
+
- Support column-based queries by prefixing query term with column name + colon (e.g. "language:english"), which can be combined with FTS queries (e.g. "jazz language:english"). Also, there is no need to enter all of the column letters, yet only the first few letters that distinguish it from other columns (e.g. "jazz l:english")
|
6
|
+
|
7
|
+
- Make window margined and provide option to disable margins if preferred (`--no-margins`)
|
8
|
+
|
9
|
+
- Show current playing song (use `vlc -I dummy --extraintf http --http-host localhost --http-port 9877 --http-password pass1234` backend, and then make an HTTP request to http://localhost:9877/requests/status.xml in order to obtain song information and update it every second; maybe regenerate a new password on every run) (alternatively, I can use backend `vlc -I rc` and call `info` to obtain song details, parsing the text after `now_playing:` .... can use `io = IO.popen('vlc -I rc http://radio.canstream.co.uk:8075/live.mp3', 'r+')` with `io.puts('inf')` and `io.gets` until all input is done; also use `Timeout::timeout(0.5) {}` to avoid timing out on `gets` when there is no more input)
|
data/exe/rubio
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require 'puts_debuggerer' if ENV['PD'].to_s.downcase == 'true'
|
4
5
|
require 'rubio'
|
5
6
|
require 'optparse'
|
6
7
|
|
@@ -20,10 +21,14 @@ opt.on('-c', '--count INT', Numeric, 'number of stations to fetch from radio-bro
|
|
20
21
|
options[:radio_station_count] = c
|
21
22
|
end
|
22
23
|
opt.on('--per-page INT', Numeric, 'number of stations per page [20]') { |c| options[:table_per_page] = c }
|
23
|
-
opt.on('--[no-]page-count', TrueClass, 'show/hide page count') { |b| options[:show_page_count] = b }
|
24
24
|
opt.on('-w', '--width INT', Numeric, 'main window width') { |w| options[:initial_width] = w }
|
25
25
|
opt.on('-h', '--height INT', Numeric, 'main window height') { |h| options[:initial_height] = h }
|
26
|
-
opt.on('--[no-]
|
26
|
+
opt.on('--[no-]page-count', TrueClass, 'show/hide page count [false]') { |b| options[:show_page_count] = b }
|
27
|
+
opt.on('--[no-]menu', TrueClass, 'show/hide menu [true]') { |b| options[:show_menu] = b }
|
28
|
+
opt.on('--[no-]bookmarks', TrueClass, 'show/hide bookmarks [true]') { |b| options[:show_bookmarks] = b }
|
29
|
+
opt.on('--[no-]gradual', TrueClass, 'gradually/non-gradually fetch stations [true]') do |g|
|
30
|
+
options[:gradually_fetch_stations] = g
|
31
|
+
end
|
27
32
|
opt.on('--debug', 'output status of monitored threads') { options[:debug] = true }
|
28
33
|
opt.on('--help', 'show this help message') do
|
29
34
|
puts opt
|
@@ -43,8 +48,8 @@ rescue OptionParser::InvalidOption, OptionParser::AmbiguousOption => e
|
|
43
48
|
end
|
44
49
|
|
45
50
|
begin
|
46
|
-
Rubio::Radio.launch(**options)
|
51
|
+
Rubio::View::Radio.launch(**options)
|
47
52
|
rescue StandardError => e
|
48
|
-
Rubio::Radio.launched_application&.player&.stop_all
|
53
|
+
Rubio::View::Radio.launched_application&.player&.stop_all
|
49
54
|
raise e
|
50
55
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module Rubio
|
7
|
+
module Model
|
8
|
+
module Bookmark
|
9
|
+
DIR_RUBIO_RADIO = File.join(Dir.home, '.rubio-radio')
|
10
|
+
FileUtils.mkdir_p(DIR_RUBIO_RADIO)
|
11
|
+
FILE_RUBIO_RADIO_BOOKMARKS = File.join(DIR_RUBIO_RADIO, 'bookmarks.yml')
|
12
|
+
FileUtils.touch(FILE_RUBIO_RADIO_BOOKMARKS)
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def add(stationuuid)
|
16
|
+
unless all.include?(stationuuid)
|
17
|
+
all << stationuuid
|
18
|
+
save_all
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def remove(stationuuid)
|
23
|
+
if all.include?(stationuuid)
|
24
|
+
all.delete(stationuuid)
|
25
|
+
save_all
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def all
|
30
|
+
@all ||= load_all || []
|
31
|
+
end
|
32
|
+
|
33
|
+
def save_all
|
34
|
+
bookmarks_yaml = YAML.dump(all)
|
35
|
+
File.write(FILE_RUBIO_RADIO_BOOKMARKS, bookmarks_yaml)
|
36
|
+
rescue StandardError => e
|
37
|
+
puts 'Failed in saving bookmarks!'
|
38
|
+
puts all.inspect
|
39
|
+
puts e.full_message
|
40
|
+
# No Op
|
41
|
+
end
|
42
|
+
|
43
|
+
def load_all
|
44
|
+
bookmarks_yaml = File.read(FILE_RUBIO_RADIO_BOOKMARKS)
|
45
|
+
YAML.load(bookmarks_yaml)
|
46
|
+
rescue StandardError => e
|
47
|
+
puts 'Failed in loading bookmarks! Returning empty bookmarks.'
|
48
|
+
puts e.full_message
|
49
|
+
[]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rubio
|
4
|
+
module Model
|
5
|
+
class Player
|
6
|
+
attr_accessor :backend, :pid, :thr, :status, :history
|
7
|
+
|
8
|
+
def initialize(backend = OS.linux? ? 'cvlc' : 'vlc -I rc')
|
9
|
+
raise unless backend.is_a?(String)
|
10
|
+
|
11
|
+
@backend = backend
|
12
|
+
@pid = nil
|
13
|
+
@thr = nil
|
14
|
+
@status = []
|
15
|
+
@history = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def alive?
|
19
|
+
return false if @thr.nil?
|
20
|
+
|
21
|
+
@thr.alive?
|
22
|
+
end
|
23
|
+
|
24
|
+
def stop?
|
25
|
+
@thr.nil? || @thr.stop?
|
26
|
+
end
|
27
|
+
|
28
|
+
def play(url)
|
29
|
+
# Do not include spaces in the command line
|
30
|
+
# if a space exist :
|
31
|
+
# * sh -c command url # this process with @pid will be killed
|
32
|
+
# * cmmand url # will not be killd because pid is differennt
|
33
|
+
# if no space :
|
34
|
+
# * cmmand url # will be killed by @pid
|
35
|
+
raise if url.match(/\s/)
|
36
|
+
|
37
|
+
@pid = spawn(*backend.split(' '), url)
|
38
|
+
@thr = Process.detach(@pid)
|
39
|
+
@status = [@pid, @thr]
|
40
|
+
@history << @status
|
41
|
+
end
|
42
|
+
|
43
|
+
def stop
|
44
|
+
return unless alive?
|
45
|
+
|
46
|
+
r = Process.kill(OS.windows? ? :KILL : :TERM, pid)
|
47
|
+
@thr = nil
|
48
|
+
@pid = nil
|
49
|
+
r
|
50
|
+
end
|
51
|
+
|
52
|
+
def stop_all
|
53
|
+
@history.each do |pid, thr|
|
54
|
+
Process.kill(OS.windows? ? :KILL : :TERM, pid) if thr.alive?
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'open-uri'
|
5
|
+
|
6
|
+
require_relative 'station'
|
7
|
+
|
8
|
+
module Rubio
|
9
|
+
module Model
|
10
|
+
# https://www.radio-browser.info
|
11
|
+
module RadioBrowser
|
12
|
+
module_function
|
13
|
+
|
14
|
+
def base_url
|
15
|
+
'http://all.api.radio-browser.info/json/'
|
16
|
+
end
|
17
|
+
|
18
|
+
def topvote(n = 100, offset: 0)
|
19
|
+
content = URI.parse(base_url + "stations/topvote/#{n}?offset=#{offset}")
|
20
|
+
result = []
|
21
|
+
JSON[content.read].each_with_index do |s, _i|
|
22
|
+
result << Station.new(s['stationuuid'], s['name'], s['language'], s['url_resolved'])
|
23
|
+
end
|
24
|
+
result
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'bookmark'
|
4
|
+
|
5
|
+
module Rubio
|
6
|
+
module Model
|
7
|
+
Station = Struct.new(:stationuuid, :name, :language, :url, :play, :bookmark) do
|
8
|
+
attr_reader :playing, :bookmarked
|
9
|
+
alias_method :playing?, :playing
|
10
|
+
alias_method :bookmarked?, :bookmarked
|
11
|
+
|
12
|
+
def initialize(*args, **kwargs)
|
13
|
+
super(*args, **kwargs)
|
14
|
+
self.playing = false
|
15
|
+
self.bookmarked = Bookmark.all.include?(stationuuid)
|
16
|
+
end
|
17
|
+
|
18
|
+
def playing=(value)
|
19
|
+
self.play = value ? '■' : '▶'
|
20
|
+
@playing = value
|
21
|
+
end
|
22
|
+
|
23
|
+
def bookmarked=(value)
|
24
|
+
self.bookmark = value ? '★' : '☆'
|
25
|
+
@bookmarked = value
|
26
|
+
if @bookmarked
|
27
|
+
Bookmark.add(stationuuid)
|
28
|
+
else
|
29
|
+
Bookmark.remove(stationuuid)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/rubio/version.rb
CHANGED
@@ -0,0 +1,297 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'glimmer-dsl-libui'
|
4
|
+
|
5
|
+
require_relative '../model/radio_browser'
|
6
|
+
require_relative '../model/player'
|
7
|
+
|
8
|
+
module Rubio
|
9
|
+
module View
|
10
|
+
class Radio
|
11
|
+
include Glimmer::LibUI::Application
|
12
|
+
|
13
|
+
options :backend, :initial_width, :initial_height
|
14
|
+
option :radio_station_count, default: 10_000
|
15
|
+
option :debug, default: false
|
16
|
+
option :show_menu, default: true
|
17
|
+
option :show_page_count, default: false
|
18
|
+
option :show_bookmarks, default: true
|
19
|
+
option :gradually_fetch_stations, default: true
|
20
|
+
option :table_per_page, default: 20
|
21
|
+
|
22
|
+
attr_reader :stations, :player
|
23
|
+
attr_accessor :current_station, :view
|
24
|
+
|
25
|
+
before_body do
|
26
|
+
@loaded_station_count = [gradually_fetch_stations ? 100 : radio_station_count, radio_station_count].min
|
27
|
+
@loaded_station_offset = 0
|
28
|
+
@stations = Model::RadioBrowser.topvote(@loaded_station_count, offset: @loaded_station_offset)
|
29
|
+
@player = Model::Player.new(backend)
|
30
|
+
@initial_width = (initial_width || (show_bookmarks ? 740 : 620)).to_i
|
31
|
+
@initial_height = (initial_height || calculate_initial_height).to_i
|
32
|
+
@view = :all
|
33
|
+
end
|
34
|
+
|
35
|
+
after_body do
|
36
|
+
monitor_thread(debug)
|
37
|
+
async_fetch_stations if gradually_fetch_stations && @stations.count < radio_station_count
|
38
|
+
end
|
39
|
+
|
40
|
+
body do
|
41
|
+
radio_menu_bar
|
42
|
+
|
43
|
+
window('Rubio', @initial_width, @initial_height) do
|
44
|
+
vertical_box do
|
45
|
+
horizontal_box do
|
46
|
+
@station_table = refined_table(
|
47
|
+
table_columns: station_table_columns,
|
48
|
+
model_array: stations,
|
49
|
+
per_page: table_per_page.to_i,
|
50
|
+
visible_page_count: show_page_count
|
51
|
+
)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
on_closing do
|
56
|
+
@player.stop_all
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def radio_menu_bar
|
62
|
+
return unless OS.mac? || show_menu
|
63
|
+
|
64
|
+
radio_menu
|
65
|
+
view_menu
|
66
|
+
help_menu
|
67
|
+
end
|
68
|
+
|
69
|
+
def radio_menu
|
70
|
+
menu('Radio') do
|
71
|
+
menu_item('Stop') do
|
72
|
+
enabled <= [self, 'current_station', { on_read: ->(value) { !!value } }]
|
73
|
+
|
74
|
+
on_clicked do
|
75
|
+
stop_station
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
separator_menu_item
|
80
|
+
|
81
|
+
menu_item('Bookmark') do
|
82
|
+
enabled <= [self, 'current_station.bookmarked', { on_read: :! }]
|
83
|
+
|
84
|
+
on_clicked do
|
85
|
+
toggle_bookmarked_station(current_station) if current_station
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
menu_item('Unbookmark') do
|
90
|
+
enabled <= [self, 'current_station.bookmarked']
|
91
|
+
|
92
|
+
on_clicked do
|
93
|
+
toggle_bookmarked_station(current_station) if current_station
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
separator_menu_item
|
98
|
+
|
99
|
+
if OS.mac?
|
100
|
+
about_menu_item do
|
101
|
+
on_clicked do
|
102
|
+
about_message_box
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
quit_menu_item do
|
108
|
+
on_clicked do
|
109
|
+
@player.stop_all
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def view_menu
|
116
|
+
menu('View') do
|
117
|
+
radio_menu_item('All') do
|
118
|
+
checked <=> [self, :view,
|
119
|
+
{ on_read: ->(value) { value == :all },
|
120
|
+
on_write: ->(_value) { :all } }]
|
121
|
+
|
122
|
+
on_clicked do
|
123
|
+
view_all
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
radio_menu_item('Bookmarks') do
|
128
|
+
checked <=> [self, :view,
|
129
|
+
{ on_read: ->(value) { value == :bookmarks },
|
130
|
+
on_write: ->(_value) { :bookmarks } }]
|
131
|
+
|
132
|
+
on_clicked do
|
133
|
+
view_bookmarks
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
radio_menu_item('Playing') do
|
138
|
+
checked <=> [self, :view,
|
139
|
+
{ on_read: ->(value) { value == :playing },
|
140
|
+
on_write: ->(_value) { :playing } }]
|
141
|
+
|
142
|
+
on_clicked do
|
143
|
+
view_playing
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
separator_menu_item if OS.mac?
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def help_menu
|
152
|
+
menu('Help') do
|
153
|
+
menu_item('About') do
|
154
|
+
on_clicked do
|
155
|
+
about_message_box
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def station_table_columns
|
162
|
+
table_columns = {
|
163
|
+
'Play' => {
|
164
|
+
button: {
|
165
|
+
on_clicked: lambda { |row|
|
166
|
+
station = @station_table.refined_model_array[row]
|
167
|
+
select_station(station)
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
}
|
172
|
+
|
173
|
+
if show_bookmarks
|
174
|
+
table_columns.merge!(
|
175
|
+
'Bookmark' => {
|
176
|
+
button: {
|
177
|
+
on_clicked: lambda { |row|
|
178
|
+
station = @station_table.refined_model_array[row]
|
179
|
+
toggle_bookmarked_station(station)
|
180
|
+
}
|
181
|
+
}
|
182
|
+
}
|
183
|
+
)
|
184
|
+
end
|
185
|
+
|
186
|
+
table_columns.merge!(
|
187
|
+
'name' => :text,
|
188
|
+
'language' => :text
|
189
|
+
)
|
190
|
+
end
|
191
|
+
|
192
|
+
def about_message_box
|
193
|
+
license = begin
|
194
|
+
File.read(File.expand_path('../../../LICENSE.txt', __dir__))
|
195
|
+
rescue StandardError
|
196
|
+
''
|
197
|
+
end
|
198
|
+
product = "rubio-radio #{Rubio::VERSION}"
|
199
|
+
message_box(product, "#{product}\n\n#{license}")
|
200
|
+
end
|
201
|
+
|
202
|
+
def select_station(station)
|
203
|
+
playing = station.playing?
|
204
|
+
stop_station
|
205
|
+
self.current_station = station
|
206
|
+
if playing
|
207
|
+
self.current_station = nil
|
208
|
+
else
|
209
|
+
play_station
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def toggle_bookmarked_station(station)
|
214
|
+
station.bookmarked = !station.bookmarked?
|
215
|
+
view_bookmarks if view == :bookmarks && !station.bookmarked
|
216
|
+
end
|
217
|
+
|
218
|
+
def play_station
|
219
|
+
@player.play(current_station.url)
|
220
|
+
current_station.playing = true
|
221
|
+
rescue StandardError => e
|
222
|
+
message_box(e.message)
|
223
|
+
self.current_station = nil
|
224
|
+
end
|
225
|
+
|
226
|
+
def stop_station
|
227
|
+
return if current_station.nil?
|
228
|
+
|
229
|
+
@player.stop
|
230
|
+
current_station.playing = false
|
231
|
+
self.current_station = nil
|
232
|
+
end
|
233
|
+
|
234
|
+
def view_all
|
235
|
+
@station_table.model_array = stations
|
236
|
+
end
|
237
|
+
|
238
|
+
def view_bookmarks
|
239
|
+
@station_table.model_array = stations.select(&:bookmarked?)
|
240
|
+
end
|
241
|
+
|
242
|
+
def view_playing
|
243
|
+
@station_table.model_array = stations.select(&:playing?)
|
244
|
+
end
|
245
|
+
|
246
|
+
def refresh_view
|
247
|
+
case view
|
248
|
+
when :all
|
249
|
+
view_all
|
250
|
+
when :bookmarks
|
251
|
+
view_bookmarks
|
252
|
+
when :playing
|
253
|
+
view_playing
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
private
|
258
|
+
|
259
|
+
def calculate_initial_height
|
260
|
+
if OS.linux?
|
261
|
+
107 + (show_menu ? 26 : 0) + 24 * table_per_page.to_i
|
262
|
+
elsif OS.mac? && OS.host_cpu == 'arm64'
|
263
|
+
90 + 24 * table_per_page.to_i
|
264
|
+
elsif OS.mac?
|
265
|
+
85 + 19 * table_per_page.to_i
|
266
|
+
else # Windows
|
267
|
+
95 + 19 * table_per_page.to_i
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def monitor_thread(debug)
|
272
|
+
Glimmer::LibUI.timer(1) do
|
273
|
+
p @player.history if debug
|
274
|
+
next if current_station.nil? || @player.alive?
|
275
|
+
|
276
|
+
message_box("player '#{@player.backend}' stopped!", @player.thr.to_s)
|
277
|
+
stop_station
|
278
|
+
true
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def async_fetch_stations
|
283
|
+
@loaded_station_offset += @loaded_station_count
|
284
|
+
@loaded_station_count *= 2
|
285
|
+
Thread.new do
|
286
|
+
new_station_count = [@loaded_station_count, radio_station_count - @loaded_station_offset].min
|
287
|
+
@stations += Model::RadioBrowser.topvote(new_station_count, offset: @loaded_station_offset)
|
288
|
+
|
289
|
+
Glimmer::LibUI.queue_main do
|
290
|
+
refresh_view
|
291
|
+
async_fetch_stations if @stations.count < radio_station_count
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
data/lib/rubio.rb
CHANGED
@@ -1,12 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'glimmer-dsl-libui'
|
4
|
-
|
5
3
|
require_relative 'rubio/version'
|
6
|
-
require_relative 'rubio/
|
7
|
-
require_relative 'rubio/radio_browser'
|
8
|
-
require_relative 'rubio/player'
|
9
|
-
require_relative 'rubio/radio'
|
4
|
+
require_relative 'rubio/view/radio'
|
10
5
|
|
11
6
|
module Rubio
|
12
7
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubio-radio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- kojix2
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2022-08-
|
12
|
+
date: 2022-08-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: glimmer-dsl-libui
|
@@ -17,14 +17,14 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - '='
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: 0.5.
|
20
|
+
version: 0.5.22
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - '='
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: 0.5.
|
27
|
+
version: 0.5.22
|
28
28
|
description: Rubio is a simple radio player written in Ruby.
|
29
29
|
email:
|
30
30
|
- 2xijok@gmail.com
|
@@ -39,11 +39,12 @@ files:
|
|
39
39
|
- TODO.md
|
40
40
|
- exe/rubio
|
41
41
|
- lib/rubio.rb
|
42
|
-
- lib/rubio/
|
43
|
-
- lib/rubio/
|
44
|
-
- lib/rubio/radio_browser.rb
|
45
|
-
- lib/rubio/station.rb
|
42
|
+
- lib/rubio/model/bookmark.rb
|
43
|
+
- lib/rubio/model/player.rb
|
44
|
+
- lib/rubio/model/radio_browser.rb
|
45
|
+
- lib/rubio/model/station.rb
|
46
46
|
- lib/rubio/version.rb
|
47
|
+
- lib/rubio/view/radio.rb
|
47
48
|
homepage: https://github.com/kojix2/rubio
|
48
49
|
licenses:
|
49
50
|
- MIT
|
data/lib/rubio/player.rb
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Rubio
|
4
|
-
class Player
|
5
|
-
attr_accessor :backend, :pid, :thr, :status, :history
|
6
|
-
|
7
|
-
def initialize(backend = OS.linux? ? 'cvlc' : 'vlc -I rc')
|
8
|
-
raise unless backend.is_a?(String)
|
9
|
-
|
10
|
-
@backend = backend
|
11
|
-
@pid = nil
|
12
|
-
@thr = nil
|
13
|
-
@status = []
|
14
|
-
@history = []
|
15
|
-
end
|
16
|
-
|
17
|
-
def alive?
|
18
|
-
return false if @thr.nil?
|
19
|
-
|
20
|
-
@thr.alive?
|
21
|
-
end
|
22
|
-
|
23
|
-
def stop?
|
24
|
-
@thr.nil? || @thr.stop?
|
25
|
-
end
|
26
|
-
|
27
|
-
def play(url)
|
28
|
-
# Do not include spaces in the command line
|
29
|
-
# if a space exist :
|
30
|
-
# * sh -c command url # this process with @pid will be killed
|
31
|
-
# * cmmand url # will not be killd because pid is differennt
|
32
|
-
# if no space :
|
33
|
-
# * cmmand url # will be killed by @pid
|
34
|
-
raise if url.match(/\s/)
|
35
|
-
|
36
|
-
@pid = spawn(*backend.split(' '), url)
|
37
|
-
@thr = Process.detach(@pid)
|
38
|
-
@status = [@pid, @thr]
|
39
|
-
@history << @status
|
40
|
-
end
|
41
|
-
|
42
|
-
def stop
|
43
|
-
return unless alive?
|
44
|
-
|
45
|
-
r = Process.kill(OS.windows? ? :KILL : :TERM, pid)
|
46
|
-
@thr = nil
|
47
|
-
@pid = nil
|
48
|
-
r
|
49
|
-
end
|
50
|
-
|
51
|
-
def stop_all
|
52
|
-
@history.each do |pid, thr|
|
53
|
-
Process.kill(OS.windows? ? :KILL : :TERM, pid) if thr.alive?
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
data/lib/rubio/radio.rb
DELETED
@@ -1,164 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'glimmer-dsl-libui'
|
4
|
-
|
5
|
-
module Rubio
|
6
|
-
class Radio
|
7
|
-
include Glimmer::LibUI::Application
|
8
|
-
|
9
|
-
options :backend, :initial_width, :initial_height
|
10
|
-
option :radio_station_count, default: 10_000
|
11
|
-
option :debug, default: false
|
12
|
-
option :visible_menu, default: true
|
13
|
-
option :show_page_count, default: false
|
14
|
-
option :table_per_page, default: 20
|
15
|
-
|
16
|
-
attr_reader :stations, :player
|
17
|
-
|
18
|
-
before_body do
|
19
|
-
@stations_all, @table = RadioBrowser.topvote(radio_station_count)
|
20
|
-
@stations = @stations_all.dup
|
21
|
-
@station_uuid = nil
|
22
|
-
@player = Player.new(backend)
|
23
|
-
@initial_width = (initial_width || 400).to_i
|
24
|
-
@initial_height = (initial_height || calculate_initial_height).to_i
|
25
|
-
|
26
|
-
monitor_thread(debug)
|
27
|
-
end
|
28
|
-
|
29
|
-
body do
|
30
|
-
radio_menu_bar
|
31
|
-
|
32
|
-
window('Rubio', @initial_width, @initial_height) do
|
33
|
-
vertical_box do
|
34
|
-
horizontal_box do
|
35
|
-
@station_table = refined_table(
|
36
|
-
table_columns: {
|
37
|
-
'Play' => { button: {
|
38
|
-
on_clicked: lambda { |row|
|
39
|
-
station = @station_table.paginated_model_array[row]
|
40
|
-
select_station(station)
|
41
|
-
}
|
42
|
-
} },
|
43
|
-
'name' => :text,
|
44
|
-
'language' => :text
|
45
|
-
},
|
46
|
-
model_array: stations,
|
47
|
-
per_page: table_per_page.to_i,
|
48
|
-
visible_page_count: show_page_count
|
49
|
-
)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
on_closing do
|
54
|
-
@player.stop_all
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def radio_menu_bar
|
60
|
-
return unless OS.mac? || visible_menu
|
61
|
-
|
62
|
-
menu('Radio') do
|
63
|
-
menu_item('Stop') do
|
64
|
-
on_clicked do
|
65
|
-
stop_uuid(@station_uuid)
|
66
|
-
@station_uuid = nil
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
if OS.mac?
|
71
|
-
about_menu_item do
|
72
|
-
on_clicked do
|
73
|
-
about_message_box
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
quit_menu_item do
|
79
|
-
on_clicked do
|
80
|
-
@player.stop_all
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
menu('Help') do
|
86
|
-
menu_item('About') do
|
87
|
-
on_clicked do
|
88
|
-
about_message_box
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def about_message_box
|
95
|
-
license = begin
|
96
|
-
File.read(File.expand_path('../../LICENSE.txt', __dir__))
|
97
|
-
rescue StandardError
|
98
|
-
''
|
99
|
-
end
|
100
|
-
product = "rubio-radio #{Rubio::VERSION}"
|
101
|
-
message_box(product, "#{product}\n\n#{license}")
|
102
|
-
end
|
103
|
-
|
104
|
-
def select_station(station)
|
105
|
-
station_uuid = station.stationuuid
|
106
|
-
stop_uuid(@station_uuid)
|
107
|
-
if @station_uuid == station_uuid
|
108
|
-
@station_uuid = nil
|
109
|
-
elsif play_uuid(station_uuid)
|
110
|
-
@station_uuid = station_uuid
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
def play_uuid(station_uuid)
|
115
|
-
station = uuid_to_station(station_uuid)
|
116
|
-
begin
|
117
|
-
@player.play(station.url)
|
118
|
-
rescue StandardError => e
|
119
|
-
message_box(e.message)
|
120
|
-
return false
|
121
|
-
end
|
122
|
-
station.playing = true
|
123
|
-
end
|
124
|
-
|
125
|
-
def stop_uuid(station_uuid)
|
126
|
-
return if station_uuid.nil?
|
127
|
-
|
128
|
-
station = uuid_to_station(station_uuid)
|
129
|
-
@player.stop
|
130
|
-
station.playing = false
|
131
|
-
end
|
132
|
-
|
133
|
-
private
|
134
|
-
|
135
|
-
def calculate_initial_height
|
136
|
-
if OS.linux?
|
137
|
-
107 + (visible_menu ? 26 : 0) + 24 * table_per_page.to_i
|
138
|
-
elsif OS.mac? && OS.host_cpu == 'arm64'
|
139
|
-
90 + 24 * table_per_page.to_i
|
140
|
-
elsif OS.mac?
|
141
|
-
85 + 19 * table_per_page.to_i
|
142
|
-
else # Windows
|
143
|
-
95 + 19 * table_per_page.to_i
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
def monitor_thread(debug)
|
148
|
-
Glimmer::LibUI.timer(1) do
|
149
|
-
p @player.history if debug
|
150
|
-
next if @station_uuid.nil? || @player.alive?
|
151
|
-
|
152
|
-
message_box("player '#{@player.backend}' stopped!", @player.thr.to_s)
|
153
|
-
stop_uuid(@station_uuid)
|
154
|
-
@station_uuid = nil
|
155
|
-
true
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
def uuid_to_station(uuid)
|
160
|
-
idx = @table[uuid]
|
161
|
-
@stations_all[idx]
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
data/lib/rubio/radio_browser.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'json'
|
4
|
-
require 'open-uri'
|
5
|
-
|
6
|
-
module Rubio
|
7
|
-
# https://www.radio-browser.info
|
8
|
-
module RadioBrowser
|
9
|
-
module_function
|
10
|
-
|
11
|
-
def base_url
|
12
|
-
'http://all.api.radio-browser.info/json/'
|
13
|
-
end
|
14
|
-
|
15
|
-
def topvote(n = 100)
|
16
|
-
content = URI.parse(base_url + "stations/topvote/#{n}")
|
17
|
-
table = {} # uuid => index
|
18
|
-
result = []
|
19
|
-
JSON[content.read].each_with_index do |s, i|
|
20
|
-
table[s['stationuuid']] = i
|
21
|
-
result << Station.new(s['stationuuid'], s['name'], s['language'], s['url_resolved'])
|
22
|
-
end
|
23
|
-
[result, table]
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
data/lib/rubio/station.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Rubio
|
4
|
-
Station = Struct.new(:stationuuid, :name, :language, :url, :play) do
|
5
|
-
attr_reader :playing
|
6
|
-
|
7
|
-
def initialize(*args, **kwargs)
|
8
|
-
super(*args, **kwargs)
|
9
|
-
self.playing = false
|
10
|
-
end
|
11
|
-
|
12
|
-
def playing=(value)
|
13
|
-
self.play = value ? '■' : '▶'
|
14
|
-
@playing = value
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|