fyt 1.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/fyt +17 -0
- data/lib/fyt/base.rb +15 -0
- data/lib/fyt/builder.rb +64 -0
- data/lib/fyt/config.rb +35 -0
- data/lib/fyt/parser.rb +18 -0
- data/lib/fyt/storage.rb +91 -0
- data/lib/fyt.rb +76 -0
- metadata +52 -0
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
data/lib/fyt/base.rb
ADDED
data/lib/fyt/builder.rb
ADDED
@@ -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
|
data/lib/fyt/storage.rb
ADDED
@@ -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: []
|