mirror of
https://github.com/hkalexling/Mango.git
synced 2025-08-03 03:15:31 -04:00
Finish search for MD
This commit is contained in:
parent
011768ed1f
commit
a9a2c9faa8
@ -34,9 +34,11 @@
|
|||||||
.uk-card-body {
|
.uk-card-body {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
.uk-card-title {
|
.uk-card-title {
|
||||||
max-height: 3em;
|
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
.uk-card-title:not(.free-height) {
|
||||||
|
max-height: 3em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,9 +3,12 @@ const downloadComponent = () => {
|
|||||||
chaptersLimit: 1000,
|
chaptersLimit: 1000,
|
||||||
loading: false,
|
loading: false,
|
||||||
addingToDownload: false,
|
addingToDownload: false,
|
||||||
|
searchAvailable: false,
|
||||||
searchInput: '',
|
searchInput: '',
|
||||||
data: {},
|
data: {},
|
||||||
chapters: [],
|
chapters: [],
|
||||||
|
mangaAry: undefined, // undefined: not searching; []: searched but no result
|
||||||
|
candidateManga: {},
|
||||||
langChoice: 'All',
|
langChoice: 'All',
|
||||||
groupChoice: 'All',
|
groupChoice: 'All',
|
||||||
chapterRange: '',
|
chapterRange: '',
|
||||||
@ -48,7 +51,21 @@ const downloadComponent = () => {
|
|||||||
childList: true,
|
childList: true,
|
||||||
subtree: true
|
subtree: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$.getJSON(`${base_url}api/admin/mangadex/expires`)
|
||||||
|
.done((data) => {
|
||||||
|
if (data.error) {
|
||||||
|
alert('danger', 'Failed to check MangaDex integration status. Error: ' + data.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data.expires && data.expires > Math.floor(Date.now() / 1000))
|
||||||
|
this.searchAvailable = true;
|
||||||
|
})
|
||||||
|
.fail((jqXHR, status) => {
|
||||||
|
alert('danger', `Failed to check MangaDex integration status. Error: [${jqXHR.status}] ${jqXHR.statusText}`);
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
filtersUpdated() {
|
filtersUpdated() {
|
||||||
if (!this.data.chapters)
|
if (!this.data.chapters)
|
||||||
this.chapters = [];
|
this.chapters = [];
|
||||||
@ -90,10 +107,11 @@ const downloadComponent = () => {
|
|||||||
console.log('filtered chapters:', _chapters);
|
console.log('filtered chapters:', _chapters);
|
||||||
this.chapters = _chapters;
|
this.chapters = _chapters;
|
||||||
},
|
},
|
||||||
|
|
||||||
search() {
|
search() {
|
||||||
if (this.loading || this.searchInput === '') return;
|
if (this.loading || this.searchInput === '') return;
|
||||||
this.loading = true;
|
|
||||||
this.data = {};
|
this.data = {};
|
||||||
|
this.mangaAry = undefined;
|
||||||
|
|
||||||
var int_id = -1;
|
var int_id = -1;
|
||||||
try {
|
try {
|
||||||
@ -103,29 +121,54 @@ const downloadComponent = () => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
int_id = parseInt(this.searchInput);
|
int_id = parseInt(this.searchInput);
|
||||||
}
|
}
|
||||||
if (int_id <= 0 || isNaN(int_id)) {
|
|
||||||
alert('danger', 'Please make sure you are using a valid manga ID or manga URL from Mangadex.');
|
if (!isNaN(int_id) && int_id > 0) {
|
||||||
this.loading = false;
|
// The input is a positive integer. We treat it as an ID.
|
||||||
return;
|
this.loading = true;
|
||||||
|
$.getJSON(`${base_url}api/admin/mangadex/manga/${int_id}`)
|
||||||
|
.done((data) => {
|
||||||
|
if (data.error) {
|
||||||
|
alert('danger', 'Failed to get manga info. Error: ' + data.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data = data;
|
||||||
|
this.chapters = data.chapters;
|
||||||
|
this.mangaAry = undefined;
|
||||||
|
})
|
||||||
|
.fail((jqXHR, status) => {
|
||||||
|
alert('danger', `Failed to get manga info. Error: [${jqXHR.status}] ${jqXHR.statusText}`);
|
||||||
|
})
|
||||||
|
.always(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (!this.searchAvailable) {
|
||||||
|
alert('danger', 'Please make sure you are using a valid manga ID or manga URL from Mangadex. If you are trying to search MangaDex with a search term, please log in to MangaDex first by going to "Admin -> Connect to MangaDex"');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search as a search term
|
||||||
|
this.loading = true;
|
||||||
|
$.getJSON(`${base_url}api/admin/mangadex/search?${$.param({
|
||||||
|
query: this.searchInput
|
||||||
|
})}`)
|
||||||
|
.done((data) => {
|
||||||
|
if (data.error) {
|
||||||
|
alert('danger', `Failed to search MangaDex. Error: ${data.error}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.mangaAry = data.manga;
|
||||||
|
this.data = {};
|
||||||
|
})
|
||||||
|
.fail((jqXHR, status) => {
|
||||||
|
alert('danger', `Failed to search MangaDex. Error: [${jqXHR.status}] ${jqXHR.statusText}`);
|
||||||
|
})
|
||||||
|
.always(() => {
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$.getJSON(`${base_url}api/admin/mangadex/manga/${int_id}`)
|
|
||||||
.done((data) => {
|
|
||||||
if (data.error) {
|
|
||||||
alert('danger', 'Failed to get manga info. Error: ' + data.error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.data = data;
|
|
||||||
this.chapters = data.chapters;
|
|
||||||
})
|
|
||||||
.fail((jqXHR, status) => {
|
|
||||||
alert('danger', `Failed to get manga info. Error: [${jqXHR.status}] ${jqXHR.statusText}`);
|
|
||||||
})
|
|
||||||
.always(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
parseRange(str) {
|
parseRange(str) {
|
||||||
@ -228,6 +271,17 @@ const downloadComponent = () => {
|
|||||||
this.addingToDownload = false;
|
this.addingToDownload = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
chooseManga(manga) {
|
||||||
|
this.candidateManga = manga;
|
||||||
|
UIkit.modal($('#modal').get(0)).show();
|
||||||
|
},
|
||||||
|
|
||||||
|
confirmManga(id) {
|
||||||
|
UIkit.modal($('#modal').get(0)).hide();
|
||||||
|
this.searchInput = id;
|
||||||
|
this.search();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -54,7 +54,7 @@ shards:
|
|||||||
|
|
||||||
mangadex:
|
mangadex:
|
||||||
git: https://github.com/hkalexling/mangadex.git
|
git: https://github.com/hkalexling/mangadex.git
|
||||||
version: 0.6.0+git.commit.d87a1d7a6e84d122814b38618954dcc73fc5553b
|
version: 0.8.0+git.commit.24e6fb51afd043721139355854e305b43bf98c43
|
||||||
|
|
||||||
mg:
|
mg:
|
||||||
git: https://github.com/hkalexling/mg.git
|
git: https://github.com/hkalexling/mg.git
|
||||||
|
@ -57,14 +57,16 @@ struct APIRouter
|
|||||||
}
|
}
|
||||||
|
|
||||||
Koa.schema("mdChapter", {
|
Koa.schema("mdChapter", {
|
||||||
|
"id" => Int64,
|
||||||
"group" => {} of String => String,
|
"group" => {} of String => String,
|
||||||
}.merge(s %w(id title volume chapter language full_title time
|
}.merge(s %w(title volume chapter language full_title time
|
||||||
manga_title manga_id)),
|
manga_title manga_id)),
|
||||||
desc: "A MangaDex chapter")
|
desc: "A MangaDex chapter")
|
||||||
|
|
||||||
Koa.schema "mdManga", {
|
Koa.schema "mdManga", {
|
||||||
|
"id" => Int64,
|
||||||
"chapters" => ["mdChapter"],
|
"chapters" => ["mdChapter"],
|
||||||
}.merge(s %w(id title description author artist cover_url)),
|
}.merge(s %w(title description author artist cover_url)),
|
||||||
desc: "A MangaDex manga"
|
desc: "A MangaDex manga"
|
||||||
|
|
||||||
Koa.describe "Returns a page in a manga entry"
|
Koa.describe "Returns a page in a manga entry"
|
||||||
@ -944,6 +946,44 @@ struct APIRouter
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Koa.describe "Searches MangaDex for manga matching `query`", <<-MD
|
||||||
|
Returns an empty list if the current user hasn't logged in to MangaDex.
|
||||||
|
MD
|
||||||
|
Koa.query "query"
|
||||||
|
Koa.response 200, schema: {
|
||||||
|
"success" => Bool,
|
||||||
|
"error" => String?,
|
||||||
|
"manga?" => [{
|
||||||
|
"id" => Int64,
|
||||||
|
"title" => String,
|
||||||
|
"description" => String,
|
||||||
|
"mainCover" => String,
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
Koa.tags ["admin", "mangadex"]
|
||||||
|
get "/api/admin/mangadex/search" do |env|
|
||||||
|
begin
|
||||||
|
token, expires = Storage.default.get_md_token get_username env
|
||||||
|
client = MangaDex::Client.from_config
|
||||||
|
client.token = token
|
||||||
|
client.token_expires = expires
|
||||||
|
|
||||||
|
query = env.params.query["query"]
|
||||||
|
|
||||||
|
send_json env, {
|
||||||
|
"success" => true,
|
||||||
|
"error" => nil,
|
||||||
|
"manga" => client.partial_search query,
|
||||||
|
}.to_json
|
||||||
|
rescue e
|
||||||
|
Logger.error e
|
||||||
|
send_json env, {
|
||||||
|
"success" => false,
|
||||||
|
"error" => e.message,
|
||||||
|
}.to_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
doc = Koa.generate
|
doc = Koa.generate
|
||||||
@@api_json = doc.to_json if doc
|
@@api_json = doc.to_json if doc
|
||||||
|
|
||||||
|
@ -530,9 +530,9 @@ class Storage
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_md_token(username) : Tuple(String?, Time?)
|
def get_md_token(username) : Tuple(String?, Time)
|
||||||
token = nil
|
token = nil
|
||||||
expires = nil
|
expires = Time.utc
|
||||||
MainFiber.run do
|
MainFiber.run do
|
||||||
get_db do |db|
|
get_db do |db|
|
||||||
db.query_one? "select token, expire from md_account where " \
|
db.query_one? "select token, expire from md_account where " \
|
||||||
|
@ -1,17 +1,39 @@
|
|||||||
<h2 class=uk-title>Download from MangaDex</h2>
|
<h2 class=uk-title>Download from MangaDex</h2>
|
||||||
<div x-data="downloadComponent()" x-init="init()">
|
<div x-data="downloadComponent()" x-init="init()">
|
||||||
<div class="uk-grid-small" uk-grid>
|
<div class="uk-grid-small" uk-grid style="margin-bottom:40px;">
|
||||||
<div class="uk-width-3-4">
|
<div class="uk-width-expand">
|
||||||
<input class="uk-input" type="text" placeholder="MangaDex manga ID or URL" x-model="searchInput" @keydown.enter.debounce="search()">
|
<input class="uk-input" type="text" :placeholder="searchAvailable ? 'Search MangaDex or enter a manga ID/URL' : 'MangaDex manga ID or URL'" x-model="searchInput" @keydown.enter.debounce="search()">
|
||||||
</div>
|
</div>
|
||||||
<div class="uk-width-1-4">
|
<div class="uk-width-auto">
|
||||||
<div uk-spinner class="uk-align-center" x-show="loading" x-cloak></div>
|
<div uk-spinner class="uk-align-center" x-show="loading" x-cloak></div>
|
||||||
<button class="uk-button uk-button-default" x-show="!loading" @click="search()">Search</button>
|
<button class="uk-button uk-button-default" x-show="!loading" @click="search()">Search</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<template x-if="mangaAry">
|
||||||
|
<div>
|
||||||
|
<p x-show="mangaAry.length === 0">No matching manga found.</p>
|
||||||
|
|
||||||
|
<div class="uk-child-width-1-4@m uk-child-width-1-2" uk-grid>
|
||||||
|
<template x-for="manga in mangaAry" :key="manga.id">
|
||||||
|
<div class="item" :data-id="manga.id" @click="chooseManga(manga)">
|
||||||
|
<div class="uk-card uk-card-default">
|
||||||
|
<div class="uk-card-media-top uk-inline">
|
||||||
|
<img uk-img :data-src="manga.mainCover">
|
||||||
|
</div>
|
||||||
|
<div class="uk-card-body">
|
||||||
|
<h3 class="uk-card-title break-word uk-margin-remove-bottom free-height" x-text="manga.title"></h3>
|
||||||
|
<p class="uk-text-meta" x-text="`ID: ${manga.id}`"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<div x-show="data && data.chapters" x-cloak>
|
<div x-show="data && data.chapters" x-cloak>
|
||||||
<div class"uk-grid-small" uk-grid style="margin-top:40px">
|
<div class"uk-grid-small" uk-grid>
|
||||||
<div class="uk-width-1-4@s">
|
<div class="uk-width-1-4@s">
|
||||||
<img :src="data.mainCover">
|
<img :src="data.mainCover">
|
||||||
</div>
|
</div>
|
||||||
@ -107,6 +129,29 @@
|
|||||||
</template>
|
</template>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="modal" class="uk-flex-top" uk-modal="container: false">
|
||||||
|
<div class="uk-modal-dialog uk-margin-auto-vertical">
|
||||||
|
<button class="uk-modal-close-default" type="button" uk-close></button>
|
||||||
|
<div class="uk-modal-header">
|
||||||
|
<h3 class="uk-modal-title break-word" x-text="candidateManga.title"></h3>
|
||||||
|
</div>
|
||||||
|
<div class="uk-modal-body">
|
||||||
|
<div class="uk-grid">
|
||||||
|
<div class="uk-width-1-3@s">
|
||||||
|
<img uk-img data-width data-height :src="candidateManga.mainCover" style="width:100%;margin-bottom:10px;">
|
||||||
|
<a :href="`<%= mangadex_base_url %>/manga/${candidateManga.id}`" x-text="`ID: ${candidateManga.id}`" class="uk-link-muted"></a>
|
||||||
|
</div>
|
||||||
|
<div class="uk-width-2-3@s" uk-overflow-auto>
|
||||||
|
<p x-text="candidateManga.description"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uk-modal-footer">
|
||||||
|
<button class="uk-button uk-button-primary" type="button" @click="confirmManga(candidateManga.id)">Choose</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% content_for "script" do %>
|
<% content_for "script" do %>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user