audio-feed-manager 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.
- 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
|