organo 0.3.0

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