audio-feed-manager 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +27 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Guardfile +17 -0
- data/LICENSE.txt +22 -0
- data/README.md +86 -0
- data/Rakefile +2 -0
- data/audio-feed-manager.gemspec +32 -0
- data/bin/afm +5 -0
- data/lib/audio-feed-manager.rb +1 -0
- data/lib/audio_feed_manager/application.rb +39 -0
- data/lib/audio_feed_manager/audio_file.rb +9 -0
- data/lib/audio_feed_manager/audio_file_adder.rb +14 -0
- data/lib/audio_feed_manager/audio_file_repository.rb +42 -0
- data/lib/audio_feed_manager/cli/add_audio_file.rb +39 -0
- data/lib/audio_feed_manager/cli/add_feed.rb +20 -0
- data/lib/audio_feed_manager/cli/arguments.rb +27 -0
- data/lib/audio_feed_manager/cli/arguments_parser.rb +67 -0
- data/lib/audio_feed_manager/cli/arguments_specification.rb +103 -0
- data/lib/audio_feed_manager/cli/command.rb +23 -0
- data/lib/audio_feed_manager/cli/commands_list.rb +120 -0
- data/lib/audio_feed_manager/cli/get_feed_url.rb +20 -0
- data/lib/audio_feed_manager/cli/initialize_project.rb +44 -0
- data/lib/audio_feed_manager/cli/list_feeds.rb +38 -0
- data/lib/audio_feed_manager/cli/publish.rb +21 -0
- data/lib/audio_feed_manager/cli/show_feed.rb +44 -0
- data/lib/audio_feed_manager/cli/update_feed_rss.rb +19 -0
- data/lib/audio_feed_manager/cli.rb +19 -0
- data/lib/audio_feed_manager/config.rb +5 -0
- data/lib/audio_feed_manager/config_repository.rb +14 -0
- data/lib/audio_feed_manager/console.rb +28 -0
- data/lib/audio_feed_manager/directory_lister.rb +9 -0
- data/lib/audio_feed_manager/error.rb +4 -0
- data/lib/audio_feed_manager/feed.rb +9 -0
- data/lib/audio_feed_manager/feed_items_repository.rb +30 -0
- data/lib/audio_feed_manager/feed_repository.rb +32 -0
- data/lib/audio_feed_manager/feed_syncer.rb +20 -0
- data/lib/audio_feed_manager/file_storage.rb +20 -0
- data/lib/audio_feed_manager/id3_tags.rb +14 -0
- data/lib/audio_feed_manager/new_model_creator.rb +15 -0
- data/lib/audio_feed_manager/rss_generator.rb +10 -0
- data/lib/audio_feed_manager/rss_repository.rb +36 -0
- data/lib/audio_feed_manager/s3_gateway.rb +39 -0
- data/lib/audio_feed_manager/secret_token_generator.rb +9 -0
- data/lib/audio_feed_manager/stop_application.rb +4 -0
- data/lib/audio_feed_manager/support/getter_setter_method.rb +18 -0
- data/lib/audio_feed_manager/support/hash_constructor.rb +9 -0
- data/lib/audio_feed_manager/tags.rb +9 -0
- data/lib/audio_feed_manager/unique_id_generator.rb +19 -0
- data/lib/audio_feed_manager/url_maker.rb +13 -0
- data/lib/audio_feed_manager/version.rb +3 -0
- data/lib/audio_feed_manager.rb +57 -0
- data/spec/audio_feed_manager/audio_file_adder_spec.rb +49 -0
- data/spec/audio_feed_manager/audio_file_repository_spec.rb +58 -0
- data/spec/audio_feed_manager/cli/add_audio_file_spec.rb +44 -0
- data/spec/audio_feed_manager/cli/add_feed_spec.rb +27 -0
- data/spec/audio_feed_manager/cli/arguments_parser_spec.rb +85 -0
- data/spec/audio_feed_manager/cli/arguments_specification_spec.rb +51 -0
- data/spec/audio_feed_manager/cli/commands_list_spec.rb +91 -0
- data/spec/audio_feed_manager/cli/get_feed_url_spec.rb +26 -0
- data/spec/audio_feed_manager/cli/initialize_project_spec.rb +47 -0
- data/spec/audio_feed_manager/cli/list_feeds_spec.rb +27 -0
- data/spec/audio_feed_manager/cli/show_feed_spec.rb +41 -0
- data/spec/audio_feed_manager/cli/update_feed_rss_spec.rb +26 -0
- data/spec/audio_feed_manager/cli_spec.rb +42 -0
- data/spec/audio_feed_manager/directory_lister_spec.rb +23 -0
- data/spec/audio_feed_manager/feed_items_repository_spec.rb +34 -0
- data/spec/audio_feed_manager/feeds_repository_spec.rb +40 -0
- data/spec/audio_feed_manager/file_storage_spec.rb +22 -0
- data/spec/audio_feed_manager/id3_tags_spec.rb +26 -0
- data/spec/audio_feed_manager/new_model_creator_spec.rb +44 -0
- data/spec/audio_feed_manager/rss_repository_spec.rb +34 -0
- data/spec/audio_feed_manager/s3_gateway_spec.rb +51 -0
- data/spec/audio_feed_manager/secret_token_generator_spec.rb +13 -0
- data/spec/audio_feed_manager/support/getter_setter_method_spec.rb +77 -0
- data/spec/audio_feed_manager/unique_id_generator_spec.rb +23 -0
- data/spec/fixtures/test.mp3 +0 -0
- data/spec/fixtures/test.txt +1 -0
- data/spec/help_spec.rb +64 -0
- data/spec/initializing_spec.rb +12 -0
- data/spec/managing_audio_files_spec.rb +21 -0
- data/spec/managing_feeds_spec.rb +51 -0
- data/spec/publishing_spec.rb +24 -0
- data/spec/s3_credentials.yml.sample +5 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/builders.rb +26 -0
- data/spec/support/fake_console.rb +13 -0
- data/spec/support/fakes.rb +5 -0
- data/spec/support/integration_helpers.rb +38 -0
- metadata +297 -0
@@ -0,0 +1,120 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
class CommandsList < Command
|
3
|
+
arguments do |s|
|
4
|
+
s.name 'help'
|
5
|
+
s.description 'Print help about available commands and arguments/options'
|
6
|
+
|
7
|
+
s.argument :command_name, required: false, description: "Command, for which to print help"
|
8
|
+
s.argument :subcommand_name, required: false, description: "Subcommand (such as 'add' in 'feeds add')"
|
9
|
+
end
|
10
|
+
|
11
|
+
takes :initialize_project,
|
12
|
+
:add_feed,
|
13
|
+
:list_feeds,
|
14
|
+
:add_audio_file,
|
15
|
+
:show_feed,
|
16
|
+
:get_feed_url,
|
17
|
+
:update_feed_rss,
|
18
|
+
:publish,
|
19
|
+
:console
|
20
|
+
|
21
|
+
def get(argv)
|
22
|
+
name, *options = *argv
|
23
|
+
command = nil
|
24
|
+
|
25
|
+
case name
|
26
|
+
when 'init'
|
27
|
+
command = initialize_project
|
28
|
+
when 'publish'
|
29
|
+
command = publish
|
30
|
+
when 'help'
|
31
|
+
command = help
|
32
|
+
when 'feeds'
|
33
|
+
subcommand, *options = *options
|
34
|
+
name = "#{name} #{subcommand}"
|
35
|
+
|
36
|
+
case subcommand
|
37
|
+
when 'add'
|
38
|
+
command = add_feed
|
39
|
+
when 'show'
|
40
|
+
command = show_feed
|
41
|
+
when 'get-url'
|
42
|
+
command = get_feed_url
|
43
|
+
when 'update-rss'
|
44
|
+
command = update_feed_rss
|
45
|
+
when 'list'
|
46
|
+
command = list_feeds
|
47
|
+
end
|
48
|
+
when 'audio'
|
49
|
+
subcommand, *options = *options
|
50
|
+
name = "#{name} #{subcommand}"
|
51
|
+
|
52
|
+
case subcommand
|
53
|
+
when 'add'
|
54
|
+
command = add_audio_file
|
55
|
+
end
|
56
|
+
when nil
|
57
|
+
command = help
|
58
|
+
end
|
59
|
+
|
60
|
+
raise UnknownCommand, "Unknown command: #{name}" unless command
|
61
|
+
|
62
|
+
[command, options]
|
63
|
+
end
|
64
|
+
|
65
|
+
def print_usage
|
66
|
+
console.error "Usage: afm <COMMAND> [options] [arguments...]"
|
67
|
+
console.error ""
|
68
|
+
console.error "Available commands:"
|
69
|
+
console.error ""
|
70
|
+
command_list.each do |command|
|
71
|
+
console.error "\t#{command.name} - #{command.description}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def print_command_usage(command_name, subcommand_name = nil)
|
76
|
+
command, _ = get([command_name, subcommand_name].compact)
|
77
|
+
|
78
|
+
console.error "Usage: afm #{command.usage_line}"
|
79
|
+
|
80
|
+
if command.has_arguments?
|
81
|
+
console.error ""
|
82
|
+
console.error "Arguments:"
|
83
|
+
|
84
|
+
command.required_arguments.each do |arg|
|
85
|
+
console.error "\t#{arg.usage_name} - #{arg.description}"
|
86
|
+
end
|
87
|
+
|
88
|
+
command.optional_arguments.each do |arg|
|
89
|
+
console.error "\t#{arg.usage_name} (optional) - #{arg.description}"
|
90
|
+
end
|
91
|
+
|
92
|
+
command.varargs_arguments.each do |arg|
|
93
|
+
at_least_text = arg.minimum > 0 ? "(at least #{arg.minimum}) " : ""
|
94
|
+
console.error "\t#{arg.usage_name} #{at_least_text}- #{arg.description}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def run(command_name: nil, subcommand_name: nil)
|
100
|
+
if command_name
|
101
|
+
print_command_usage(command_name, subcommand_name)
|
102
|
+
else
|
103
|
+
print_usage
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
|
109
|
+
def help
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
def command_list
|
114
|
+
[initialize_project, add_feed, list_feeds, show_feed, update_feed_rss, get_feed_url, add_audio_file, publish, help]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class UnknownCommand < Error
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
class GetFeedUrl < Command
|
3
|
+
arguments do |s|
|
4
|
+
s.name 'feeds get-url'
|
5
|
+
s.description 'Get URL of the feed'
|
6
|
+
|
7
|
+
s.argument :feed, required: true, description: "Identifier of the feed to print the URL for."
|
8
|
+
end
|
9
|
+
|
10
|
+
takes :console, :feed_repository, :url_maker
|
11
|
+
|
12
|
+
def run(feed:)
|
13
|
+
feed = feed_repository.fetch(feed)
|
14
|
+
url = url_maker.feed_url(feed)
|
15
|
+
console.info(url)
|
16
|
+
rescue FeedNotFound => e
|
17
|
+
console.die(e)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
module AudioFeedManager
|
4
|
+
class InitializeProject < Command
|
5
|
+
takes :directory,
|
6
|
+
:console,
|
7
|
+
:config_repository,
|
8
|
+
:rss_files_prefix,
|
9
|
+
:audio_files_prefix,
|
10
|
+
:data_files_prefix,
|
11
|
+
:items_prefix,
|
12
|
+
:feeds_prefix
|
13
|
+
|
14
|
+
arguments do |s|
|
15
|
+
s.name 'init'
|
16
|
+
s.description 'Initialize a new project directory'
|
17
|
+
|
18
|
+
s.argument :project_dir, required: true, description: "Name of the project (will be used to create a directory)"
|
19
|
+
end
|
20
|
+
|
21
|
+
def run(project_dir:)
|
22
|
+
project_dir = directory.join(project_dir)
|
23
|
+
mkdir(project_dir, rss_files_prefix)
|
24
|
+
mkdir(project_dir, audio_files_prefix)
|
25
|
+
mkdir(project_dir, data_files_prefix)
|
26
|
+
mkdir(project_dir, items_prefix)
|
27
|
+
mkdir(project_dir, feeds_prefix)
|
28
|
+
|
29
|
+
config = Config.new(
|
30
|
+
s3_access_key: console.ask("S3 Access key"),
|
31
|
+
s3_secret: console.ask("S3 Secret key"),
|
32
|
+
s3_region: console.ask("S3 Region", default: "us-west-2"),
|
33
|
+
s3_bucket: console.ask("Bucket name")
|
34
|
+
)
|
35
|
+
config_repository.store(project_dir.join("config.yml"), config)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def mkdir(project_dir, prefix)
|
41
|
+
FileUtils.mkdir_p(project_dir.join(prefix))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
class ListFeeds < Command
|
3
|
+
arguments do |s|
|
4
|
+
s.name 'feeds list'
|
5
|
+
s.description 'Lists the created feeds'
|
6
|
+
end
|
7
|
+
|
8
|
+
takes :feed_repository, :console
|
9
|
+
|
10
|
+
def run(_ = {})
|
11
|
+
feeds = feed_repository.list
|
12
|
+
|
13
|
+
if feeds.empty?
|
14
|
+
print_no_feeds_message
|
15
|
+
else
|
16
|
+
print_feeds(feeds)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def print_feeds(feeds)
|
23
|
+
console.info("Feeds:")
|
24
|
+
console.info("")
|
25
|
+
|
26
|
+
console.info("#\tID\tTitle")
|
27
|
+
feeds.each_with_index do |feed, idx|
|
28
|
+
console.info("#{idx+1}.\t#{feed.id}\t#{feed.title}")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def print_no_feeds_message
|
33
|
+
console.info("No feeds yet. Add one using: afm feeds add 'some title'")
|
34
|
+
console.info("")
|
35
|
+
console.info("\tafm feeds add 'some title'")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
class Publish < Command
|
3
|
+
arguments do |s|
|
4
|
+
s.name 'publish'
|
5
|
+
s.description 'Publish the RSS and MP3 files to S3.'
|
6
|
+
end
|
7
|
+
|
8
|
+
takes :feed_syncer, :feed_repository, :console
|
9
|
+
|
10
|
+
def run(_ = {})
|
11
|
+
feeds = feed_repository.list
|
12
|
+
feeds.each do |feed|
|
13
|
+
console.info("Syncing #{feed.id}")
|
14
|
+
feed_syncer.sync(feed) do |file|
|
15
|
+
console.info("Uploading file #{file}")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
console.info("Done.")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
class ShowFeed < Command
|
3
|
+
arguments do |s|
|
4
|
+
s.name 'feeds show'
|
5
|
+
s.description 'Show detailed information about the feed'
|
6
|
+
|
7
|
+
s.argument :feed, required: true, description: "Feed to show information about, ex. feeds/hello-world"
|
8
|
+
end
|
9
|
+
|
10
|
+
takes :feed_repository, :feed_items_repository, :url_maker, :console
|
11
|
+
|
12
|
+
def run(feed:)
|
13
|
+
feed = feed_repository.fetch(feed)
|
14
|
+
audio_files = feed_items_repository.list(feed)
|
15
|
+
|
16
|
+
print_details(feed)
|
17
|
+
separator
|
18
|
+
print_audio_files(audio_files)
|
19
|
+
rescue FeedNotFound => e
|
20
|
+
console.die(e)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def print_details(feed)
|
26
|
+
console.info "Title: #{feed.title}"
|
27
|
+
console.info "ID: #{feed.id}"
|
28
|
+
console.info "URL: #{url_maker.feed_url(feed)}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def separator
|
32
|
+
console.info ""
|
33
|
+
end
|
34
|
+
|
35
|
+
def print_audio_files(audio_files)
|
36
|
+
return console.info "No audio files. Add one with 'afm audio add <FILE>'." if audio_files.empty?
|
37
|
+
|
38
|
+
console.info("#\tID\tTitle")
|
39
|
+
audio_files.each_with_index do |audio_file, idx|
|
40
|
+
console.info "#{idx + 1}\t#{audio_file.id}\t#{audio_file.title}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
class UpdateFeedRss < Command
|
3
|
+
arguments do |s|
|
4
|
+
s.name 'feeds update-rss'
|
5
|
+
s.description "Regenerates the RSS .xml file for the given feed"
|
6
|
+
|
7
|
+
s.argument :feed, required: true, description: "Identifier of the feed to regenerate RSS for."
|
8
|
+
end
|
9
|
+
|
10
|
+
takes :feed_repository, :rss_generator, :console
|
11
|
+
|
12
|
+
def run(feed:)
|
13
|
+
feed = feed_repository.fetch(feed)
|
14
|
+
rss_generator.generate(feed)
|
15
|
+
rescue FeedNotFound => e
|
16
|
+
console.die(e)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
class CLI
|
3
|
+
takes :commands_list, :arguments_parser, :help, :console
|
4
|
+
|
5
|
+
def run(argv)
|
6
|
+
command, options = commands_list.get(argv)
|
7
|
+
args = arguments_parser.parse(command.arguments, options)
|
8
|
+
command.run(args.to_hash)
|
9
|
+
rescue UnknownCommand => e
|
10
|
+
console.fail(e)
|
11
|
+
help.print_usage
|
12
|
+
raise StopApplication
|
13
|
+
rescue InvalidArguments => e
|
14
|
+
console.fail(e)
|
15
|
+
help.print_command_usage(command.name)
|
16
|
+
raise StopApplication
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
class ConfigRepository
|
3
|
+
takes :storage
|
4
|
+
|
5
|
+
def load(file_name)
|
6
|
+
attrs = storage.read(file_name) if File.exists?(file_name)
|
7
|
+
Config.new(attrs || {})
|
8
|
+
end
|
9
|
+
|
10
|
+
def store(file_name, config)
|
11
|
+
storage.store(file_name, config.to_h)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
class Console
|
3
|
+
takes :stderr, :stdout, :stdin
|
4
|
+
|
5
|
+
def info(*msg)
|
6
|
+
stdout.puts(*msg)
|
7
|
+
end
|
8
|
+
|
9
|
+
def error(*msg)
|
10
|
+
stderr.puts(*msg)
|
11
|
+
end
|
12
|
+
|
13
|
+
def fail(error)
|
14
|
+
error(error.message)
|
15
|
+
end
|
16
|
+
|
17
|
+
def die(error)
|
18
|
+
self.fail(error)
|
19
|
+
raise StopApplication
|
20
|
+
end
|
21
|
+
|
22
|
+
def ask(label, default: "")
|
23
|
+
stdout.print "#{label} [#{default}]: "
|
24
|
+
result = stdin.gets.to_s.chomp
|
25
|
+
result == "" ? default : result
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
class FeedItemsRepository
|
3
|
+
takes :items_directory, :audio_file_repository
|
4
|
+
|
5
|
+
def add(feed, audio_file)
|
6
|
+
prepend_to_file(items_file(feed), audio_file.id)
|
7
|
+
end
|
8
|
+
|
9
|
+
def list(feed)
|
10
|
+
file = items_file(feed)
|
11
|
+
return [] unless File.exist?(file)
|
12
|
+
ids = File.read(file).lines.map(&:chomp)
|
13
|
+
ids.map{|id| audio_file_repository.fetch(id)}
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def items_file(feed)
|
19
|
+
items_directory.join(feed.bare_id)
|
20
|
+
end
|
21
|
+
|
22
|
+
def prepend_to_file(file_name, id)
|
23
|
+
contents = File.exist?(file_name) ? File.read(file_name) : ""
|
24
|
+
File.open(file_name, "w") do |f|
|
25
|
+
f.puts id
|
26
|
+
f.print contents
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
class FeedRepository
|
3
|
+
takes :feeds_directory,
|
4
|
+
:feeds_prefix,
|
5
|
+
:storage,
|
6
|
+
:directory_lister,
|
7
|
+
:new_model_creator
|
8
|
+
|
9
|
+
def add(feed)
|
10
|
+
new_model_creator.create(feed, directory: feeds_directory, prefix: feeds_prefix)
|
11
|
+
end
|
12
|
+
|
13
|
+
def list
|
14
|
+
names = directory_lister.list(feeds_directory)
|
15
|
+
ids = names.map{|name| "#{feeds_prefix}/#{name}"}
|
16
|
+
ids.map {|id| fetch(id)}
|
17
|
+
end
|
18
|
+
|
19
|
+
def fetch(id)
|
20
|
+
attrs = storage.read(id)
|
21
|
+
Feed.new(attrs)
|
22
|
+
rescue FileNotFound
|
23
|
+
raise FeedNotFound.for_id(id)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class FeedNotFound < Error
|
28
|
+
def self.for_id(id)
|
29
|
+
new("Feed by ID #{id} not found!")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
class FeedSyncer
|
3
|
+
takes :s3_gateway, :feed_items_repository,
|
4
|
+
:rss_files_prefix,
|
5
|
+
:rss_files_directory,
|
6
|
+
:data_files_prefix,
|
7
|
+
:data_files_directory
|
8
|
+
|
9
|
+
def sync(feed, &block)
|
10
|
+
rss_file = rss_files_directory.join(feed.secret_token + ".xml")
|
11
|
+
s3_gateway.sync("#{rss_files_prefix}/#{feed.secret_token}.xml", rss_file, &block)
|
12
|
+
|
13
|
+
audio_files = feed_items_repository.list(feed)
|
14
|
+
audio_files.each do |audio_file|
|
15
|
+
data_file = data_files_directory.join(audio_file.secret_token + audio_file.extension)
|
16
|
+
s3_gateway.sync("#{data_files_prefix}/#{audio_file.secret_token}#{audio_file.extension}", data_file, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module AudioFeedManager
|
4
|
+
class FileStorage
|
5
|
+
takes :directory
|
6
|
+
|
7
|
+
def store(file_name, data = {})
|
8
|
+
File.write(directory.join(file_name), data.to_yaml)
|
9
|
+
end
|
10
|
+
|
11
|
+
def read(file_name)
|
12
|
+
YAML.load_file(directory.join(file_name))
|
13
|
+
rescue SystemCallError => e
|
14
|
+
raise FileNotFound, e.message
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class FileNotFound < Error
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'id3tag'
|
2
|
+
|
3
|
+
module AudioFeedManager
|
4
|
+
class Id3Tags
|
5
|
+
def read(file_name)
|
6
|
+
return Tags.blank unless File.exist?(file_name)
|
7
|
+
|
8
|
+
File.open(file_name, "rb") do |f|
|
9
|
+
tags = ID3Tag.read(f)
|
10
|
+
return Tags.new(title: tags.title, artist: tags.artist)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
class NewModelCreator
|
3
|
+
takes :unique_id_generator,
|
4
|
+
:storage,
|
5
|
+
:secret_token_generator
|
6
|
+
|
7
|
+
def create(object, directory:, prefix:)
|
8
|
+
id = unique_id_generator.generate(object.title, directory)
|
9
|
+
object.id = prefix + "/" + id
|
10
|
+
object.secret_token = secret_token_generator.generate
|
11
|
+
storage.store(object.id, object.to_h)
|
12
|
+
object
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'rss'
|
2
|
+
|
3
|
+
module AudioFeedManager
|
4
|
+
class RSSRepository
|
5
|
+
takes :rss_files_directory, :url_maker
|
6
|
+
|
7
|
+
def store(feed, audio_files = [])
|
8
|
+
xml = render(feed, audio_files)
|
9
|
+
File.write(rss_files_directory.join("#{feed.secret_token}.xml"), xml)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def render(feed, audio_files)
|
15
|
+
RSS::Maker.make("2.0") do |m|
|
16
|
+
m.channel.title = feed.title
|
17
|
+
m.channel.description = feed.description
|
18
|
+
m.channel.link = url_maker.feed_url(feed)
|
19
|
+
|
20
|
+
audio_files.each_with_index do |file, idx|
|
21
|
+
m.items.new_item do |item|
|
22
|
+
link = url_maker.audio_file_url(file)
|
23
|
+
item.link = link
|
24
|
+
item.title = file.title
|
25
|
+
item.author = file.author
|
26
|
+
item.itunes_author = file.author
|
27
|
+
item.updated = (Date.today - idx).to_s
|
28
|
+
item.enclosure.url = link
|
29
|
+
item.enclosure.type = file.content_type
|
30
|
+
item.enclosure.length = file.size
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require 'digest'
|
3
|
+
|
4
|
+
module AudioFeedManager
|
5
|
+
class S3Gateway
|
6
|
+
takes :config
|
7
|
+
|
8
|
+
def url(path)
|
9
|
+
bucket.object(path).public_url
|
10
|
+
end
|
11
|
+
|
12
|
+
def sync(key, file_name, &block)
|
13
|
+
return unless needs_syncing?(key, file_name)
|
14
|
+
yield key if block_given?
|
15
|
+
|
16
|
+
object = bucket.object(key)
|
17
|
+
object.upload_file(file_name)
|
18
|
+
object.acl.put(acl: "public-read")
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def needs_syncing?(key, file_name)
|
24
|
+
object = bucket.object(key)
|
25
|
+
return true unless object.exists?
|
26
|
+
object.etag.gsub('"', '') != Digest::MD5.file(file_name).to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
def s3
|
30
|
+
@s3 ||= Aws::S3::Resource.new(access_key_id: config.s3_access_key,
|
31
|
+
secret_access_key: config.s3_secret,
|
32
|
+
region: config.s3_region)
|
33
|
+
end
|
34
|
+
|
35
|
+
def bucket
|
36
|
+
@bucket ||= s3.bucket(config.s3_bucket)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module AudioFeedManager
|
2
|
+
module GetterSetterMethod
|
3
|
+
def get_set(name, &default)
|
4
|
+
define_method name do |value = :no_value_given|
|
5
|
+
default ||= ->(){}
|
6
|
+
inst_variable_name = "@#{name}"
|
7
|
+
|
8
|
+
instance_variable_set(inst_variable_name, value) if value != :no_value_given
|
9
|
+
|
10
|
+
if !instance_variable_defined?(inst_variable_name) && value == :no_value_given
|
11
|
+
instance_variable_set(inst_variable_name, instance_exec(&default))
|
12
|
+
end
|
13
|
+
|
14
|
+
instance_variable_get(inst_variable_name)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|