fyt 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 887c1039e232c88cea39f0ee81f91b5ad4016d27
4
+ data.tar.gz: 496085398da2655a31237eeffbe808d722c0500b
5
+ SHA512:
6
+ metadata.gz: 5afc811c1e1f7ebfa4b041974c10ad2d888bf2e6b87322425eccc14e9730aa1fad300ea290556b0b9b1da91c6836ec889fdeb067836e4d9a1773817112858000
7
+ data.tar.gz: e4c809f130ce2b717d1eaa060b42f40798ad2b5f150b08cc6f8968f7da42501622d26732c41588db55994be75e6f36e0549c99ef0459220da20e854f3d7984d3
data/bin/fyt ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'fyt'
5
+
6
+ FYT.lock
7
+
8
+ case ARGV.first
9
+ when 'run'
10
+ FYT.run
11
+ when 'config'
12
+ FYT.config
13
+ when 'add'
14
+ FYT.add
15
+ else
16
+ puts "unknown paramter #{ARGV.first}"
17
+ end
data/lib/fyt/base.rb ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ require 'logger'
3
+
4
+ module FYT
5
+ # processes the Youtube feed
6
+ class Base
7
+ @@logger = nil
8
+
9
+ def logger
10
+ @@logger ||= Logger.new(STDOUT).tap do |logger|
11
+ logger.level = Logger::INFO
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+ module FYT
3
+ # processes the Youtube feed
4
+ class Builder < FYT::Base
5
+ def initialize(source_feed, storage, server_prefix)
6
+ @source_feed = source_feed
7
+ @storage = storage
8
+ @server_prefix = server_prefix
9
+ @maker = RSS::Maker['2.0'].new
10
+ end
11
+
12
+ def build
13
+ add_channel_data(@source_feed.link.href, @source_feed.title.content)
14
+ add_image(@source_feed.author.uri.content, @source_feed.title.content)
15
+
16
+ build_items
17
+
18
+ @maker.to_feed
19
+ end
20
+
21
+ private
22
+
23
+ def build_items
24
+ @source_feed.items.each do |item|
25
+ filename = @storage.add(item)
26
+
27
+ add_item(item.link.href, item.title.content, filename)
28
+ end
29
+ end
30
+
31
+ def add_channel_data(link, title)
32
+ logger.debug "Title: #{title}"
33
+
34
+ @maker.channel.updated = Time.now.to_s
35
+ @maker.channel.link = link
36
+ @maker.channel.title = title
37
+ @maker.channel.description = 'Processed Youtube Feed'
38
+ end
39
+
40
+ def add_image(youtube_url, title)
41
+ youtube_url = youtube_url.gsub('http:', 'https:')
42
+
43
+ open(youtube_url) do |file|
44
+ image_url =
45
+ file.read.scan(/<meta property=\"og:image\" content=\"(.*)\">/)
46
+ .flatten.first
47
+
48
+ @maker.image.url = image_url
49
+ @maker.image.title = title
50
+ end
51
+ end
52
+
53
+ def add_item(link, title, filename)
54
+ @maker.items.new_item do |new_item|
55
+ new_item.link = link
56
+ new_item.title = title
57
+ new_item.updated = @storage.mtime(filename)
58
+ new_item.enclosure.url = "#{@server_prefix}/#{filename}"
59
+ new_item.enclosure.type = 'video/mp4'
60
+ new_item.enclosure.length = @storage.size(filename)
61
+ end
62
+ end
63
+ end
64
+ end
data/lib/fyt/config.rb ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+ require 'yaml/store'
3
+
4
+ module FYT
5
+ # reads and prepares a youtube feed for further processing
6
+ class Config < FYT::Base
7
+ def initialize(path = nil)
8
+ path ||= File.join(Dir.home, '.fyt.config.yml')
9
+ @store = YAML::Store.new(path)
10
+
11
+ # populate defaults
12
+ @store.transaction do
13
+ @store[:storage_path] ||= 'storage'
14
+ @store[:server_prefix] ||= 'https://localhost:2017'
15
+ @store[:format_options] ||= '22+140'
16
+ @store[:output_format] ||= 'mp4'
17
+ @store[:feeds] ||= []
18
+
19
+ @store.commit
20
+ end
21
+ end
22
+
23
+ def [](key)
24
+ @store.transaction { @store[key] }
25
+ end
26
+
27
+ def []=(key, value)
28
+ return if value.is_a?(String) && value.size.zero?
29
+
30
+ @store.transaction do
31
+ @store[key] = value
32
+ end
33
+ end
34
+ end
35
+ end
data/lib/fyt/parser.rb ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rss'
4
+
5
+ module FYT
6
+ # reads and prepares a youtube feed for further processing
7
+ class Parser < FYT::Base
8
+ def initialize(url)
9
+ @url = url
10
+ end
11
+
12
+ def read
13
+ open(@url) do |rss|
14
+ return RSS::Parser.parse(rss, false)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+ module FYT
3
+ # Manages file downloads and storage
4
+ class Storage < FYT::Base
5
+ def initialize(path, format_options, output_format, proxy = nil)
6
+ @path = path || ''
7
+ @format_options = format_options
8
+ @output_format = output_format
9
+ @proxy = proxy
10
+ @known_files = []
11
+ end
12
+
13
+ def add(item)
14
+ url = item.link.href
15
+
16
+ path = path_for(item)
17
+ download_file!(url, path)
18
+ @known_files << File.basename(path)
19
+ File.basename(path)
20
+ end
21
+
22
+ def add_feed(name, feed)
23
+ logger.debug feed.to_s
24
+ logger.debug @known_files
25
+
26
+ File.join(@path, "#{name}.feed.rss20.xml").tap do |path|
27
+ File.write(path, feed)
28
+
29
+ @known_files << File.basename(path)
30
+ end
31
+ end
32
+
33
+ def cleanup!
34
+ logger.debug 'Files to delete:'
35
+ logger.debug files_to_delete
36
+
37
+ files_to_delete.each do |filename|
38
+ delete_file! filename
39
+ end
40
+ end
41
+
42
+ def mtime(filename)
43
+ File.mtime(File.join(@path, filename))
44
+ end
45
+
46
+ def size(filename)
47
+ File.size(File.join(@path, filename))
48
+ end
49
+
50
+ private
51
+
52
+ def download_file!(url, output_path)
53
+ options = [
54
+ "-f '#{@format_options}'",
55
+ "--merge-output-format '#{@output_format}'",
56
+ "-o '#{output_path}'"
57
+ ]
58
+ options << "--proxy '#{@proxy}'" if @proxy
59
+ options << "'#{url}'"
60
+
61
+ options_string = options.join(' ')
62
+
63
+ logger.debug "Executing: youtube-dl #{options_string}"
64
+
65
+ raise unless system("youtube-dl #{options_string}", out: $stdout, err: :out)
66
+ end
67
+
68
+ def delete_file!(filename)
69
+ File.join(@path, filename).tap do |path|
70
+ logger.debug "Deleting file: #{path}"
71
+ File.delete(path)
72
+ end
73
+ end
74
+
75
+ def filename_for(item)
76
+ "#{item.id.content}.mp4"
77
+ end
78
+
79
+ def path_for(item)
80
+ File.join(@path, filename_for(item))
81
+ end
82
+
83
+ def files_on_disk
84
+ Dir.entries(@path).reject { |filename| filename.start_with? '.' }
85
+ end
86
+
87
+ def files_to_delete
88
+ files_on_disk - @known_files
89
+ end
90
+ end
91
+ end
data/lib/fyt.rb ADDED
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+ require_relative 'fyt/base'
3
+ require_relative 'fyt/builder'
4
+ require_relative 'fyt/config'
5
+ require_relative 'fyt/parser'
6
+ require_relative 'fyt/storage'
7
+
8
+ require 'fileutils'
9
+
10
+ # handles the general behaviour of FYT
11
+ module FYT
12
+ def self.lock
13
+ lockfile = '~/.fyt/pid.lock'
14
+ dirname = File.dirname(lockfile)
15
+
16
+ FileUtils.mkdir_p(dirname) unless File.directory?(dirname)
17
+
18
+ File.open(lockfile, ::File::CREAT | ::File::EXCL | ::File::WRONLY) do |f|
19
+ f.write(Process.pid.to_s)
20
+ end
21
+
22
+ at_exit { File.delete(lockfile) if File.exist?(lockfile) }
23
+ end
24
+
25
+ def self.run
26
+ config = FYT::Config.new
27
+ storage = FYT::Storage.new(
28
+ config[:storage_path],
29
+ config[:format_options],
30
+ config[:output_format],
31
+ config[:proxy]
32
+ )
33
+
34
+ config[:feeds].each do |feed_config|
35
+ source_feed = FYT::Parser.new(feed_config[:url]).read
36
+
37
+ new_feed = FYT::Builder.new(source_feed, storage, config[:server_prefix]).build
38
+
39
+ storage.add_feed(feed_config[:name], new_feed)
40
+ end
41
+
42
+ storage.cleanup!
43
+ end
44
+
45
+ def self.config
46
+ config = FYT::Config.new
47
+
48
+ print "Please enter storage path [#{config[:storage_path]}]: "
49
+ config[:storage_path] = STDIN.gets.rstrip
50
+
51
+ print "Please enter server prefix [#{config[:server_prefix]}]: "
52
+ config[:server_prefix] = STDIN.gets.rstrip
53
+
54
+ print "Please enter format options [#{config[:format_options]}]: "
55
+ config[:format_options] = STDIN.gets.rstrip
56
+
57
+ print "Please enter output_format [#{config[:output_format]}]: "
58
+ config[:output_format] = STDIN.gets.rstrip
59
+ end
60
+
61
+ def self.add
62
+ print 'Please name of the feed e.g. "My Favorite Videos": '
63
+ name = STDIN.gets.rstrip
64
+
65
+ print 'Please enter feed url e.g. "https://www.youtube.com/feeds/videos.xml?channel_id=AbCdEf1234567890aBcDeF00": '
66
+ url = STDIN.gets.rstrip
67
+
68
+ raise 'No name given' if name.size.zero?
69
+ raise 'No feed url given' if url.size.zero?
70
+
71
+ config = FYT::Config.new
72
+ feeds = config[:feeds]
73
+ feeds << { name: name, url: url }
74
+ config[:feeds] = feeds
75
+ end
76
+ end
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fyt
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.pre
5
+ platform: ruby
6
+ authors:
7
+ - Steffen Schröder
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-08-08 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Downloads a youtube channel and its videos and stores themlocally as
14
+ a podcatcher-friendly feed
15
+ email: steffen@schröder.xyz
16
+ executables:
17
+ - fyt
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - bin/fyt
22
+ - lib/fyt.rb
23
+ - lib/fyt/base.rb
24
+ - lib/fyt/builder.rb
25
+ - lib/fyt/config.rb
26
+ - lib/fyt/parser.rb
27
+ - lib/fyt/storage.rb
28
+ homepage: https://github.com/ChaosSteffen/fyt
29
+ licenses:
30
+ - BSD-2-Clause
31
+ metadata: {}
32
+ post_install_message:
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">"
44
+ - !ruby/object:Gem::Version
45
+ version: 1.3.1
46
+ requirements: []
47
+ rubyforge_project:
48
+ rubygems_version: 2.6.10
49
+ signing_key:
50
+ specification_version: 4
51
+ summary: fyt
52
+ test_files: []