budik 1.0.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/.coveralls.yml +1 -0
- data/.gitignore +10 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +176 -0
- data/Rakefile +23 -0
- data/bin/budik +4 -0
- data/bin/console +14 -0
- data/bin/setup +10 -0
- data/budik.gemspec +55 -0
- data/config/templates/lang/en.yml +37 -0
- data/config/templates/options/linux.yml +39 -0
- data/config/templates/options/rpi.yml +39 -0
- data/config/templates/options/windows.yml +39 -0
- data/config/templates/sources/sources.yml +34 -0
- data/lib/budik.rb +152 -0
- data/lib/budik/command.rb +199 -0
- data/lib/budik/config.rb +149 -0
- data/lib/budik/devices.rb +146 -0
- data/lib/budik/io.rb +58 -0
- data/lib/budik/player.rb +174 -0
- data/lib/budik/rng.rb +154 -0
- data/lib/budik/sources.rb +182 -0
- data/lib/budik/storage.rb +91 -0
- data/lib/budik/version.rb +15 -0
- metadata +288 -0
data/lib/budik/rng.rb
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
# = rng.rb
|
2
|
+
# This file contains methods for random number generation.
|
3
|
+
#
|
4
|
+
# == Contact
|
5
|
+
#
|
6
|
+
# Author:: Petr Schmied (mailto:jblack@paworld.eu)
|
7
|
+
# Website:: http://www.paworld.eu
|
8
|
+
# Date:: September 20, 2015
|
9
|
+
|
10
|
+
module Budik
|
11
|
+
# 'Rng' class provides various methods for random number generation.
|
12
|
+
class Rng
|
13
|
+
# Loads RNG options including method.
|
14
|
+
def initialize
|
15
|
+
@options = Config.instance.options['rng']
|
16
|
+
@method = @options['method']
|
17
|
+
end
|
18
|
+
|
19
|
+
# Gets RNG options and method.
|
20
|
+
attr_accessor :options, :method
|
21
|
+
|
22
|
+
# Generates random number.
|
23
|
+
#
|
24
|
+
# - *Args*:
|
25
|
+
# - +items+ -> Total number of items (Fixnum).
|
26
|
+
# - *Returns*:
|
27
|
+
# - Fixnum (0...items)
|
28
|
+
#
|
29
|
+
def generate(items)
|
30
|
+
case @method
|
31
|
+
when 'hwrng'
|
32
|
+
hwrng(@options['hwrng'], items)
|
33
|
+
when 'random.org'
|
34
|
+
random_org(@options['random.org'], items)
|
35
|
+
when 'rand-hwrng-seed'
|
36
|
+
swrng(items, hwrng(@options['hwrng'], 2**64))
|
37
|
+
else
|
38
|
+
swrng(items)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# Reads random number from hwrng (/dev/hwrng, /dev/(u)random).
|
45
|
+
# Removes modulo bias.
|
46
|
+
# http://funloop.org/post/2015-02-27-removing-modulo-bias-redux.html
|
47
|
+
# Falls back to swrng if an exception is encountered.
|
48
|
+
#
|
49
|
+
# - *Args*:
|
50
|
+
# - +options+ -> Hwrng options (Hash).
|
51
|
+
# - +items+ -> Total number of items (Fixnum).
|
52
|
+
# - *Returns*:
|
53
|
+
# - Fixnum (0...items)
|
54
|
+
#
|
55
|
+
def hwrng(options, items)
|
56
|
+
source = File.new(options['source'], 'r')
|
57
|
+
max = 2**64
|
58
|
+
bound = items - 1
|
59
|
+
threshold = (max - bound) % bound
|
60
|
+
number = source.read(8).unpack('Q') while number.first < threshold
|
61
|
+
number.first % bound
|
62
|
+
rescue
|
63
|
+
swrng(items)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Queries Random.org API to obtain random number.
|
67
|
+
# https://api.random.org/json-rpc/1/
|
68
|
+
# Falls back to swrng if incorrect response is received.
|
69
|
+
#
|
70
|
+
# - *Args*:
|
71
|
+
# - +options+ -> Random.org options (Hash).
|
72
|
+
# - +items+ -> Total number of items (Fixnum).
|
73
|
+
# - *Returns*:
|
74
|
+
# - Fixnum (0...items)
|
75
|
+
#
|
76
|
+
def random_org(options, items)
|
77
|
+
response = random_org_request(options, items)
|
78
|
+
return response if response.is_a? Fixnum
|
79
|
+
|
80
|
+
if response.code.to_i == 200
|
81
|
+
JSON.parse(response.body)['result']['random']['data'].first
|
82
|
+
else
|
83
|
+
swrng(items)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Generates Random.org API request data.
|
88
|
+
#
|
89
|
+
# - *Args*:
|
90
|
+
# - +apikey+ -> Random.org API key (String).
|
91
|
+
# - +items+ -> Total number of items (Fixnum).
|
92
|
+
# - *Returns*:
|
93
|
+
# - Random.org API request data (Hash).
|
94
|
+
#
|
95
|
+
def random_org_request_data(apikey, items)
|
96
|
+
{ jsonrpc: '2.0',
|
97
|
+
method: 'generateIntegers',
|
98
|
+
params: {
|
99
|
+
apiKey: apikey,
|
100
|
+
n: 1,
|
101
|
+
min: 0,
|
102
|
+
max: items - 1
|
103
|
+
},
|
104
|
+
id: 29 }
|
105
|
+
end
|
106
|
+
|
107
|
+
# Builds a request and sends it to Random.org API.
|
108
|
+
#
|
109
|
+
# - *Args*:
|
110
|
+
# - +options+ -> Random.org options (Hash).
|
111
|
+
# - +items+ -> Total number of items (Fixnum).
|
112
|
+
# - *Returns*:
|
113
|
+
# - Random.org API response object or Fixnum (0...items).
|
114
|
+
#
|
115
|
+
def random_org_request(options, items)
|
116
|
+
uri = URI.parse('http://api.random.org/json-rpc/1/invoke')
|
117
|
+
header = { 'Content-Type' => 'application/json-rpc' }
|
118
|
+
data = random_org_request_data(options['apikey'], items)
|
119
|
+
|
120
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
121
|
+
request = Net::HTTP::Post.new(uri.request_uri, header)
|
122
|
+
request.body = data.to_json
|
123
|
+
|
124
|
+
random_org_request_send(http, request)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Sends a request to Random.org API.
|
128
|
+
#
|
129
|
+
# - *Args*:
|
130
|
+
# - +http+ -> Net::HTTP object.
|
131
|
+
# - +request+ -> Net::HTTP::Post object.
|
132
|
+
# - *Returns*:
|
133
|
+
# - HTTPResponse object or Fixnum (0...items).
|
134
|
+
#
|
135
|
+
def random_org_request_send(http, request)
|
136
|
+
http.request(request)
|
137
|
+
rescue
|
138
|
+
swrng(items)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Generates random number using (s)rand.
|
142
|
+
#
|
143
|
+
# - *Args*:
|
144
|
+
# - +items+ -> Total number of items (Fixnum).
|
145
|
+
# - +seed+ -> Custom seed for rand.
|
146
|
+
# - *Returns*:
|
147
|
+
# - Fixnum (0...items).
|
148
|
+
#
|
149
|
+
def swrng(items, seed = nil)
|
150
|
+
seed.nil? ? srand : srand(seed) # TODO: Test this
|
151
|
+
rand(0...items)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
# = sources.rb
|
2
|
+
# This file contains methods for parsing sources and category modifiers.
|
3
|
+
#
|
4
|
+
# == Contact
|
5
|
+
#
|
6
|
+
# Author:: Petr Schmied (mailto:jblack@paworld.eu)
|
7
|
+
# Website:: http://www.paworld.eu
|
8
|
+
# Date:: September 20, 2015
|
9
|
+
|
10
|
+
module Budik
|
11
|
+
# 'Sources' class loads and parses media sources file.
|
12
|
+
class Sources
|
13
|
+
include Singleton
|
14
|
+
|
15
|
+
# Initializes sources instance variable and strings in currently set
|
16
|
+
# language.
|
17
|
+
def initialize
|
18
|
+
@sources = []
|
19
|
+
@strings = Config.instance.lang.sources
|
20
|
+
end
|
21
|
+
|
22
|
+
# Gets sources
|
23
|
+
attr_accessor :sources
|
24
|
+
|
25
|
+
# Applies category modifiers.
|
26
|
+
#
|
27
|
+
# - *Args*:
|
28
|
+
# - +mods+ -> Category modifiers (Hash).
|
29
|
+
#
|
30
|
+
def apply_mods(mods)
|
31
|
+
@sources.keep_if do |source|
|
32
|
+
mods[:adds].any? { |mod| apply_mods_check(source[:category], mod) }
|
33
|
+
end
|
34
|
+
|
35
|
+
@sources.delete_if do |source|
|
36
|
+
mods[:rms].any? { |mod| apply_mods_check(source[:category], mod) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Checks if mod applies to category.
|
41
|
+
#
|
42
|
+
# - *Args*:
|
43
|
+
# - +category+ -> Category to be checked (Array).
|
44
|
+
# - +mod+ -> Modifier to be checked (Array).
|
45
|
+
# - *Returns*:
|
46
|
+
# - true or false
|
47
|
+
#
|
48
|
+
def apply_mods_check(category, mod)
|
49
|
+
mod_len = mod.length - 1
|
50
|
+
cat_len = category.length - 1
|
51
|
+
len = mod_len <= cat_len ? mod_len : cat_len
|
52
|
+
|
53
|
+
map = category[0..len].zip(mod[0..len]).map { |c, m| c == m }
|
54
|
+
!map.include? false
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns total count of sources
|
58
|
+
def count
|
59
|
+
@sources.length
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns source by number.
|
63
|
+
#
|
64
|
+
# - *Args*:
|
65
|
+
# - +number+ -> Fixnum.
|
66
|
+
#
|
67
|
+
def get(number)
|
68
|
+
@sources[number]
|
69
|
+
end
|
70
|
+
|
71
|
+
# Normalizes item.
|
72
|
+
#
|
73
|
+
# - *Args*:
|
74
|
+
# - +item+ -> Item to normalize (Array, Hash or String).
|
75
|
+
# - +category+ -> Item's category (Array).
|
76
|
+
# - *Returns*:
|
77
|
+
# - Normalized source (Hash).
|
78
|
+
# - *Raises*:
|
79
|
+
# - +RuntimeError+ -> If item is not Array, Hash or String.
|
80
|
+
#
|
81
|
+
def normalize(source, category)
|
82
|
+
case source
|
83
|
+
when Array
|
84
|
+
normalize_multiple_items(source, category)
|
85
|
+
when Hash
|
86
|
+
normalize_named_source(source, category)
|
87
|
+
when String
|
88
|
+
normalize_unnamed_source(source, category)
|
89
|
+
else
|
90
|
+
fail @strings.invalid_format
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Normalizes unnamed source with multiple items.
|
95
|
+
#
|
96
|
+
# - *Args*:
|
97
|
+
# - +source+ -> Source to normalize (Array).
|
98
|
+
# - +category+ -> Source's category (Array).
|
99
|
+
# - *Returns*:
|
100
|
+
# - Normalized source (Hash).
|
101
|
+
#
|
102
|
+
def normalize_multiple_items(source, category)
|
103
|
+
{ name: source.join(' + '), category: category, path: source }
|
104
|
+
end
|
105
|
+
|
106
|
+
# Normalizes named source.
|
107
|
+
#
|
108
|
+
# - *Args*:
|
109
|
+
# - +source+ -> Source to normalize (Hash).
|
110
|
+
# - +category+ -> Source's category (Array).
|
111
|
+
# - *Returns*:
|
112
|
+
# - Normalized source (Hash).
|
113
|
+
#
|
114
|
+
def normalize_named_source(source, category)
|
115
|
+
{ name: source.keys[0], category: category, path: source.values[0] }
|
116
|
+
end
|
117
|
+
|
118
|
+
# Normalizes unnamed source with single item.
|
119
|
+
#
|
120
|
+
# - *Args*:
|
121
|
+
# - +source+ -> Source to normalize (String).
|
122
|
+
# - +category+ -> Source's category (Array).
|
123
|
+
# - *Returns*:
|
124
|
+
# - Normalized source (Hash).
|
125
|
+
#
|
126
|
+
def normalize_unnamed_source(source, category)
|
127
|
+
{ name: source, category: category, path: [] << source }
|
128
|
+
end
|
129
|
+
|
130
|
+
# Parses sources' categories.
|
131
|
+
#
|
132
|
+
# - *Args*:
|
133
|
+
# - +sources+ -> Sources loaded from YAML (Hash).
|
134
|
+
# - +current_category+ -> Source's category (Array).
|
135
|
+
# - *Raises*:
|
136
|
+
# - +RuntimeError+ -> If category's contents is not Array nor Hash.
|
137
|
+
#
|
138
|
+
def parse(sources, current_category = [])
|
139
|
+
sources.each do |category, contents|
|
140
|
+
case contents
|
141
|
+
when Hash
|
142
|
+
parse(contents, current_category + ([] << category))
|
143
|
+
when Array
|
144
|
+
parse_category(contents, current_category + ([] << category))
|
145
|
+
else
|
146
|
+
fail @strings.invalid_format
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Parses category contents.
|
152
|
+
#
|
153
|
+
# - *Args*:
|
154
|
+
# - +contents+ -> Category's contents (Array).
|
155
|
+
# - +category+ -> Source's category (Array).
|
156
|
+
#
|
157
|
+
def parse_category(contents, category)
|
158
|
+
contents.each { |source| @sources << normalize(source, category) }
|
159
|
+
end
|
160
|
+
|
161
|
+
# Parses string of category modifiers into two arrays (adds, rms).
|
162
|
+
#
|
163
|
+
# - *Args*:
|
164
|
+
# - +mods+ -> Category modifiers (String).
|
165
|
+
# - *Returns*:
|
166
|
+
# - Parsed modifiers (Hash).
|
167
|
+
#
|
168
|
+
def parse_mods(mods)
|
169
|
+
parsed_mods = { adds: [], rms: [] }
|
170
|
+
|
171
|
+
mods.split(' ').each do |mod|
|
172
|
+
if mod.split('.').first.empty?
|
173
|
+
parsed_mods[:rms] << mod.split('.').drop(1)
|
174
|
+
else
|
175
|
+
parsed_mods[:adds] << mod.split('.')
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
parsed_mods
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# = storage.rb
|
2
|
+
# This file contains methods for managing downloaded sources.
|
3
|
+
#
|
4
|
+
# == Contact
|
5
|
+
#
|
6
|
+
# Author:: Petr Schmied (mailto:jblack@paworld.eu)
|
7
|
+
# Website:: http://www.paworld.eu
|
8
|
+
# Date:: September 20, 2015
|
9
|
+
|
10
|
+
module Budik
|
11
|
+
# 'Storage' class downloads and manages media sources/items.
|
12
|
+
class Storage
|
13
|
+
include Singleton
|
14
|
+
|
15
|
+
# Loads sources, download directory and download method.
|
16
|
+
def initialize
|
17
|
+
@sources = Sources.instance.sources
|
18
|
+
dir = Config.instance.options['sources']['download']['dir']
|
19
|
+
@dir = File.expand_path(dir) + '/'
|
20
|
+
@method = Config.instance.options['sources']['download']['method']
|
21
|
+
end
|
22
|
+
|
23
|
+
# Gets sources, download directory and download method.
|
24
|
+
attr_accessor :sources, :dir, :method
|
25
|
+
|
26
|
+
# Downloads specified source or all sources.
|
27
|
+
#
|
28
|
+
# - *Args*:
|
29
|
+
# - +source+ -> Source to download (Hash).
|
30
|
+
#
|
31
|
+
def download_sources(source = nil)
|
32
|
+
if source
|
33
|
+
IO.instance.storage_download_info(source)
|
34
|
+
source[:path].each do |path|
|
35
|
+
download_youtube(YouTubeAddy.extract_video_id(path), path)
|
36
|
+
end
|
37
|
+
else
|
38
|
+
@sources.each { |src| download_sources(src) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Downloads video from YouTube by ID with specified options.
|
43
|
+
#
|
44
|
+
# - *Args*:
|
45
|
+
# - +id+ -> YouTube video ID (String).
|
46
|
+
# - +address+ -> YouTube video address (String).
|
47
|
+
#
|
48
|
+
def download_youtube(id, address)
|
49
|
+
return unless id && !File.file?(@dir + id + '.mp4')
|
50
|
+
|
51
|
+
# TODO: Update youtube-dl if fail
|
52
|
+
# TODO: username + password
|
53
|
+
options = { output: @dir + '%(id)s.%(ext)s',
|
54
|
+
format: 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4',
|
55
|
+
playlist: false }
|
56
|
+
YoutubeDL.download '"' + address + '"', options
|
57
|
+
end
|
58
|
+
|
59
|
+
# Gets downloaded item's location.
|
60
|
+
#
|
61
|
+
# - *Args*:
|
62
|
+
# - +item+ -> Item to locate (String).
|
63
|
+
# - *Returns*:
|
64
|
+
# - Path to downloaded file.
|
65
|
+
# - Unaltered path if streaming or if the item is local.
|
66
|
+
#
|
67
|
+
def locate_item(item)
|
68
|
+
return item if @method == 'stream'
|
69
|
+
is_url = (item =~ /\A#{URI.regexp(%w(http https))}\z/)
|
70
|
+
is_url ? @dir + YouTubeAddy.extract_video_id(item) + '.mp4' : item
|
71
|
+
end
|
72
|
+
|
73
|
+
# Removes specified source or all sources.
|
74
|
+
#
|
75
|
+
# - *Args*:
|
76
|
+
# - +source+ -> Source to remove (Hash).
|
77
|
+
#
|
78
|
+
def remove_sources(source = nil)
|
79
|
+
return unless @method == 'remove'
|
80
|
+
|
81
|
+
if source
|
82
|
+
source[:path].each do |path|
|
83
|
+
next if locate_item(path) == path
|
84
|
+
FileUtils.rm File.expand_path(locate_item(path)), force: true
|
85
|
+
end
|
86
|
+
else
|
87
|
+
@sources.each { |src| remove_sources(src) }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# = version.rb
|
2
|
+
# This file defines application's version.
|
3
|
+
#
|
4
|
+
# == Contact
|
5
|
+
#
|
6
|
+
# Author:: Petr Schmied (mailto:jblack@paworld.eu)
|
7
|
+
# Website:: http://www.paworld.eu
|
8
|
+
# Date:: September 20, 2015
|
9
|
+
|
10
|
+
# 'Budik' is an alarm clock that randomly plays an item from your media
|
11
|
+
# collection (local or YouTube).
|
12
|
+
module Budik
|
13
|
+
# Application's version
|
14
|
+
VERSION = '1.0.0'
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,288 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: budik
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Petr Schmied
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-09-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: colorize
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: commander
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: r18n-core
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sys-uname
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: terminal-table
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: ya2yaml
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: youtube_addy
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: youtube-dl.rb
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: bundler
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '1.10'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '1.10'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rake
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '10.0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '10.0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rspec
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: cucumber
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: rubocop
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: coveralls
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - ">="
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
type: :development
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - ">="
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '0'
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: rdoc
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - ">="
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '0'
|
216
|
+
type: :development
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - ">="
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
223
|
+
description: Alarm clock that randomly plays a song or a video from YouTube or your
|
224
|
+
local collection.
|
225
|
+
email:
|
226
|
+
- jblack@paworld.eu
|
227
|
+
executables:
|
228
|
+
- budik
|
229
|
+
extensions: []
|
230
|
+
extra_rdoc_files: []
|
231
|
+
files:
|
232
|
+
- ".coveralls.yml"
|
233
|
+
- ".gitignore"
|
234
|
+
- ".rspec"
|
235
|
+
- ".travis.yml"
|
236
|
+
- CODE_OF_CONDUCT.md
|
237
|
+
- Gemfile
|
238
|
+
- LICENSE.txt
|
239
|
+
- README.md
|
240
|
+
- Rakefile
|
241
|
+
- bin/budik
|
242
|
+
- bin/console
|
243
|
+
- bin/setup
|
244
|
+
- budik.gemspec
|
245
|
+
- config/templates/lang/en.yml
|
246
|
+
- config/templates/options/linux.yml
|
247
|
+
- config/templates/options/rpi.yml
|
248
|
+
- config/templates/options/windows.yml
|
249
|
+
- config/templates/sources/sources.yml
|
250
|
+
- lib/budik.rb
|
251
|
+
- lib/budik/command.rb
|
252
|
+
- lib/budik/config.rb
|
253
|
+
- lib/budik/devices.rb
|
254
|
+
- lib/budik/io.rb
|
255
|
+
- lib/budik/player.rb
|
256
|
+
- lib/budik/rng.rb
|
257
|
+
- lib/budik/sources.rb
|
258
|
+
- lib/budik/storage.rb
|
259
|
+
- lib/budik/version.rb
|
260
|
+
homepage: http://jblack.paworld.eu/apps/budik
|
261
|
+
licenses:
|
262
|
+
- MIT
|
263
|
+
metadata:
|
264
|
+
allowed_push_host: https://rubygems.org
|
265
|
+
post_install_message: |-
|
266
|
+
Please make sure VLC/omxplayer and FFmpeg/Libav are installed.
|
267
|
+
Run 'budik(.bat) config' to edit app's options as needed.
|
268
|
+
Run 'budik(.bat) sources -e' to edit your media sources.
|
269
|
+
rdoc_options: []
|
270
|
+
require_paths:
|
271
|
+
- lib
|
272
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
273
|
+
requirements:
|
274
|
+
- - ">="
|
275
|
+
- !ruby/object:Gem::Version
|
276
|
+
version: '0'
|
277
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
278
|
+
requirements:
|
279
|
+
- - ">="
|
280
|
+
- !ruby/object:Gem::Version
|
281
|
+
version: '0'
|
282
|
+
requirements: []
|
283
|
+
rubyforge_project:
|
284
|
+
rubygems_version: 2.4.8
|
285
|
+
signing_key:
|
286
|
+
specification_version: 4
|
287
|
+
summary: Alarm clock.
|
288
|
+
test_files: []
|