mirror of
https://github.com/hkalexling/Mango.git
synced 2025-08-05 04:15:35 -04:00
WIP
This commit is contained in:
parent
a571d21cba
commit
e0713ccde8
42
public/js/plugin-download-2.js
Normal file
42
public/js/plugin-download-2.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
const component = () => {
|
||||||
|
return {
|
||||||
|
plugins: [],
|
||||||
|
info: undefined,
|
||||||
|
pid: undefined,
|
||||||
|
init() {
|
||||||
|
fetch(`${base_url}api/admin/plugin`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (!data.success) {
|
||||||
|
alert('danger', `Failed to list the available plugins. Error: ${data.error}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.plugins = data.plugins;
|
||||||
|
|
||||||
|
const pid = localStorage.getItem('plugin');
|
||||||
|
if (pid && this.plugins.map(p => p.id).includes(pid))
|
||||||
|
return this.loadPlugin(pid);
|
||||||
|
|
||||||
|
if (this.plugins.length > 0)
|
||||||
|
this.loadPlugin(this.plugins[0].id);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadPlugin(pid) {
|
||||||
|
fetch(`${base_url}api/admin/plugin/info?${new URLSearchParams({
|
||||||
|
plugin: pid
|
||||||
|
})}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (!data.success) {
|
||||||
|
alert('danger', `Failed to get plugin metadata. Error: ${data.error}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.info = data.info;
|
||||||
|
this.pid = pid;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
pluginChanged() {
|
||||||
|
this.loadPlugin(this.pid);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
@ -16,6 +16,8 @@ class Plugin
|
|||||||
end
|
end
|
||||||
|
|
||||||
struct Info
|
struct Info
|
||||||
|
include JSON::Serializable
|
||||||
|
|
||||||
{% for name in ["id", "title", "placeholder"] %}
|
{% for name in ["id", "title", "placeholder"] %}
|
||||||
getter {{name.id}} = ""
|
getter {{name.id}} = ""
|
||||||
{% end %}
|
{% end %}
|
||||||
@ -24,6 +26,9 @@ class Plugin
|
|||||||
getter settings = {} of String => String?
|
getter settings = {} of String => String?
|
||||||
getter dir : String
|
getter dir : String
|
||||||
|
|
||||||
|
@[JSON::Field(ignore: true)]
|
||||||
|
@json : JSON::Any
|
||||||
|
|
||||||
def initialize(@dir)
|
def initialize(@dir)
|
||||||
info_path = File.join @dir, "info.json"
|
info_path = File.join @dir, "info.json"
|
||||||
|
|
||||||
@ -150,6 +155,12 @@ class Plugin
|
|||||||
sbx.push_string path
|
sbx.push_string path
|
||||||
sbx.put_prop_string -2, "storage_path"
|
sbx.put_prop_string -2, "storage_path"
|
||||||
|
|
||||||
|
sbx.push_pointer info.dir.as(Void*)
|
||||||
|
path = sbx.require_pointer(-1).as String
|
||||||
|
sbx.pop
|
||||||
|
sbx.push_string path
|
||||||
|
sbx.put_prop_string -2, "info_dir"
|
||||||
|
|
||||||
def_helper_functions sbx
|
def_helper_functions sbx
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -164,11 +175,36 @@ class Plugin
|
|||||||
{% end %}
|
{% end %}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def assert_manga_type(obj : JSON::Any)
|
||||||
|
obj["id"].as_s && obj["title"].as_s
|
||||||
|
rescue e
|
||||||
|
raise Error.new "Missing required fields in the Manga type"
|
||||||
|
end
|
||||||
|
|
||||||
|
def assert_chapter_type(obj : JSON::Any)
|
||||||
|
obj["id"].as_s && obj["title"].as_s && obj["pages"].as_i &&
|
||||||
|
obj["manga_title"].as_s
|
||||||
|
rescue e
|
||||||
|
raise Error.new "Missing required fields in the Chapter type"
|
||||||
|
end
|
||||||
|
|
||||||
|
def assert_page_type(obj : JSON::Any)
|
||||||
|
obj["url"].as_s && obj["filename"].as_s
|
||||||
|
rescue e
|
||||||
|
raise Error.new "Missing required fields in the Page type"
|
||||||
|
end
|
||||||
|
|
||||||
def search_manga(query : String)
|
def search_manga(query : String)
|
||||||
|
if info.version == 1
|
||||||
|
raise Error.new "Manga searching is only available for plugins targeting API " \
|
||||||
|
"v2 or above"
|
||||||
|
end
|
||||||
json = eval_json "searchManga('#{query}')"
|
json = eval_json "searchManga('#{query}')"
|
||||||
begin
|
begin
|
||||||
check_fields ["id", "title"]
|
json.as_a.each do |obj|
|
||||||
rescue
|
assert_manga_type obj
|
||||||
|
end
|
||||||
|
rescue e
|
||||||
raise Error.new e.message
|
raise Error.new e.message
|
||||||
end
|
end
|
||||||
json
|
json
|
||||||
@ -180,11 +216,7 @@ class Plugin
|
|||||||
if info.version > 1
|
if info.version > 1
|
||||||
# Since v2, listChapters returns an array
|
# Since v2, listChapters returns an array
|
||||||
json.as_a.each do |obj|
|
json.as_a.each do |obj|
|
||||||
{% for field in %w(id title pages manga_title) %}
|
assert_chapter_type obj
|
||||||
unless obj[{{field}}]?
|
|
||||||
raise "Field `{{field.id}}` is required in the chapter objects"
|
|
||||||
end
|
|
||||||
{% end %}
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
check_fields ["title", "chapters"]
|
check_fields ["title", "chapters"]
|
||||||
@ -200,7 +232,9 @@ class Plugin
|
|||||||
end
|
end
|
||||||
|
|
||||||
title = obj["title"]?
|
title = obj["title"]?
|
||||||
raise "Field `title` missing from `listChapters` outputs" if title.nil?
|
if title.nil?
|
||||||
|
raise "Field `title` missing from `listChapters` outputs"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
rescue e
|
rescue e
|
||||||
@ -212,11 +246,15 @@ class Plugin
|
|||||||
def select_chapter(id : String)
|
def select_chapter(id : String)
|
||||||
json = eval_json "selectChapter('#{id}')"
|
json = eval_json "selectChapter('#{id}')"
|
||||||
begin
|
begin
|
||||||
|
if info.version > 1
|
||||||
|
assert_chapter_type json
|
||||||
|
else
|
||||||
check_fields ["title", "pages"]
|
check_fields ["title", "pages"]
|
||||||
|
|
||||||
if json["title"].to_s.empty?
|
if json["title"].to_s.empty?
|
||||||
raise "The `title` field of the chapter can not be empty"
|
raise "The `title` field of the chapter can not be empty"
|
||||||
end
|
end
|
||||||
|
end
|
||||||
rescue e
|
rescue e
|
||||||
raise Error.new e.message
|
raise Error.new e.message
|
||||||
end
|
end
|
||||||
@ -227,7 +265,19 @@ class Plugin
|
|||||||
json = eval_json "nextPage()"
|
json = eval_json "nextPage()"
|
||||||
return if json.size == 0
|
return if json.size == 0
|
||||||
begin
|
begin
|
||||||
check_fields ["filename", "url"]
|
assert_page_type json
|
||||||
|
rescue e
|
||||||
|
raise Error.new e.message
|
||||||
|
end
|
||||||
|
json
|
||||||
|
end
|
||||||
|
|
||||||
|
def new_chapters(manga_id : String, after : Int64)
|
||||||
|
json = eval_json "newChapters('#{manga_id}', #{after})"
|
||||||
|
begin
|
||||||
|
json.as_a.each do |obj|
|
||||||
|
assert_chapter_type obj
|
||||||
|
end
|
||||||
rescue e
|
rescue e
|
||||||
raise Error.new e.message
|
raise Error.new e.message
|
||||||
end
|
end
|
||||||
@ -412,10 +462,16 @@ class Plugin
|
|||||||
end
|
end
|
||||||
sbx.put_prop_string -2, "storage"
|
sbx.put_prop_string -2, "storage"
|
||||||
|
|
||||||
|
if info.version > 1
|
||||||
sbx.push_proc 1 do |ptr|
|
sbx.push_proc 1 do |ptr|
|
||||||
env = Duktape::Sandbox.new ptr
|
env = Duktape::Sandbox.new ptr
|
||||||
key = env.require_string 0
|
key = env.require_string 0
|
||||||
|
|
||||||
|
env.get_global_string "info_dir"
|
||||||
|
info_dir = env.require_string -1
|
||||||
|
env.pop
|
||||||
|
info = Info.new info_dir
|
||||||
|
|
||||||
if value = info.settings[key]?
|
if value = info.settings[key]?
|
||||||
env.push_string value
|
env.push_string value
|
||||||
else
|
else
|
||||||
@ -425,6 +481,7 @@ class Plugin
|
|||||||
env.call_success
|
env.call_success
|
||||||
end
|
end
|
||||||
sbx.put_prop_string -2, "settings"
|
sbx.put_prop_string -2, "settings"
|
||||||
|
end
|
||||||
|
|
||||||
sbx.put_prop_string -2, "mango"
|
sbx.put_prop_string -2, "mango"
|
||||||
end
|
end
|
||||||
|
@ -539,6 +539,97 @@ struct APIRouter
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Koa.describe "Returns a list of available plugins"
|
||||||
|
Koa.tags ["admin", "downloader"]
|
||||||
|
Koa.query "plugin", schema: String
|
||||||
|
Koa.response 200, schema: {
|
||||||
|
"success" => Bool,
|
||||||
|
"error" => String?,
|
||||||
|
"plugins" => [{
|
||||||
|
"id" => String,
|
||||||
|
"title" => String,
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
get "/api/admin/plugin" do |env|
|
||||||
|
begin
|
||||||
|
send_json env, {
|
||||||
|
"success" => true,
|
||||||
|
"plugins" => Plugin.list,
|
||||||
|
}.to_json
|
||||||
|
rescue e
|
||||||
|
Logger.error e
|
||||||
|
send_json env, {
|
||||||
|
"success" => false,
|
||||||
|
"error" => e.message,
|
||||||
|
}.to_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Koa.describe "Returns the metadata of a plugin"
|
||||||
|
Koa.tags ["admin", "downloader"]
|
||||||
|
Koa.query "plugin", schema: String
|
||||||
|
Koa.response 200, schema: {
|
||||||
|
"success" => Bool,
|
||||||
|
"error" => String?,
|
||||||
|
"info" => {
|
||||||
|
"dir" => String,
|
||||||
|
"id" => String,
|
||||||
|
"title" => String,
|
||||||
|
"placeholder" => String,
|
||||||
|
"wait_seconds" => Int32,
|
||||||
|
"version" => Int32,
|
||||||
|
"settings" => {} of String => String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
get "/api/admin/plugin/info" do |env|
|
||||||
|
begin
|
||||||
|
plugin = Plugin.new env.params.query["plugin"].as String
|
||||||
|
send_json env, {
|
||||||
|
"success" => true,
|
||||||
|
"info" => plugin.info,
|
||||||
|
}.to_json
|
||||||
|
rescue e
|
||||||
|
Logger.error e
|
||||||
|
send_json env, {
|
||||||
|
"success" => false,
|
||||||
|
"error" => e.message,
|
||||||
|
}.to_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Koa.describe "Searches for manga matching the given query from a plugin", <<-MD
|
||||||
|
Only available for plugins targeting API v2 or above.
|
||||||
|
MD
|
||||||
|
Koa.tags ["admin", "downloader"]
|
||||||
|
Koa.query "plugin", schema: String
|
||||||
|
Koa.query "query", schema: String
|
||||||
|
Koa.response 200, schema: {
|
||||||
|
"success" => Bool,
|
||||||
|
"error" => String?,
|
||||||
|
"manga" => [{
|
||||||
|
"id" => String,
|
||||||
|
"title" => String,
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
get "/api/admin/plugin/search" do |env|
|
||||||
|
begin
|
||||||
|
query = env.params.query["query"].as String
|
||||||
|
plugin = Plugin.new env.params.query["plugin"].as String
|
||||||
|
|
||||||
|
manga_ary = plugin.search_manga(query).as_a
|
||||||
|
send_json env, {
|
||||||
|
"success" => true,
|
||||||
|
"manga" => manga_ary,
|
||||||
|
}.to_json
|
||||||
|
rescue e
|
||||||
|
Logger.error e
|
||||||
|
send_json env, {
|
||||||
|
"success" => false,
|
||||||
|
"error" => e.message,
|
||||||
|
}.to_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
Koa.describe "Lists the chapters in a title from a plugin"
|
Koa.describe "Lists the chapters in a title from a plugin"
|
||||||
Koa.tags ["admin", "downloader"]
|
Koa.tags ["admin", "downloader"]
|
||||||
Koa.query "plugin", schema: String
|
Koa.query "plugin", schema: String
|
||||||
|
@ -96,6 +96,15 @@ struct MainRouter
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
get "/download/plugins2" do |env|
|
||||||
|
begin
|
||||||
|
layout "plugin-download-2"
|
||||||
|
rescue e
|
||||||
|
Logger.error e
|
||||||
|
env.response.status_code = 500
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
get "/download/subscription" do |env|
|
get "/download/subscription" do |env|
|
||||||
mangadex_base_url = Config.current.mangadex["base_url"]
|
mangadex_base_url = Config.current.mangadex["base_url"]
|
||||||
username = get_username env
|
username = get_username env
|
||||||
|
61
src/views/plugin-download-2.html.ecr
Normal file
61
src/views/plugin-download-2.html.ecr
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<div x-data="component()" x-init="init()">
|
||||||
|
<div class="uk-grid-small" uk-grid style="margin-bottom:40px;">
|
||||||
|
<div class="uk-container uk-text-center" x-show="plugins.length === 0">
|
||||||
|
<h2>No Plugins Found</h2>
|
||||||
|
<p>We could't find any plugins in the directory <code><%= Config.current.plugin_path %></code>.</p>
|
||||||
|
<p>You can download official plugins from the <a href="https://github.com/hkalexling/mango-plugins">Mango plugins repository</a>.</p>
|
||||||
|
</div>
|
||||||
|
<div x-show="plugins.length > 0" style="width:100%">
|
||||||
|
<h2 class=uk-title>Download with Plugins</h2>
|
||||||
|
|
||||||
|
<template x-if="info !== undefined">
|
||||||
|
<div>
|
||||||
|
<div class="uk-grid-small" uk-grid>
|
||||||
|
<div class="uk-width-3-4@m uk-child-width-1-1">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<label class="uk-form-label"> </label>
|
||||||
|
<input class="uk-input" type="text" :placeholder="info.placeholder">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-expand">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label">Choose a plugin</label>
|
||||||
|
<div class="uk-form-controls">
|
||||||
|
<select class="uk-select" x-model="pid" @change="pluginChanged()">
|
||||||
|
<template x-for="p in plugins" :key="p">
|
||||||
|
<option :value="p.id" x-text="p.title"></option>
|
||||||
|
</template>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-auto">
|
||||||
|
<div class="uk-margin">
|
||||||
|
<label class="uk-form-label"> </label>
|
||||||
|
<div class="uk-form-controls" style="padding-top: 10px;">
|
||||||
|
<span uk-icon="info" uk-toggle="target: #toggle"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template x-for="entry, idx in Object.entries(info).filter(tp => !['id', 'settings'].includes(tp[0]))" :key="idx">
|
||||||
|
<dl class="uk-description-list" id="toggle" hidden>
|
||||||
|
<dt x-text="entry[0]"></dt>
|
||||||
|
<dd x-text="entry[1]"></dd>
|
||||||
|
</dl>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% content_for "script" do %>
|
||||||
|
<%= render_component "moment" %>
|
||||||
|
<%= render_component "jquery-ui" %>
|
||||||
|
<script src="<%= base_url %>js/alert.js"></script>
|
||||||
|
<script src="<%= base_url %>js/plugin-download-2.js"></script>
|
||||||
|
<% end %>
|
Loading…
x
Reference in New Issue
Block a user