fyt 1.0.0.pre

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 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: []