organo 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +6 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +7 -0
  5. data/exe/organo +6 -0
  6. data/lib/organo/commands/anime/anime_add.rb +35 -0
  7. data/lib/organo/commands/anime/anime_add_sequel.rb +43 -0
  8. data/lib/organo/commands/anime/anime_get_broken_links.rb +30 -0
  9. data/lib/organo/commands/anime/anime_remove.rb +29 -0
  10. data/lib/organo/commands/anime/anime_search.rb +26 -0
  11. data/lib/organo/commands/anime/anime_show.rb +26 -0
  12. data/lib/organo/commands/anime/anime_update_image.rb +36 -0
  13. data/lib/organo/commands/anime/anime_update_query.rb +33 -0
  14. data/lib/organo/commands/api/api_get_anime.rb +25 -0
  15. data/lib/organo/commands/api/api_get_season.rb +26 -0
  16. data/lib/organo/commands/api/api_get_sequels.rb +24 -0
  17. data/lib/organo/commands/api/api_search_anime.rb +27 -0
  18. data/lib/organo/commands/init.rb +22 -0
  19. data/lib/organo/commands/merge.rb +20 -0
  20. data/lib/organo/commands/remote/remote_add.rb +38 -0
  21. data/lib/organo/commands/remote/remote_download.rb +41 -0
  22. data/lib/organo/commands/remote/remote_remove.rb +34 -0
  23. data/lib/organo/commands/remote/remote_upload.rb +43 -0
  24. data/lib/organo/commands/schedule/schedule_create.rb +27 -0
  25. data/lib/organo/commands/statistics.rb +26 -0
  26. data/lib/organo/commands/version.rb +15 -0
  27. data/lib/organo/config.rb +6 -0
  28. data/lib/organo/jikan_api.rb +11 -0
  29. data/lib/organo/query.rb +30 -0
  30. data/lib/organo/reader.rb +38 -0
  31. data/lib/organo/request.rb +13 -0
  32. data/lib/organo/search_show.rb +58 -0
  33. data/lib/organo/sequel.rb +39 -0
  34. data/lib/organo/show.rb +36 -0
  35. data/lib/organo/show_list.rb +32 -0
  36. data/lib/organo/version.rb +5 -0
  37. data/lib/organo/writer.rb +16 -0
  38. data/lib/organo.rb +61 -0
  39. metadata +141 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 07f88d3bafa6f79b66260174f984f415229f6f449da715b010df238d201c1eab
4
+ data.tar.gz: 77efeae28ceefe926c10be6e6789ca5158fc49e1c405ea88c37932a087c40a5b
5
+ SHA512:
6
+ metadata.gz: f9cfb0f392479f25a6713525966805c30fd455403e2079f479337050b8722c2f9c5ddd294cbfab4992e20a691b3f09ef3695715feefa6b3fd9efa857f5bcbd87
7
+ data.tar.gz: 637433a7ad969aeaab7cace448cea1b1a2b74a60891c384c8ab8ed2f48b0c46ec4506b0c003352c68b168d33a004b13f5da874800883958b824488f2b2291c0a
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in dkb-datatool.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Louis-Philippe Fortin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # Organo
2
+
3
+ Organo is named after the mafia group that runs the city of Lux in the anime [Texhnolyze](https://myanimelist.net/anime/26/Texhnolyze).
4
+
5
+ This tool has been made to manage data that is shown on the DKB Anime Community website. It uses to the [JikanAPI](https://jikan.moe), an unofficial MyAnimeList API, to get data.
6
+
7
+ **This tool is still under construction. Documentation is coming soon.**
data/exe/organo ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'organo'
5
+
6
+ Dry::CLI.new(Organo::CLI::Commands).call
@@ -0,0 +1,35 @@
1
+ require_relative '../../config'
2
+ require_relative '../../writer'
3
+ require_relative '../../reader'
4
+
5
+ module Organo
6
+ module CLI
7
+ module Commands
8
+ module Anime
9
+ module Add
10
+
11
+ class AnimeEntry < Dry::CLI::Command
12
+ desc "Add anime entry to files"
13
+ argument :mal_id, type: :integer, required: true, desc: "MyAnimeList ID"
14
+
15
+ def call(mal_id:, **)
16
+ json_obj = Query.get_anime_by_id(mal_id)
17
+ show = SearchShow.new(json_obj['mal_id'], json_obj['title'], json_obj['images']['jpg']['image_url'])
18
+ show_type = json_obj['type']
19
+ file_name = (show_type == 'TV' ? "#{json_obj['year']}-#{json_obj['season']}" : show_type.downcase)
20
+ file_path = "#{Config::DEFAULT_DIR}/#{file_name}.json"
21
+ show_list = File.exist?(file_path) ? Reader.read_file(file_path) : Array.new
22
+ if (show_list.find_index { |s| s.id.to_s == mal_id.to_s }.nil?)
23
+ Writer.to_file(show_list.push(show).sort_by(&:title), file_path)
24
+ puts "Added anime \"#{show.title}\" (#{mal_id})"
25
+ else
26
+ puts 'Cannot add an already existing anime'
27
+ end
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,43 @@
1
+ require_relative '../../config'
2
+ require_relative '../../writer'
3
+ require_relative '../../reader'
4
+
5
+ module Organo
6
+ module CLI
7
+ module Commands
8
+ module Anime
9
+ module Add
10
+
11
+ class SequelEntry < Dry::CLI::Command
12
+ desc 'Add anime entry to files'
13
+ argument :anime_mal_id, type: :integer, required: true, desc: 'Anime MyAnimeList ID'
14
+ argument :sequel_mal_id, type: :integer, required: true, desc: 'Sequel MyAnimeList ID'
15
+ argument :season_num, type: :integer, required: false, default: 0, desc: 'Season number'
16
+ argument :type, type: :string, required: false, default: '', values: %w[part movie special], desc: 'Type of sequel (part, movie, special)'
17
+ argument :type_num, type: :integer, required: false, default: 0, desc: 'The sequel type number'
18
+
19
+ def call(anime_mal_id:, sequel_mal_id:, season_num:, type:, type_num:, **)
20
+ json_obj = Query.get_anime_by_id(sequel_mal_id)
21
+ Dir["#{Config::DEFAULT_DIR}/*.json"].each do |file_path|
22
+ shows_list = Reader.read_file(file_path)
23
+ show = shows_list.find { |s| s.id.to_s == anime_mal_id.to_s }
24
+ next if show.nil?
25
+
26
+ unless show.sequels.find { |s| s['id'].to_s == sequel_mal_id.to_s }
27
+ sequel = Sequel.new(json_obj['mal_id'], json_obj['title'], json_obj['images']['jpg']['image_url'], season_num, type, type_num)
28
+ show.sequels.push(sequel)
29
+ Writer.to_file(shows_list, file_path)
30
+ puts "Added sequel \"#{sequel.title}\" (#{sequel.id}) to anime."
31
+ else
32
+ puts 'Cannot add an already existing sequel.'
33
+ end
34
+ break
35
+ end
36
+ end
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,30 @@
1
+ require 'httparty'
2
+ require_relative '../../reader'
3
+
4
+ module Organo
5
+ module CLI
6
+ module Commands
7
+ module Anime
8
+ module Get
9
+
10
+ class BrokenLinks < Dry::CLI::Command
11
+ desc "Get list of anime IDs that have broken links"
12
+
13
+ def call(*)
14
+ show_list = Reader.read_all_files
15
+ broken_list = Array.new
16
+ show_list.each do |show|
17
+ response = HTTParty.get(show.image_url)
18
+ next unless response.code == 404
19
+
20
+ broken_list.push(show.id)
21
+ end
22
+ puts broken_list.join(' ') unless broken_list.empty?
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ require_relative '../../config'
2
+
3
+ module Organo
4
+ module CLI
5
+ module Commands
6
+ module Anime
7
+
8
+ class Remove < Dry::CLI::Command
9
+ desc "Remove anime entry from files"
10
+ argument :mal_id, type: :integer, required: true, desc: "MyAnimeList ID"
11
+
12
+ def call(mal_id:, **)
13
+ Dir["#{Config::DEFAULT_DIR}/*.json"].each do |file_path|
14
+ shows_list = Reader.read_file(file_path)
15
+ show_index = shows_list.find_index { |s| s.id.to_s == mal_id.to_s }
16
+ if show_index >= 0
17
+ show = shows_list.delete_at(show_index)
18
+ Writer.to_file(shows_list, file_path)
19
+ puts "Removed anime \"#{show.title}\" (#{mal_id})"
20
+ break
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ require_relative '../../config'
2
+
3
+ module Organo
4
+ module CLI
5
+ module Commands
6
+ module Anime
7
+
8
+ class Search < Dry::CLI::Command
9
+ desc "Search anime by query from files"
10
+ argument :query, type: :string, required: true, desc: "Search query"
11
+
12
+ def call(query:, **)
13
+ found_list = Array.new
14
+ Dir["#{Config::DEFAULT_DIR}/*.json"].each do |file_path|
15
+ shows_list = Reader.read_file(file_path)
16
+ show = shows_list.find { |s| s.title.downcase.include? query }
17
+ found_list.push(show) unless show.nil?
18
+ end
19
+ puts found_list.to_json
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ require_relative '../../config'
2
+
3
+ module Organo
4
+ module CLI
5
+ module Commands
6
+ module Anime
7
+
8
+ class Show < Dry::CLI::Command
9
+ desc "Show anime entry from files"
10
+ argument :mal_id, type: :integer, required: true, desc: "MyAnimeList ID"
11
+
12
+ def call(mal_id:, **)
13
+ Dir["#{Config::DEFAULT_DIR}/*.json"].each do |file_path|
14
+ shows_list = Reader.read_file(file_path)
15
+ show = shows_list.find { |s| s.id.to_s == mal_id.to_s }
16
+ next if show.nil?
17
+
18
+ puts show.to_json
19
+ end
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,36 @@
1
+ require_relative '../../config'
2
+ require_relative '../../query'
3
+ require_relative '../../writer'
4
+ require_relative '../../reader'
5
+
6
+ module Organo
7
+ module CLI
8
+ module Commands
9
+ module Anime
10
+ module Update
11
+
12
+ class Image < Dry::CLI::Command
13
+ desc "Update cover image for the specified anime"
14
+ argument :mal_id_list, type: :array, required: true, desc: "MyAnimeList ID list"
15
+
16
+ def call(mal_id_list:, **)
17
+ Dir["#{Config::DEFAULT_DIR}/*.json"].each do |file_path|
18
+ shows_list = Reader.read_file(file_path)
19
+ mal_id_list.each do |mal_id|
20
+ show = shows_list.find { |s| s.id.to_s == mal_id.to_s }
21
+ next if show.nil?
22
+
23
+ show.image_url = Query.get_anime_image(show.id)
24
+ Writer.to_file(shows_list, file_path)
25
+ # puts show.image_url
26
+ end
27
+ end
28
+ puts 'Cover image(s) has been updated.'
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,33 @@
1
+ require_relative '../../config'
2
+ require_relative '../../writer'
3
+ require_relative '../../reader'
4
+
5
+ module Organo
6
+ module CLI
7
+ module Commands
8
+ module Anime
9
+ module Update
10
+
11
+ class SearchQuery < Dry::CLI::Command
12
+ desc "Update search query for the specified anime"
13
+ argument :mal_id, type: :integer, required: true, desc: "MyAnimeList ID"
14
+ argument :search_query, type: :string, required: true, desc: "String query for Nyaa Torrents search"
15
+
16
+ def call(mal_id:, search_query:, **)
17
+ Dir["#{Config::DEFAULT_DIR}/*.json"].each do |file_path|
18
+ shows_list = Reader.read_file(file_path)
19
+ show = shows_list.find { |s| s.id.to_s == mal_id.to_s }
20
+ next if show.nil?
21
+
22
+ show.search_query = search_query
23
+ Writer.to_file(shows_list, file_path)
24
+ puts "Search query has been updated."
25
+ end
26
+ end
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,25 @@
1
+ require_relative '../../query'
2
+ require_relative '../../show_list'
3
+
4
+ module Organo
5
+ module CLI
6
+ module Commands
7
+ module API
8
+ module Get
9
+
10
+ class Anime < Dry::CLI::Command
11
+ desc "Get anime with specific ID from API"
12
+ argument :mal_id, type: :integer, required: true, desc: "MyAnimeList ID"
13
+
14
+ def call(mal_id:, **)
15
+ json_obj = Query.get_anime_by_id(mal_id)
16
+ show = SearchShow.new(json_obj['mal_id'], json_obj['title'], json_obj['images']['jpg']['image_url'])
17
+ puts show.to_json
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ require_relative '../../query'
2
+ require_relative '../../show_list'
3
+
4
+ module Organo
5
+ module CLI
6
+ module Commands
7
+ module API
8
+ module Get
9
+
10
+ class Season < Dry::CLI::Command
11
+ desc "Get list of anime for a specific season"
12
+ argument :season, type: :string, required: true, desc: "Winter, Spring, Summer or Fall"
13
+ argument :year, type: :integer, required: true, desc: "Year"
14
+
15
+ def call(season:, year:, **)
16
+ json_obj = Query.get_anime_season(season, year)
17
+ show_list = ShowList.create_search_show_list(json_obj)
18
+ puts show_list.to_json
19
+ end
20
+ end
21
+
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ require_relative '../../query'
2
+ require_relative '../../show_list'
3
+
4
+ module Organo
5
+ module CLI
6
+ module Commands
7
+ module API
8
+ module Get
9
+
10
+ class Sequels < Dry::CLI::Command
11
+ desc "Get list of sequels for a specific anime"
12
+ argument :mal_id, type: :integer, required: true, desc: "MyAnimeList ID"
13
+
14
+ def call(mal_id:, **)
15
+ sequels = ShowList.create_search_sequel_list(Query.get_anime_sequel(mal_id))
16
+ puts sequels.to_json
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ require_relative '../../query'
2
+ require_relative '../../show_list'
3
+
4
+ module Organo
5
+ module CLI
6
+ module Commands
7
+ module API
8
+ module Search
9
+
10
+ class Anime < Dry::CLI::Command
11
+ desc "Get a list anime from query"
12
+ argument :query, type: :string, required: true, desc: "Search query"
13
+ option :type, type: :string, required: false, default: '', values: %w[tv movie ova special ona music], desc: "Type of anime"
14
+ option :limit, type: :integer, required: false, default: 5, desc: "Limit number of results"
15
+
16
+ def call(query:, type:, limit:, **)
17
+ json_obj = Query.search_anime(query, { 'type' => type, 'limit' => limit})
18
+ show_list = ShowList.create_search_show_list(json_obj)
19
+ puts show_list.to_json
20
+ end
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,22 @@
1
+ module Organo
2
+ module CLI
3
+ module Commands
4
+
5
+ class Init < Dry::CLI::Command
6
+ desc "Initialize directory with default files and directories"
7
+
8
+ def call(*)
9
+ Dir.mkdir './.organo' unless File.exist?('./.organo')
10
+ Dir.mkdir './.organo/datafiles' unless File.exist?('./.organo/datafiles')
11
+ File.write(
12
+ './shows.config.json',
13
+ '{"last_id":0,"schema":{"id":"integer","slug":"string","title":"string",' \
14
+ '"image_url":"string","sequels":"string","search_query":"string"}}'
15
+ )
16
+ puts 'Initialized organo directory'
17
+ end
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ require_relative '../reader'
2
+ require_relative '../writer'
3
+
4
+ module Organo
5
+ module CLI
6
+ module Commands
7
+
8
+ class Merge < Dry::CLI::Command
9
+ desc "Merge edited seasonal files into one main file"
10
+
11
+ def call(*)
12
+ show_list = Reader.read_all_files
13
+ Writer.to_data_file(show_list, 'shows')
14
+ puts 'Edited files have been merged.'
15
+ end
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,38 @@
1
+ require_relative '../../config'
2
+
3
+ module Organo
4
+ module CLI
5
+ module Commands
6
+ module Remote
7
+
8
+ class Add < Dry::CLI::Command
9
+ desc "Add remote FTP server to config file"
10
+ argument :env, type: :string, required: true, desc: "Remote environment name"
11
+ argument :url, type: :string, required: true, desc: "FTP server url"
12
+ option :username, type: :string, default: '', desc: "FTP account username"
13
+ option :password, type: :string, default: '', desc: "FTP account password"
14
+ option :directory, type: :string, default: '', desc: "FTP directory"
15
+
16
+ def call(env:, url:, username:, password:, directory:, **)
17
+ config = File.exist?(Config::DEFAULT_FILE) ? JSON.parse(File.read(Config::DEFAULT_FILE)) : Array.new
18
+ section = config.find { |option| option['section'] == 'remotes' }
19
+ remote = { env: env, url: url, username: username, password: password, directory: directory }
20
+ if section.nil?
21
+ section = { section: 'remotes', entries: []}
22
+ config.push(section)
23
+ section[:entries].push(remote)
24
+ else
25
+ if section['entries'].find { |entry| entry['env'] == env }.nil?
26
+ section['entries'].push(remote)
27
+ else
28
+ puts 'Environment name is already used'
29
+ end
30
+ end
31
+ File.write(Config::DEFAULT_FILE, JSON.pretty_generate(config))
32
+ end
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,41 @@
1
+ require 'net/ftp'
2
+
3
+ module Organo
4
+ module CLI
5
+ module Commands
6
+ module Remote
7
+
8
+ class Download < Dry::CLI::Command
9
+ desc "Download data from website"
10
+ argument :env, type: :string, required: true, desc: "Environment"
11
+
12
+ def call(env:, **)
13
+ config = File.exist?(Config::DEFAULT_FILE) ? JSON.parse(File.read(Config::DEFAULT_FILE)) : nil
14
+ section = config.find { |option| option['section'] == 'remotes' }
15
+ if section.nil?
16
+ puts "No remote is configured in the configuration file"
17
+ else
18
+ credentials = section['entries'].find { |entry| entry['env'] == env }
19
+ if credentials.nil?
20
+ puts 'Could not find the specified environment'
21
+ else
22
+ Net::FTP.open(credentials['url']) do |ftp|
23
+ ftp.login(credentials['username'], credentials['password'])
24
+ ftp.chdir(credentials['directory'])
25
+ if File.exist?('shows.data.json')
26
+ File.delete('shows.data.json.old') if File.exist?('shows.data.json.old')
27
+ File.rename('shows.data.json', 'shows.data.json.old')
28
+ end
29
+ ftp.getbinaryfile('shows.data.json')
30
+ end
31
+ end
32
+ end
33
+ rescue Net::FTPPermError
34
+ puts 'Login to FTP server failed.'
35
+ end
36
+ end
37
+
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,34 @@
1
+ require_relative '../../config'
2
+
3
+ module Organo
4
+ module CLI
5
+ module Commands
6
+ module Remote
7
+
8
+ class Remove < Dry::CLI::Command
9
+ desc "Remove remote FTP server from config file"
10
+ argument :env, type: :string, required: true, desc: "Remote environment name"
11
+
12
+ def call(env:, **)
13
+ config = File.exist?(Config::DEFAULT_FILE) ? JSON.parse(File.read(Config::DEFAULT_FILE)) : nil
14
+ unless config.nil?
15
+ section = config.find { |option| option['section'] == 'remotes' }
16
+ unless section.nil?
17
+ index = section['entries'].find_index { |entry| entry['env'] == env }
18
+ if index.nil?
19
+ puts "Specified environment does not exist"
20
+ elsif !section['entries'].delete_at(index).nil?
21
+ puts "Removed \"#{env}\" entry"
22
+ end
23
+ else
24
+ puts 'Configuration file does not have a remote'
25
+ end
26
+ end
27
+ File.write(Config::DEFAULT_FILE, JSON.pretty_generate(config))
28
+ end
29
+ end
30
+
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,43 @@
1
+ require 'net/ftp'
2
+
3
+ module Organo
4
+ module CLI
5
+ module Commands
6
+ module Remote
7
+
8
+ class Upload < Dry::CLI::Command
9
+ desc "Upload data to the website FTP"
10
+ argument :env, type: :string, required: true, desc: "Environment"
11
+
12
+ def call(env:, **)
13
+ config = File.exist?(Config::DEFAULT_FILE) ? JSON.parse(File.read(Config::DEFAULT_FILE)) : nil
14
+ section = config.find { |option| option['section'] == 'remotes' }
15
+ if section.nil?
16
+ puts "No remote is configured in the configuration file"
17
+ else
18
+ credentials = section['entries'].find { |entry| entry['env'] == env }
19
+ if credentials.nil?
20
+ puts 'Could not find the specified environment'
21
+ else
22
+ local_files = Dir['./*.{data,config}.json']
23
+ local_files.push('./schedule.json')
24
+ Net::FTP.open(credentials['url']) do |ftp|
25
+ ftp.login(credentials['username'], credentials['password'])
26
+ ftp.chdir(credentials['directory'])
27
+ local_files.each do |file_path|
28
+ file_name = File.basename(file_path)
29
+ puts "Uploading file: #{file_name}"
30
+ ftp.putbinaryfile(file_name)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ rescue Net::FTPPermError
36
+ puts 'Login to FTP server failed.'
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,27 @@
1
+ require 'date'
2
+
3
+ module Organo
4
+ module CLI
5
+ module Commands
6
+ module Schedule
7
+
8
+ class Create < Dry::CLI::Command
9
+ desc "Initialize directory with default files and directories"
10
+
11
+ def call(*)
12
+ content = Array.new
13
+ Date::DAYNAMES.each do |dname|
14
+ content.push({ 'weekday' => dname.downcase, 'shows' => [] } )
15
+ end
16
+ File.write(
17
+ './schedule.json',
18
+ JSON.pretty_generate(content)
19
+ )
20
+ puts 'Created the schedule'
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ require_relative '../reader'
2
+
3
+ module Organo
4
+ module CLI
5
+ module Commands
6
+
7
+ class Statistics < Dry::CLI::Command
8
+ desc "Merge edited seasonal files into one main file"
9
+
10
+ def call(*)
11
+ i = 0
12
+ total_size = 0
13
+ Dir["#{Config::DEFAULT_DIR}/*.json"].each do |file_path|
14
+ shows = Reader.read_file(file_path)
15
+ file_size = File.size(file_path)
16
+ puts "#{File.basename(file_path)}: #{shows.count} (#{file_size} b)"
17
+ i += shows.count
18
+ total_size += file_size
19
+ end
20
+ puts "Total count: #{i} (#{total_size} o)"
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ require_relative '../version'
2
+
3
+ module Organo
4
+ module CLI
5
+ module Commands
6
+ class Version < Dry::CLI::Command
7
+ desc "Print version"
8
+
9
+ def call(*)
10
+ puts Organo::VERSION
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ module Organo
2
+ class Config
3
+ DEFAULT_FILE = './.organo/config.json'
4
+ DEFAULT_DIR = './.organo/datafiles'
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Organo
4
+ class JikanAPI
5
+ BASE_URL = 'https://api.jikan.moe/'
6
+
7
+ def self.get_url(endpoint, api_version = 'v4')
8
+ "#{BASE_URL}#{api_version}/#{endpoint}"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'jikan_api'
4
+ require_relative 'request'
5
+ require_relative 'search_show'
6
+
7
+ module Organo
8
+ class Query
9
+ def self.get_anime_by_id(mal_id)
10
+ Request.get_json("#{JikanAPI.get_url('anime')}/#{mal_id}")
11
+ end
12
+
13
+ def self.search_anime(query, options)
14
+ form = URI.encode_www_form('q' => query, 'type' => options['type'], 'limit' => options['limit'])
15
+ Request.get_json("#{JikanAPI.get_url('anime')}?#{form}")
16
+ end
17
+
18
+ def self.get_anime_season(season, year)
19
+ Request.get_json("#{JikanAPI.get_url('seasons')}/#{year}/#{season.downcase}")
20
+ end
21
+
22
+ def self.get_anime_image(mal_id)
23
+ Request.get_json("#{JikanAPI.get_url('anime')}/#{mal_id}")['images']['jpg']['image_url']
24
+ end
25
+
26
+ def self.get_anime_sequel(mal_id)
27
+ Request.get_json("#{JikanAPI.get_url('anime')}/#{mal_id}/relations")
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require_relative 'config'
5
+ require_relative 'show'
6
+
7
+ module Organo
8
+ class Reader
9
+ def self.read_all_files
10
+ shows = []
11
+ Dir["#{Config::DEFAULT_DIR}/*.json"].each do |file_path|
12
+ shows = shows.union(Reader.read_file(file_path))
13
+ end
14
+ shows.sort_by(&:title)
15
+ end
16
+
17
+ def self.read_file(file_path)
18
+ shows = []
19
+ file = File.read(file_path)
20
+ data = JSON.parse(file)
21
+ data.each do |show|
22
+ shows.push(
23
+ Show.new(
24
+ show['id'],
25
+ show['slug'],
26
+ show['title'],
27
+ show['image_url'],
28
+ show['search_query'],
29
+ show['links'],
30
+ show['sequels']
31
+ )
32
+ )
33
+ end
34
+ shows
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rest-client'
4
+ require 'json'
5
+
6
+ module Organo
7
+ class Request
8
+ def self.get_json(url)
9
+ response = RestClient.get(url)
10
+ JSON.parse(response)['data']
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Organo
6
+ class SearchShow
7
+ attr_accessor :id, :title, :image_url, :search_query
8
+
9
+ def initialize(id, title, image_url)
10
+ @id = id
11
+ @slug = create_slug(title)
12
+ @title = title.to_s
13
+ @image_url = image_url
14
+ @search_query = title
15
+ end
16
+
17
+ def create_slug(title)
18
+ # strip the string
19
+ ret = title.strip
20
+
21
+ # blow away apostrophes
22
+ ret.gsub!(/['`]/, '')
23
+
24
+ # @ --> at, and & --> and
25
+ ret.gsub!(/\s*@\s*/, ' at ')
26
+ ret.gsub!(/\s*&\s*/, ' and ')
27
+
28
+ # replace all non alphanumeric with dash
29
+ ret.gsub!(/\s*[^A-Za-z0-9]\s*/, '-')
30
+
31
+ # convert double underscores to single
32
+ ret.gsub!(/-+/, '-')
33
+
34
+ # strip off leading/trailing dash
35
+ ret.gsub!(/\A-+|-+\z/, '')
36
+
37
+ ret.downcase
38
+ end
39
+
40
+ def to_s
41
+ "title: #{@title}, synopsis: #{@description}, image_url: #{@image_url}"
42
+ end
43
+
44
+ def as_json(_options = {})
45
+ {
46
+ id: @id,
47
+ slug: @slug,
48
+ title: @title,
49
+ image_url: @image_url,
50
+ search_query: @search_query
51
+ }
52
+ end
53
+
54
+ def to_json(*options)
55
+ as_json(*options).to_json(*options)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Organo
6
+ class Sequel
7
+ attr_accessor :id, :type, :title, :image_url, :selector
8
+
9
+ def initialize(id, title, image_url, season_num = 0, type = "", type_num = 0, selector = -1)
10
+ @id = id
11
+ @title = title.to_s
12
+ @image_url = image_url
13
+ @season_num = season_num
14
+ @type = type.to_s
15
+ @type_num = type_num
16
+ @selector = selector
17
+ end
18
+
19
+ def to_s
20
+ "id: #{@id}, title: #{@title}, image_url: #{@image_url}"
21
+ end
22
+
23
+ def as_json(_options = {})
24
+ {
25
+ id: @id,
26
+ title: @title,
27
+ image_url: @image_url,
28
+ season_num: @season_num.to_i,
29
+ type: @type,
30
+ type_num: @type_num.to_i,
31
+ selector: @selector.to_i
32
+ }
33
+ end
34
+
35
+ def to_json(*options)
36
+ as_json(*options).to_json(*options)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ class Show
6
+ attr_accessor :id, :slug, :title, :image_url, :search_query, :sequels
7
+
8
+ def initialize(id, slug, title, image_url, search_query, links = [], sequels = [])
9
+ @id = id
10
+ @slug = slug
11
+ @title = title.to_s
12
+ @image_url = image_url
13
+ @search_query = search_query.empty? ? title : search_query
14
+ @links = links.nil? ? [] : links
15
+ @sequels = sequels.nil? ? [] : sequels
16
+ end
17
+
18
+ def to_s
19
+ "id: #{@id}, title: #{@title}, image_url: #{@image_url}"
20
+ end
21
+
22
+ def as_json(_options = {})
23
+ {
24
+ id: @id,
25
+ slug: @slug,
26
+ title: @title,
27
+ image_url: @image_url,
28
+ sequels: @sequels,
29
+ search_query: @search_query
30
+ }
31
+ end
32
+
33
+ def to_json(*options)
34
+ as_json(*options).to_json(*options)
35
+ end
36
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require_relative 'search_show'
5
+ require_relative 'sequel'
6
+ require_relative 'query'
7
+
8
+ module Organo
9
+ class ShowList
10
+ def self.create_search_show_list(json_obj)
11
+ show_list = []
12
+ json_obj.each do |entry|
13
+ show = SearchShow.new(entry['mal_id'], entry['title'], entry['images']['jpg']['image_url'])
14
+ show_list.push show
15
+ end
16
+ show_list.sort_by(&:title)
17
+ end
18
+
19
+ def self.create_search_sequel_list(json_obj)
20
+ sequel_list = []
21
+ json_obj.each do |relations|
22
+ next unless relations['relation'] == 'Sequel'
23
+
24
+ relations['entry'].each do |entry|
25
+ image_url = Query.get_anime_image(entry['mal_id'])
26
+ sequel_list.push Sequel.new(entry['mal_id'], entry['name'], image_url)
27
+ end
28
+ end
29
+ sequel_list.sort_by(&:title)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Organo
4
+ VERSION = '0.3.0'
5
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Organo
6
+ class Writer
7
+ def self.to_data_file(obj, file_name)
8
+ file_path = "./#{file_name}.data.json"
9
+ File.write(file_path, JSON.pretty_generate(obj))
10
+ end
11
+
12
+ def self.to_file(obj, file_path)
13
+ File.write(file_path, JSON.pretty_generate(obj))
14
+ end
15
+ end
16
+ end
data/lib/organo.rb ADDED
@@ -0,0 +1,61 @@
1
+ require 'dry/cli'
2
+
3
+ require_relative 'organo/commands/init'
4
+ require_relative 'organo/commands/anime/anime_add'
5
+ require_relative 'organo/commands/anime/anime_add_sequel'
6
+ require_relative 'organo/commands/anime/anime_get_broken_links'
7
+ require_relative 'organo/commands/anime/anime_remove'
8
+ require_relative 'organo/commands/anime/anime_show'
9
+ require_relative 'organo/commands/anime/anime_search'
10
+ require_relative 'organo/commands/anime/anime_update_image'
11
+ require_relative 'organo/commands/anime/anime_update_query'
12
+ require_relative 'organo/commands/api/api_get_anime'
13
+ require_relative 'organo/commands/api/api_get_sequels'
14
+ require_relative 'organo/commands/api/api_get_season'
15
+ require_relative 'organo/commands/api/api_search_anime'
16
+ require_relative 'organo/commands/remote/remote_add'
17
+ require_relative 'organo/commands/remote/remote_remove'
18
+ require_relative 'organo/commands/remote/remote_download'
19
+ require_relative 'organo/commands/remote/remote_upload'
20
+ require_relative 'organo/commands/merge'
21
+ require_relative 'organo/commands/version'
22
+ require_relative 'organo/commands/statistics'
23
+ require_relative 'organo/commands/schedule/schedule_create'
24
+
25
+ module Organo
26
+ module CLI
27
+ module Commands
28
+ extend Dry::CLI::Registry
29
+
30
+ register 'version', Version, aliases: %w[v -v --version]
31
+ register 'init', Init
32
+ register 'anime' do |prefix|
33
+ prefix.register 'add', Anime::Add::AnimeEntry
34
+ prefix.register 'add sequel', Anime::Add::SequelEntry
35
+ prefix.register 'remove', Anime::Remove
36
+ prefix.register 'show', Anime::Show
37
+ prefix.register 'search', Anime::Search
38
+ prefix.register 'update image', Anime::Update::Image
39
+ prefix.register 'update search_query', Anime::Update::SearchQuery
40
+ prefix.register 'get broken_links', Anime::Get::BrokenLinks
41
+ end
42
+ register 'api' do |prefix|
43
+ prefix.register 'get anime', API::Get::Anime
44
+ prefix.register 'get season', API::Get::Season
45
+ prefix.register 'get sequels', API::Get::Sequels
46
+ prefix.register 'search anime', API::Search::Anime
47
+ end
48
+ register 'merge', Merge
49
+ register 'statistics', Statistics
50
+ register 'remote' do |prefix|
51
+ prefix.register 'add', Remote::Add
52
+ prefix.register 'remove', Remote::Remove
53
+ prefix.register 'download', Remote::Download
54
+ prefix.register 'upload', Remote::Upload
55
+ end
56
+ register 'schedule' do |prefix|
57
+ prefix.register 'create', Schedule::Create
58
+ end
59
+ end
60
+ end
61
+ end
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: organo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Louis-Philippe Fortin
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-02-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dry-cli
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: httparty
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.21'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.21'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rest-client
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.1'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.44'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.44'
69
+ description: Get JSON data from Jikan, an unofficial MyAnimeList API, that can be
70
+ used on an anime website
71
+ email:
72
+ - timemaster.lpf@gmail.com
73
+ executables:
74
+ - organo
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - exe/organo
82
+ - lib/organo.rb
83
+ - lib/organo/commands/anime/anime_add.rb
84
+ - lib/organo/commands/anime/anime_add_sequel.rb
85
+ - lib/organo/commands/anime/anime_get_broken_links.rb
86
+ - lib/organo/commands/anime/anime_remove.rb
87
+ - lib/organo/commands/anime/anime_search.rb
88
+ - lib/organo/commands/anime/anime_show.rb
89
+ - lib/organo/commands/anime/anime_update_image.rb
90
+ - lib/organo/commands/anime/anime_update_query.rb
91
+ - lib/organo/commands/api/api_get_anime.rb
92
+ - lib/organo/commands/api/api_get_season.rb
93
+ - lib/organo/commands/api/api_get_sequels.rb
94
+ - lib/organo/commands/api/api_search_anime.rb
95
+ - lib/organo/commands/init.rb
96
+ - lib/organo/commands/merge.rb
97
+ - lib/organo/commands/remote/remote_add.rb
98
+ - lib/organo/commands/remote/remote_download.rb
99
+ - lib/organo/commands/remote/remote_remove.rb
100
+ - lib/organo/commands/remote/remote_upload.rb
101
+ - lib/organo/commands/schedule/schedule_create.rb
102
+ - lib/organo/commands/statistics.rb
103
+ - lib/organo/commands/version.rb
104
+ - lib/organo/config.rb
105
+ - lib/organo/jikan_api.rb
106
+ - lib/organo/query.rb
107
+ - lib/organo/reader.rb
108
+ - lib/organo/request.rb
109
+ - lib/organo/search_show.rb
110
+ - lib/organo/sequel.rb
111
+ - lib/organo/show.rb
112
+ - lib/organo/show_list.rb
113
+ - lib/organo/version.rb
114
+ - lib/organo/writer.rb
115
+ homepage: https://gitlab.com/dkb-weeblets/organo
116
+ licenses:
117
+ - MIT
118
+ metadata:
119
+ homepage_uri: https://gitlab.com/dkb-weeblets/organo
120
+ source_code_uri: https://gitlab.com/dkb-weeblets/organo
121
+ rubygems_mfa_required: 'true'
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: 3.0.5
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubygems_version: 3.2.33
138
+ signing_key:
139
+ specification_version: 4
140
+ summary: Tool to build dataset from Jikan API
141
+ test_files: []