audio-feed-manager 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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