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.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +27 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +4 -0
  5. data/Guardfile +17 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +86 -0
  8. data/Rakefile +2 -0
  9. data/audio-feed-manager.gemspec +32 -0
  10. data/bin/afm +5 -0
  11. data/lib/audio-feed-manager.rb +1 -0
  12. data/lib/audio_feed_manager/application.rb +39 -0
  13. data/lib/audio_feed_manager/audio_file.rb +9 -0
  14. data/lib/audio_feed_manager/audio_file_adder.rb +14 -0
  15. data/lib/audio_feed_manager/audio_file_repository.rb +42 -0
  16. data/lib/audio_feed_manager/cli/add_audio_file.rb +39 -0
  17. data/lib/audio_feed_manager/cli/add_feed.rb +20 -0
  18. data/lib/audio_feed_manager/cli/arguments.rb +27 -0
  19. data/lib/audio_feed_manager/cli/arguments_parser.rb +67 -0
  20. data/lib/audio_feed_manager/cli/arguments_specification.rb +103 -0
  21. data/lib/audio_feed_manager/cli/command.rb +23 -0
  22. data/lib/audio_feed_manager/cli/commands_list.rb +120 -0
  23. data/lib/audio_feed_manager/cli/get_feed_url.rb +20 -0
  24. data/lib/audio_feed_manager/cli/initialize_project.rb +44 -0
  25. data/lib/audio_feed_manager/cli/list_feeds.rb +38 -0
  26. data/lib/audio_feed_manager/cli/publish.rb +21 -0
  27. data/lib/audio_feed_manager/cli/show_feed.rb +44 -0
  28. data/lib/audio_feed_manager/cli/update_feed_rss.rb +19 -0
  29. data/lib/audio_feed_manager/cli.rb +19 -0
  30. data/lib/audio_feed_manager/config.rb +5 -0
  31. data/lib/audio_feed_manager/config_repository.rb +14 -0
  32. data/lib/audio_feed_manager/console.rb +28 -0
  33. data/lib/audio_feed_manager/directory_lister.rb +9 -0
  34. data/lib/audio_feed_manager/error.rb +4 -0
  35. data/lib/audio_feed_manager/feed.rb +9 -0
  36. data/lib/audio_feed_manager/feed_items_repository.rb +30 -0
  37. data/lib/audio_feed_manager/feed_repository.rb +32 -0
  38. data/lib/audio_feed_manager/feed_syncer.rb +20 -0
  39. data/lib/audio_feed_manager/file_storage.rb +20 -0
  40. data/lib/audio_feed_manager/id3_tags.rb +14 -0
  41. data/lib/audio_feed_manager/new_model_creator.rb +15 -0
  42. data/lib/audio_feed_manager/rss_generator.rb +10 -0
  43. data/lib/audio_feed_manager/rss_repository.rb +36 -0
  44. data/lib/audio_feed_manager/s3_gateway.rb +39 -0
  45. data/lib/audio_feed_manager/secret_token_generator.rb +9 -0
  46. data/lib/audio_feed_manager/stop_application.rb +4 -0
  47. data/lib/audio_feed_manager/support/getter_setter_method.rb +18 -0
  48. data/lib/audio_feed_manager/support/hash_constructor.rb +9 -0
  49. data/lib/audio_feed_manager/tags.rb +9 -0
  50. data/lib/audio_feed_manager/unique_id_generator.rb +19 -0
  51. data/lib/audio_feed_manager/url_maker.rb +13 -0
  52. data/lib/audio_feed_manager/version.rb +3 -0
  53. data/lib/audio_feed_manager.rb +57 -0
  54. data/spec/audio_feed_manager/audio_file_adder_spec.rb +49 -0
  55. data/spec/audio_feed_manager/audio_file_repository_spec.rb +58 -0
  56. data/spec/audio_feed_manager/cli/add_audio_file_spec.rb +44 -0
  57. data/spec/audio_feed_manager/cli/add_feed_spec.rb +27 -0
  58. data/spec/audio_feed_manager/cli/arguments_parser_spec.rb +85 -0
  59. data/spec/audio_feed_manager/cli/arguments_specification_spec.rb +51 -0
  60. data/spec/audio_feed_manager/cli/commands_list_spec.rb +91 -0
  61. data/spec/audio_feed_manager/cli/get_feed_url_spec.rb +26 -0
  62. data/spec/audio_feed_manager/cli/initialize_project_spec.rb +47 -0
  63. data/spec/audio_feed_manager/cli/list_feeds_spec.rb +27 -0
  64. data/spec/audio_feed_manager/cli/show_feed_spec.rb +41 -0
  65. data/spec/audio_feed_manager/cli/update_feed_rss_spec.rb +26 -0
  66. data/spec/audio_feed_manager/cli_spec.rb +42 -0
  67. data/spec/audio_feed_manager/directory_lister_spec.rb +23 -0
  68. data/spec/audio_feed_manager/feed_items_repository_spec.rb +34 -0
  69. data/spec/audio_feed_manager/feeds_repository_spec.rb +40 -0
  70. data/spec/audio_feed_manager/file_storage_spec.rb +22 -0
  71. data/spec/audio_feed_manager/id3_tags_spec.rb +26 -0
  72. data/spec/audio_feed_manager/new_model_creator_spec.rb +44 -0
  73. data/spec/audio_feed_manager/rss_repository_spec.rb +34 -0
  74. data/spec/audio_feed_manager/s3_gateway_spec.rb +51 -0
  75. data/spec/audio_feed_manager/secret_token_generator_spec.rb +13 -0
  76. data/spec/audio_feed_manager/support/getter_setter_method_spec.rb +77 -0
  77. data/spec/audio_feed_manager/unique_id_generator_spec.rb +23 -0
  78. data/spec/fixtures/test.mp3 +0 -0
  79. data/spec/fixtures/test.txt +1 -0
  80. data/spec/help_spec.rb +64 -0
  81. data/spec/initializing_spec.rb +12 -0
  82. data/spec/managing_audio_files_spec.rb +21 -0
  83. data/spec/managing_feeds_spec.rb +51 -0
  84. data/spec/publishing_spec.rb +24 -0
  85. data/spec/s3_credentials.yml.sample +5 -0
  86. data/spec/spec_helper.rb +17 -0
  87. data/spec/support/builders.rb +26 -0
  88. data/spec/support/fake_console.rb +13 -0
  89. data/spec/support/fakes.rb +5 -0
  90. data/spec/support/integration_helpers.rb +38 -0
  91. 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,5 @@
1
+ module AudioFeedManager
2
+ class Config < Struct.new(:s3_region, :s3_access_key, :s3_secret, :s3_bucket)
3
+ include HashConstructor
4
+ end
5
+ 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,9 @@
1
+ module AudioFeedManager
2
+ class DirectoryLister
3
+ def list(directory)
4
+ directory = Pathname.new(directory)
5
+
6
+ Dir[directory.join("*")].map{|f| File.basename(f)}.sort
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ module AudioFeedManager
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -0,0 +1,9 @@
1
+ module AudioFeedManager
2
+ class Feed < Struct.new(:id, :title, :description, :secret_token)
3
+ include HashConstructor
4
+
5
+ def bare_id
6
+ id.gsub("feeds/", "")
7
+ end
8
+ end
9
+ 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,10 @@
1
+ module AudioFeedManager
2
+ class RssGenerator
3
+ takes :rss_repository, :feed_items_repository
4
+
5
+ def generate(feed)
6
+ audio_files = feed_items_repository.list(feed)
7
+ rss_repository.store(feed, audio_files)
8
+ end
9
+ end
10
+ 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,9 @@
1
+ require 'securerandom'
2
+
3
+ module AudioFeedManager
4
+ class SecretTokenGenerator
5
+ def generate
6
+ SecureRandom.hex(8)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ module AudioFeedManager
2
+ class StopApplication < Error
3
+ end
4
+ 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
@@ -0,0 +1,9 @@
1
+ module AudioFeedManager
2
+ module HashConstructor
3
+ def initialize(args = {})
4
+ args.each do |name, value|
5
+ self.send("#{name}=", value)
6
+ end
7
+ end
8
+ end
9
+ end