Add anilist plugin (#69)
This commit is contained in:
69
README.md
69
README.md
@@ -184,16 +184,30 @@ But there's more with [plugins](https://github.com/lowlighter/metrics/tree/maste
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th><a href="#-anilist">🌸 Anilist plugin</a></th>
|
||||||
<th><a href="#%EF%B8%8F-base-content">🗃️ Header special features</a></th>
|
<th><a href="#%EF%B8%8F-base-content">🗃️ Header special features</a></th>
|
||||||
<th></th>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="#-anilist">
|
||||||
|
<img src="https://github.com/lowlighter/lowlighter/blob/master/metrics.plugin.anilist.svg" alt="" width="400">
|
||||||
|
</a>
|
||||||
|
<details><summary>Manga version</summary>
|
||||||
|
<a href="#-anilist">
|
||||||
|
<img src="https://github.com/lowlighter/lowlighter/blob/master/metrics.plugin.anilist.manga.svg" alt="" width="400">
|
||||||
|
</a>
|
||||||
|
</details>
|
||||||
|
<details open><summary>Favorites characters version</summary>
|
||||||
|
<a href="#-anilist">
|
||||||
|
<img src="https://github.com/lowlighter/lowlighter/blob/master/metrics.plugin.anilist.characters.svg" alt="" width="400">
|
||||||
|
</a>
|
||||||
|
</details>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="#%EF%B8%8F-base-content">
|
<a href="#%EF%B8%8F-base-content">
|
||||||
<img src="https://github.com/lowlighter/lowlighter/blob/master/metrics.header.svg" alt="" width="400">
|
<img src="https://github.com/lowlighter/lowlighter/blob/master/metrics.header.svg" alt="" width="400">
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2" align="center">
|
<td colspan="2" align="center">
|
||||||
@@ -558,6 +572,7 @@ The default template is `classic`.
|
|||||||
<th><span title="Stargazers">✨</span></th>
|
<th><span title="Stargazers">✨</span></th>
|
||||||
<th><span title="Gists">🎫</span></th>
|
<th><span title="Gists">🎫</span></th>
|
||||||
<th><span title="People">🧑🤝🧑</span></th>
|
<th><span title="People">🧑🤝🧑</span></th>
|
||||||
|
<th><span title="Anilist">🌸</span></th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Classic</th>
|
<th>Classic</th>
|
||||||
@@ -579,6 +594,7 @@ The default template is `classic`.
|
|||||||
<td data-for="stargazers">✔️</td>
|
<td data-for="stargazers">✔️</td>
|
||||||
<td data-for="gists"><span title="100+ gists support on @master">✔️<sup>N</sup></span></td>
|
<td data-for="gists"><span title="100+ gists support on @master">✔️<sup>N</sup></span></td>
|
||||||
<td data-for="people">✔️</td>
|
<td data-for="people">✔️</td>
|
||||||
|
<td data-for="anilist"><span title="Available on @master">✔️<sup>M</sup></span></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Terminal</th>
|
<th>Terminal</th>
|
||||||
@@ -600,6 +616,7 @@ The default template is `classic`.
|
|||||||
<td data-for="stargazers">❌</td>
|
<td data-for="stargazers">❌</td>
|
||||||
<td data-for="gists"><span title="100+ gists support on @master">✔️<sup>N</sup></span></td>
|
<td data-for="gists"><span title="100+ gists support on @master">✔️<sup>N</sup></span></td>
|
||||||
<td data-for="people">❌</td>
|
<td data-for="people">❌</td>
|
||||||
|
<td data-for="anilist">❌</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Repository<sup>R</sup></th>
|
<th>Repository<sup>R</sup></th>
|
||||||
@@ -621,6 +638,7 @@ The default template is `classic`.
|
|||||||
<td data-for="stargazers">✔️</td>
|
<td data-for="stargazers">✔️</td>
|
||||||
<td data-for="gists">❌</td>
|
<td data-for="gists">❌</td>
|
||||||
<td data-for="people">❌</td>
|
<td data-for="people">❌</td>
|
||||||
|
<td data-for="anilist">❌</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
@@ -1541,6 +1559,53 @@ It is possible to use [identicons](https://github.blog/2013-08-14-identicons/) i
|
|||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
### 🌸 Anilist
|
||||||
|
|
||||||
|
🚧 This feature is available as pre-release on @master branch (unstable)
|
||||||
|
|
||||||
|
The *anilist* plugin lets you display your favorites animes, mangas and characters from [AniList](https://anilist.co) data.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
ℹ️ This plugin significantly increase file size, it is advised to run it as standalone
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>💬 About</summary>
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
This plugin is composed of the following sections, which can be displayed or hidden through `plugin_anilist_sections` option:
|
||||||
|
- `favorites` will display your favorites mangas and animes
|
||||||
|
- `watching` will display animes currently in your watching list
|
||||||
|
- `reading` will display manga currently in your reading list
|
||||||
|
- `characters` will display characters you liked
|
||||||
|
|
||||||
|
These sections can also be filtered by media type, which can be either `anime`, `manga` or both.
|
||||||
|
|
||||||
|
Add the following to your workflow:
|
||||||
|
```yaml
|
||||||
|
- uses: lowlighter/metrics@master
|
||||||
|
with:
|
||||||
|
# ... other options
|
||||||
|
plugin_anilist: yes
|
||||||
|
plugin_anilist_medias: anime, manga
|
||||||
|
plugin_anilist_sections: favorites, watching, reading, characters
|
||||||
|
plugin_anilist_limit: 2
|
||||||
|
plugin_anilist_shuffle: yes # Shuffle data from AniList for varied outputs
|
||||||
|
```
|
||||||
|
|
||||||
|
It is possible to use a different username from your GitHub account by using `plugin_anilist_user` option.
|
||||||
|
|
||||||
|
Add the following to your workflow:
|
||||||
|
```yaml
|
||||||
|
- uses: lowlighter/metrics@master
|
||||||
|
with:
|
||||||
|
# ... other options
|
||||||
|
plugin_anilist_user: ********
|
||||||
|
```
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
### 🔧 Other options
|
### 🔧 Other options
|
||||||
|
|
||||||
A few additional options are available.
|
A few additional options are available.
|
||||||
|
|||||||
40
action.yml
40
action.yml
@@ -482,6 +482,46 @@ inputs:
|
|||||||
description: Use identicons instead of real avatars
|
description: Use identicons instead of real avatars
|
||||||
default: no
|
default: no
|
||||||
|
|
||||||
|
# Display your favorites animes and mangas from AniList
|
||||||
|
plugin_anilist:
|
||||||
|
description: Display your favorites animes and mangas from AniList
|
||||||
|
default: no
|
||||||
|
|
||||||
|
# Medias to display from AniList (comma-separated list)
|
||||||
|
# Supported values are:
|
||||||
|
# - "anime"
|
||||||
|
# - "manga"
|
||||||
|
plugin_anilist_medias:
|
||||||
|
description: Medias to display from AniList data
|
||||||
|
default: anime, manga
|
||||||
|
|
||||||
|
# Sections to display from AniList data (comma-separated list)
|
||||||
|
# Values in "plugin_anilist_medias" may also impact displayed sections
|
||||||
|
# Supported values are:
|
||||||
|
# - "favorites" for favorites animes/mangas
|
||||||
|
# - "watching" for currently watched animes
|
||||||
|
# - "reading" for currently read mangas
|
||||||
|
# - "characters" for favorites characters
|
||||||
|
plugin_anilist_sections:
|
||||||
|
description: Sections to display from AniList data
|
||||||
|
default: favorites
|
||||||
|
|
||||||
|
# Maximum number of medias to display per section from AniList Data
|
||||||
|
plugin_anilist_limit:
|
||||||
|
description: Medias to display
|
||||||
|
default: 2
|
||||||
|
|
||||||
|
# Shuffle AniList data
|
||||||
|
plugin_anilist_shuffle:
|
||||||
|
description: Shuffle AniList data
|
||||||
|
default: yes
|
||||||
|
|
||||||
|
# Username on AniList
|
||||||
|
# Default to GitHub username
|
||||||
|
plugin_anilist_user:
|
||||||
|
description: AniList login
|
||||||
|
default: ""
|
||||||
|
|
||||||
# ====================================================================================
|
# ====================================================================================
|
||||||
# Options below are mostly used for testing
|
# Options below are mostly used for testing
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,9 @@
|
|||||||
},
|
},
|
||||||
"people":{ "//":"People plugin",
|
"people":{ "//":"People plugin",
|
||||||
"enabled":false, "//":"Enable or disable people display"
|
"enabled":false, "//":"Enable or disable people display"
|
||||||
|
},
|
||||||
|
"anilist":{ "//":"Anilist plugin",
|
||||||
|
"enabled":false, "//":"Enable or disable anilist display"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,6 +151,7 @@
|
|||||||
stargazers:{enabled:input.bool("plugin_stargazers")},
|
stargazers:{enabled:input.bool("plugin_stargazers")},
|
||||||
activity:{enabled:input.bool("plugin_activity")},
|
activity:{enabled:input.bool("plugin_activity")},
|
||||||
people:{enabled:input.bool("plugin_people")},
|
people:{enabled:input.bool("plugin_people")},
|
||||||
|
anilist:{enabled:input.bool("plugin_anilist")},
|
||||||
}
|
}
|
||||||
let q = Object.fromEntries(Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => [key, true]))
|
let q = Object.fromEntries(Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => [key, true]))
|
||||||
info("Plugins enabled", Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => key))
|
info("Plugins enabled", Object.entries(plugins).filter(([key, plugin]) => plugin.enabled).map(([key]) => key))
|
||||||
@@ -243,6 +244,17 @@
|
|||||||
for (const option of ["identicons"])
|
for (const option of ["identicons"])
|
||||||
info(`People ${option}`, q[`people.${option}`] = input.bool(`plugin_people_${option}`))
|
info(`People ${option}`, q[`people.${option}`] = input.bool(`plugin_people_${option}`))
|
||||||
}
|
}
|
||||||
|
//Anilist
|
||||||
|
if (plugins.anilist.enabled) {
|
||||||
|
for (const option of ["limit"])
|
||||||
|
info(`Anilist ${option}`, q[`anilist.${option}`] = input.number(`plugin_anilist_${option}`))
|
||||||
|
for (const option of ["medias", "sections"])
|
||||||
|
info(`Anilist ${option}`, q[`anilist.${option}`] = input.array(`plugin_anilist_${option}`))
|
||||||
|
for (const option of ["shuffle"])
|
||||||
|
info(`Anilist ${option}`, q[`anilist.${option}`] = input.bool(`plugin_anilist_${option}`))
|
||||||
|
for (const option of ["user"])
|
||||||
|
info(`Anilist ${option}`, q[`anilist.${option}`] = input.string(`plugin_anilist_${option}`))
|
||||||
|
}
|
||||||
|
|
||||||
//Repositories to use
|
//Repositories to use
|
||||||
const repositories = input.number("repositories")
|
const repositories = input.number("repositories")
|
||||||
|
|||||||
@@ -68,7 +68,7 @@
|
|||||||
//Compute metrics
|
//Compute metrics
|
||||||
console.debug(`metrics/compute/${login} > compute`)
|
console.debug(`metrics/compute/${login} > compute`)
|
||||||
const computer = Templates[template].default || Templates[template]
|
const computer = Templates[template].default || Templates[template]
|
||||||
await computer({login, q, dflags}, {conf, data, rest, graphql, plugins, queries}, {s, pending, imports:{plugins:Plugins, url, imgb64, axios, puppeteer, run, fs, os, paths, util, format, bytes, shuffle, htmlescape, urlexpand}})
|
await computer({login, q, dflags}, {conf, data, rest, graphql, plugins, queries}, {s, pending, imports:{plugins:Plugins, url, imgb64, axios, puppeteer, run, fs, os, paths, util, format, bytes, shuffle, htmlescape, urlexpand, __module}})
|
||||||
const promised = await Promise.all(pending)
|
const promised = await Promise.all(pending)
|
||||||
|
|
||||||
//Check plugins errors
|
//Check plugins errors
|
||||||
@@ -123,6 +123,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns module __dirname */
|
||||||
|
function __module(module) {
|
||||||
|
return paths.join(paths.dirname(url.fileURLToPath(module)))
|
||||||
|
}
|
||||||
|
|
||||||
/** Formatter */
|
/** Formatter */
|
||||||
function format(n, {sign = false} = {}) {
|
function format(n, {sign = false} = {}) {
|
||||||
for (const {u, v} of [{u:"b", v:10**9}, {u:"m", v:10**6}, {u:"k", v:10**3}])
|
for (const {u, v} of [{u:"b", v:10**9}, {u:"m", v:10**6}, {u:"k", v:10**3}])
|
||||||
|
|||||||
@@ -925,6 +925,128 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Anilist api
|
||||||
|
if (/^https:..graphql.anilist.co/.test(url)) {
|
||||||
|
//Initialization and media generator
|
||||||
|
const query = body.query
|
||||||
|
const media = ({type}) => ({
|
||||||
|
title:{romaji:faker.lorem.words(), english:faker.lorem.words(), native:faker.lorem.words()},
|
||||||
|
description:faker.lorem.paragraphs(),
|
||||||
|
type,
|
||||||
|
status:faker.random.arrayElement(["FINISHED", "RELEASING", "NOT_YET_RELEASED", "CANCELLED", "HIATUS"]),
|
||||||
|
episodes:100+faker.random.number(100),
|
||||||
|
volumes:faker.random.number(100),
|
||||||
|
chapters:100+faker.random.number(1000),
|
||||||
|
averageScore:faker.random.number(100),
|
||||||
|
countryOfOrigin:"JP",
|
||||||
|
genres:new Array(6).fill(null).map(_ => faker.lorem.word()),
|
||||||
|
coverImage:{medium:null},
|
||||||
|
startDate:{year:faker.date.past(20).getFullYear()}
|
||||||
|
})
|
||||||
|
//User statistics query
|
||||||
|
if (/^query Statistics /.test(query)) {
|
||||||
|
console.debug(`metrics/compute/mocks > mocking anilist api result > Statistics`)
|
||||||
|
return ({
|
||||||
|
status:200,
|
||||||
|
data:{
|
||||||
|
data:{
|
||||||
|
User:{
|
||||||
|
id:faker.random.number(100000),
|
||||||
|
name:faker.internet.userName(),
|
||||||
|
about:null,
|
||||||
|
statistics:{
|
||||||
|
anime:{
|
||||||
|
count:faker.random.number(1000),
|
||||||
|
minutesWatched:faker.random.number(100000),
|
||||||
|
episodesWatched:faker.random.number(10000),
|
||||||
|
genres:new Array(4).fill(null).map(_ => ({genre:faker.lorem.word()})),
|
||||||
|
},
|
||||||
|
manga:{
|
||||||
|
count:faker.random.number(1000),
|
||||||
|
chaptersRead:faker.random.number(100000),
|
||||||
|
volumesRead:faker.random.number(10000),
|
||||||
|
genres:new Array(4).fill(null).map(_ => ({genre:faker.lorem.word()})),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//Favorites characters
|
||||||
|
if (/^query FavoritesCharacters /.test(query)) {
|
||||||
|
console.debug(`metrics/compute/mocks > mocking anilist api result > Favorites characters`)
|
||||||
|
return ({
|
||||||
|
status:200,
|
||||||
|
data:{
|
||||||
|
data:{
|
||||||
|
User:{
|
||||||
|
favourites:{
|
||||||
|
characters:{
|
||||||
|
nodes:new Array(2+faker.random.number(16)).fill(null).map(_ => ({
|
||||||
|
name:{full:faker.name.findName(), native:faker.name.findName()},
|
||||||
|
image:{medium:null}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
pageInfo:{currentPage:1, hasNextPage:false}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//Favorites anime/manga query
|
||||||
|
if (/^query Favorites /.test(query)) {
|
||||||
|
console.debug(`metrics/compute/mocks > mocking anilist api result > Favorites`)
|
||||||
|
const type = /anime[(]/.test(query) ? "ANIME" : /manga[(]/.test(query) ? "MANGA" : "OTHER"
|
||||||
|
return ({
|
||||||
|
status:200,
|
||||||
|
data:{
|
||||||
|
data:{
|
||||||
|
User:{
|
||||||
|
favourites:{
|
||||||
|
[type.toLocaleLowerCase()]:{
|
||||||
|
nodes:new Array(16).fill(null).map(_ => media({type})),
|
||||||
|
pageInfo:{currentPage:1, hasNextPage:false},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//Medias query
|
||||||
|
if (/^query Medias /.test(query)) {
|
||||||
|
console.debug(`metrics/compute/mocks > mocking anilist api result > Medias`)
|
||||||
|
const type = body.variables.type
|
||||||
|
return ({
|
||||||
|
status:200,
|
||||||
|
data:{
|
||||||
|
data:{
|
||||||
|
MediaListCollection:{
|
||||||
|
lists:[
|
||||||
|
{
|
||||||
|
name:{ANIME:"Watching", MANGA:"Reading", OTHER:"Completed"}[type],
|
||||||
|
isCustomList:false,
|
||||||
|
entries:new Array(16).fill(null).map(_ => ({
|
||||||
|
status:faker.random.arrayElement(["CURRENT", "PLANNING", "COMPLETED", "DROPPED", "PAUSED", "REPEATING"]),
|
||||||
|
progress:faker.random.number(100),
|
||||||
|
progressVolumes: null,
|
||||||
|
score:0,
|
||||||
|
startedAt:{year:null, month:null, day:null},
|
||||||
|
completedAt:{year:null, month:null, day:null},
|
||||||
|
media:media({type})
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
return target(...args)
|
return target(...args)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -1098,7 +1220,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Last.fm api
|
//Last.fm api
|
||||||
if (/^https:..ws.audioscrobbler.com/.test(url)) {
|
if (/^https:..ws.audioscrobbler.com.*$/.test(url)) {
|
||||||
//Get recently played tracks
|
//Get recently played tracks
|
||||||
if (/user.getrecenttracks/.test(url)) {
|
if (/user.getrecenttracks/.test(url)) {
|
||||||
console.debug(`metrics/compute/mocks > mocking lastfm api result > ${url}`)
|
console.debug(`metrics/compute/mocks > mocking lastfm api result > ${url}`)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
const {data:plugins} = await axios.get("/.plugins")
|
const {data:plugins} = await axios.get("/.plugins")
|
||||||
const {data:base} = await axios.get("/.plugins.base")
|
const {data:base} = await axios.get("/.plugins.base")
|
||||||
const {data:version} = await axios.get("/.version")
|
const {data:version} = await axios.get("/.version")
|
||||||
|
templates.sort((a, b) => (a.name.startsWith("@") ^ b.name.startsWith("@")) ? (a.name.startsWith("@") ? 1 : -1) : a.name.localeCompare(b.name))
|
||||||
//App
|
//App
|
||||||
return new Vue({
|
return new Vue({
|
||||||
//Initialization
|
//Initialization
|
||||||
@@ -76,6 +77,7 @@
|
|||||||
stargazers:"✨ Stargazers over last weeks",
|
stargazers:"✨ Stargazers over last weeks",
|
||||||
activity:"📰 Recent activity",
|
activity:"📰 Recent activity",
|
||||||
people:"🧑🤝🧑 Followers and followed",
|
people:"🧑🤝🧑 Followers and followed",
|
||||||
|
anilist:"🌸 Anilist",
|
||||||
base:"🗃️ Base content",
|
base:"🗃️ Base content",
|
||||||
"base.header":"Header",
|
"base.header":"Header",
|
||||||
"base.activity":"Account activity",
|
"base.activity":"Account activity",
|
||||||
@@ -87,6 +89,7 @@
|
|||||||
descriptions:{
|
descriptions:{
|
||||||
"languages.ignored":{text:"Ignored languages", placeholder:"lang-0, lang-1, ..."},
|
"languages.ignored":{text:"Ignored languages", placeholder:"lang-0, lang-1, ..."},
|
||||||
"languages.skipped":{text:"Skipped repositories", placeholder:"repo-0, repo-1, ..."},
|
"languages.skipped":{text:"Skipped repositories", placeholder:"repo-0, repo-1, ..."},
|
||||||
|
"languages.colors":{text:"Custom language colors", placeholder:"0:#ff0000, javascript:yellow, ..."},
|
||||||
"pagespeed.detailed":{text:"Detailed audit", type:"boolean"},
|
"pagespeed.detailed":{text:"Detailed audit", type:"boolean"},
|
||||||
"pagespeed.screenshot":{text:"Audit screenshot", type:"boolean"},
|
"pagespeed.screenshot":{text:"Audit screenshot", type:"boolean"},
|
||||||
"pagespeed.url":{text:"Url", placeholder:"(default to GitHub attached)"},
|
"pagespeed.url":{text:"Url", placeholder:"(default to GitHub attached)"},
|
||||||
@@ -104,6 +107,7 @@
|
|||||||
"isocalendar.duration":{text:"Duration", type:"select", values:["half-year", "full-year"]},
|
"isocalendar.duration":{text:"Duration", type:"select", values:["half-year", "full-year"]},
|
||||||
"projects.limit":{text:"Limit", type:"number", min:0, max:100},
|
"projects.limit":{text:"Limit", type:"number", min:0, max:100},
|
||||||
"projects.repositories":{text:"Repositories projects", placeholder:"user/repo/projects/1, ..."},
|
"projects.repositories":{text:"Repositories projects", placeholder:"user/repo/projects/1, ..."},
|
||||||
|
"projects.descriptions":{text:"Projects descriptions", type:"boolean"},
|
||||||
"topics.mode":{text:"Mode", type:"select", values:["starred", "mastered"]},
|
"topics.mode":{text:"Mode", type:"select", values:["starred", "mastered"]},
|
||||||
"topics.sort":{text:"Sort by", type:"select", values:["starred", "activity", "stars", "random"]},
|
"topics.sort":{text:"Sort by", type:"select", values:["starred", "activity", "stars", "random"]},
|
||||||
"topics.limit":{text:"Limit", type:"number", min:0, max:20},
|
"topics.limit":{text:"Limit", type:"number", min:0, max:20},
|
||||||
@@ -117,6 +121,11 @@
|
|||||||
"people.limit":{text:"Limit", type:"number", min:1, max:9999},
|
"people.limit":{text:"Limit", type:"number", min:1, max:9999},
|
||||||
"people.types":{text:"Types", placeholder:"followers, following"},
|
"people.types":{text:"Types", placeholder:"followers, following"},
|
||||||
"people.identicons":{text:"Use identicons", type:"boolean"},
|
"people.identicons":{text:"Use identicons", type:"boolean"},
|
||||||
|
"anilist.medias":{text:"Medias to display", placeholder:"anime, manga"},
|
||||||
|
"anilist.sections":{text:"Sections to display", placeholder:"favorites, watching, reading, characters"},
|
||||||
|
"anilist.limit":{text:"Limit", type:"number", min:0, max:9999},
|
||||||
|
"anilist.shuffle":{text:"Shuffle data", type:"boolean"},
|
||||||
|
"anilist.user":{text:"Username", placeholder:"(default to GitHub login)"},
|
||||||
},
|
},
|
||||||
"languages.ignored":"",
|
"languages.ignored":"",
|
||||||
"languages.skipped":"",
|
"languages.skipped":"",
|
||||||
@@ -149,6 +158,11 @@
|
|||||||
"people.limit":28,
|
"people.limit":28,
|
||||||
"people.types":"followers, following",
|
"people.types":"followers, following",
|
||||||
"people.identicons":false,
|
"people.identicons":false,
|
||||||
|
"anilist.medias":"anime, manga",
|
||||||
|
"anilist.sections":"favorites",
|
||||||
|
"anilist.limit":2,
|
||||||
|
"anilist.shuffle":true,
|
||||||
|
"anilist.user":"",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
templates:{
|
templates:{
|
||||||
|
|||||||
@@ -302,8 +302,10 @@
|
|||||||
...(set.plugins.enabled.projects ? ({
|
...(set.plugins.enabled.projects ? ({
|
||||||
projects:{
|
projects:{
|
||||||
totalCount:options["projects.limit"]+faker.random.number(10),
|
totalCount:options["projects.limit"]+faker.random.number(10),
|
||||||
|
descriptions:options["projects.descriptions"],
|
||||||
list:new Array(Number(options["projects.limit"])).fill(null).map(_ => ({
|
list:new Array(Number(options["projects.limit"])).fill(null).map(_ => ({
|
||||||
name:faker.lorem.sentence(),
|
name:faker.lorem.sentence(),
|
||||||
|
description:faker.lorem.paragraph(),
|
||||||
updated:`${2+faker.random.number(8)} days ago`,
|
updated:`${2+faker.random.number(8)} days ago`,
|
||||||
progress:{enabled:true, todo:faker.random.number(50), doing:faker.random.number(50), done:faker.random.number(50), get total() { return this.todo + this.doing + this.done } }
|
progress:{enabled:true, todo:faker.random.number(50), doing:faker.random.number(50), done:faker.random.number(50), get total() { return this.todo + this.doing + this.done } }
|
||||||
}))
|
}))
|
||||||
@@ -403,6 +405,59 @@
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}) : null),
|
}) : null),
|
||||||
|
//Anilist
|
||||||
|
...(set.plugins.enabled.anilist ? ({
|
||||||
|
anilist:{
|
||||||
|
user:{
|
||||||
|
stats:{
|
||||||
|
anime:{
|
||||||
|
count:faker.random.number(1000),
|
||||||
|
minutesWatched:faker.random.number(100000),
|
||||||
|
episodesWatched:faker.random.number(10000),
|
||||||
|
genres:new Array(4).fill(null).map(_ => ({genre:faker.lorem.word()})),
|
||||||
|
},
|
||||||
|
manga:{
|
||||||
|
count:faker.random.number(1000),
|
||||||
|
chaptersRead:faker.random.number(100000),
|
||||||
|
volumesRead:faker.random.number(10000),
|
||||||
|
genres:new Array(4).fill(null).map(_ => ({genre:faker.lorem.word()})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
genres:new Array(4).fill(null).map(_ => ({genre:faker.lorem.word()})),
|
||||||
|
},
|
||||||
|
get lists() {
|
||||||
|
const media = (type) => ({
|
||||||
|
name:faker.lorem.words(),
|
||||||
|
type,
|
||||||
|
status:faker.random.arrayElement(["FINISHED", "RELEASING", "NOT_YET_RELEASED", "CANCELLED", "HIATUS"]),
|
||||||
|
release:faker.date.past(20).getFullYear(),
|
||||||
|
genres:new Array(6).fill(null).map(_ => faker.lorem.word()),
|
||||||
|
progress:faker.random.number(100),
|
||||||
|
description:faker.lorem.paragraphs(),
|
||||||
|
scores:{user:faker.random.number(100), community:faker.random.number(100)},
|
||||||
|
released:100+faker.random.number(1000),
|
||||||
|
artwork:"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOcOnfpfwAGfgLYttYINwAAAABJRU5ErkJggg==",
|
||||||
|
})
|
||||||
|
const sections = options["anilist.sections"].split(",").map(x => x.trim()).filter(x => x)
|
||||||
|
const medias = options["anilist.medias"].split(",").map(x => x.trim()).filter(x => x)
|
||||||
|
return {
|
||||||
|
...(medias.includes("anime") ? {anime:{
|
||||||
|
...(sections.includes("watching") ? {watching:new Array(Number(options["anilist.limit"])||4).fill(null).map(_ => media("ANIME"))} : {}),
|
||||||
|
...(sections.includes("favorites") ? {favorites:new Array(Number(options["anilist.limit"])||4).fill(null).map(_ => media("ANIME"))} : {}),
|
||||||
|
}} : {}),
|
||||||
|
...(medias.includes("manga") ? {manga:{
|
||||||
|
...(sections.includes("reading") ? {reading:new Array(Number(options["anilist.limit"])||4).fill(null).map(_ => media("MANGA"))} : {}),
|
||||||
|
...(sections.includes("favorites") ? {favorites:new Array(Number(options["anilist.limit"])||4).fill(null).map(_ => media("MANGA"))} : {}),
|
||||||
|
}} : {}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
characters:new Array(11).fill(null).map(_ => ({
|
||||||
|
name:faker.name.findName(),
|
||||||
|
artwork:"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOcOnfpfwAGfgLYttYINwAAAABJRU5ErkJggg==",
|
||||||
|
})),
|
||||||
|
sections:options["anilist.sections"].split(",").map(x => x.trim()).filter(x => x)
|
||||||
|
}
|
||||||
|
}) : null),
|
||||||
//Activity
|
//Activity
|
||||||
...(set.plugins.enabled.activity ? ({
|
...(set.plugins.enabled.activity ? ({
|
||||||
activity:{
|
activity:{
|
||||||
|
|||||||
122
source/plugins/anilist/index.mjs
Normal file
122
source/plugins/anilist/index.mjs
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
//Setup
|
||||||
|
export default async function ({login, imports, q}, {enabled = false} = {}) {
|
||||||
|
//Plugin execution
|
||||||
|
try {
|
||||||
|
//Check if plugin is enabled and requirements are met
|
||||||
|
if ((!enabled)||(!q.anilist))
|
||||||
|
return null
|
||||||
|
//Parameters override
|
||||||
|
let {"anilist.medias":medias = ["anime", "manga"], "anilist.sections":sections = ["favorites"], "anilist.limit":limit = 2, "anilist.shuffle":shuffle = true, "anilist.user":user = login} = q
|
||||||
|
//Medias types
|
||||||
|
medias = decodeURIComponent(medias).split(",").map(x => x.trim().toLocaleLowerCase()).filter(x => ["anime", "manga"].includes(x))
|
||||||
|
//Sections
|
||||||
|
sections = decodeURIComponent(sections).split(",").map(x => x.trim().toLocaleLowerCase()).filter(x => ["favorites", "watching", "reading", "characters"].includes(x))
|
||||||
|
//Limit medias
|
||||||
|
limit = Math.max(0, Number(limit))
|
||||||
|
//GraphQL queries
|
||||||
|
const query = {
|
||||||
|
statistics:`${await imports.fs.readFile(`${imports.__module(import.meta.url)}/queries/statistics.graphql`)}`,
|
||||||
|
characters:`${await imports.fs.readFile(`${imports.__module(import.meta.url)}/queries/characters.graphql`)}`,
|
||||||
|
medias:`${await imports.fs.readFile(`${imports.__module(import.meta.url)}/queries/medias.graphql`)}`,
|
||||||
|
favorites:`${await imports.fs.readFile(`${imports.__module(import.meta.url)}/queries/favorites.graphql`)}`,
|
||||||
|
}
|
||||||
|
//Initialization
|
||||||
|
const result = {user:{stats:null, genres:[]}, lists:Object.fromEntries(medias.map(type => [type, {}])), characters:[], sections}
|
||||||
|
//User statistics
|
||||||
|
{
|
||||||
|
//Query API
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > anilist > querying api (user statistics)`)
|
||||||
|
const {data:{data:{User:{statistics:stats}}}} = await imports.axios.post("https://graphql.anilist.co", {variables:{name:user}, query:query.statistics})
|
||||||
|
//Format and save results
|
||||||
|
result.user.stats = stats
|
||||||
|
result.user.genres = [...new Set([...stats.anime.genres.map(({genre}) => genre), ...stats.manga.genres.map(({genre}) => genre)])]
|
||||||
|
}
|
||||||
|
//Medias lists
|
||||||
|
if ((sections.includes("watching"))||(sections.includes("reading"))) {
|
||||||
|
for (const type of medias) {
|
||||||
|
//Query API
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > anilist > querying api (medias lists - ${type})`)
|
||||||
|
const {data:{data:{MediaListCollection:{lists}}}} = await imports.axios.post("https://graphql.anilist.co", {variables:{name:user, type:type.toLocaleUpperCase()}, query:query.medias})
|
||||||
|
//Format and save results
|
||||||
|
for (const {name, entries} of lists) {
|
||||||
|
//Format results
|
||||||
|
const list = await Promise.all(entries.map(async media => await format({media, imports})))
|
||||||
|
result.lists[type][name.toLocaleLowerCase()] = shuffle ? imports.shuffle(list) : list
|
||||||
|
//Limit results
|
||||||
|
if (limit > 0) {
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > anilist > keeping only ${limit} medias`)
|
||||||
|
result.lists[type][name.toLocaleLowerCase()].splice(limit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Favorites anime/manga
|
||||||
|
if (sections.includes("favorites")) {
|
||||||
|
for (const type of medias) {
|
||||||
|
//Query API
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > anilist > querying api (favorites ${type}s)`)
|
||||||
|
const list = []
|
||||||
|
let page = 1
|
||||||
|
let next = false
|
||||||
|
do {
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > anilist > querying api (favorites ${type}s - page ${page})`)
|
||||||
|
const {data:{data:{User:{favourites:{[type]:{nodes, pageInfo:cursor}}}}}} = await imports.axios.post("https://graphql.anilist.co", {variables:{name:user, page}, query:query.favorites.replace(/[$]type/g, type)})
|
||||||
|
page = cursor.currentPage
|
||||||
|
next = cursor.hasNextPage
|
||||||
|
list.push(...await Promise.all(nodes.map(media => format({media:{progess:null, score:null, media}, imports}))))
|
||||||
|
} while (next)
|
||||||
|
//Format and save results
|
||||||
|
result.lists[type].favorites = shuffle ? imports.shuffle(list) : list
|
||||||
|
//Limit results
|
||||||
|
if (limit > 0) {
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > anilist > keeping only ${limit} medias`)
|
||||||
|
result.lists[type].favorites.splice(limit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Favorites characters
|
||||||
|
if (sections.includes("characters")) {
|
||||||
|
//Query API
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > anilist > querying api (favorites characters)`)
|
||||||
|
const characters = []
|
||||||
|
let page = 1
|
||||||
|
let next = false
|
||||||
|
do {
|
||||||
|
console.debug(`metrics/compute/${login}/plugins > anilist > querying api (favorites characters - page ${page})`)
|
||||||
|
const {data:{data:{User:{favourites:{characters:{nodes, pageInfo:cursor}}}}}} = await imports.axios.post("https://graphql.anilist.co", {variables:{name:user, page}, query:query.characters})
|
||||||
|
page = cursor.currentPage
|
||||||
|
next = cursor.hasNextPage
|
||||||
|
for (const {name:{full:name}, image:{medium:artwork}} of nodes)
|
||||||
|
characters.push({name, artwork:artwork ? await imports.imgb64(artwork) : "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOcOnfpfwAGfgLYttYINwAAAABJRU5ErkJggg=="})
|
||||||
|
} while (next)
|
||||||
|
//Format and save results
|
||||||
|
result.characters = shuffle ? imports.shuffle(characters) : characters
|
||||||
|
}
|
||||||
|
//Results
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
//Handle errors
|
||||||
|
catch (error) {
|
||||||
|
let message = "An error occured"
|
||||||
|
if (error.isAxiosError) {
|
||||||
|
const status = error.response?.status
|
||||||
|
console.log(error.response.data)
|
||||||
|
message = `API returned ${status}`
|
||||||
|
error = error.response?.data ?? null
|
||||||
|
}
|
||||||
|
throw {error:{message, instance:error}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Media formatter */
|
||||||
|
async function format({media, imports}) {
|
||||||
|
const {progress, score:userScore, media:{title, description, status, startDate:{year:release}, genres, averageScore, episodes, chapters, type, coverImage:{medium:artwork}}} = media
|
||||||
|
return {
|
||||||
|
name:title.romaji,
|
||||||
|
type, status, release, genres, progress,
|
||||||
|
description:description.replace(/<br\s*\\?>/g, " "),
|
||||||
|
scores:{user:userScore, community:averageScore},
|
||||||
|
released:type === "ANIME" ? episodes : chapters,
|
||||||
|
artwork:artwork ? await imports.imgb64(artwork) : "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOcOnfpfwAGfgLYttYINwAAAABJRU5ErkJggg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
21
source/plugins/anilist/queries/characters.graphql
Normal file
21
source/plugins/anilist/queries/characters.graphql
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
query FavoritesCharacters ($name: String, $page:Int) {
|
||||||
|
User(name: $name) {
|
||||||
|
favourites {
|
||||||
|
characters(page: $page) {
|
||||||
|
nodes {
|
||||||
|
name {
|
||||||
|
full
|
||||||
|
native
|
||||||
|
}
|
||||||
|
image {
|
||||||
|
medium
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pageInfo {
|
||||||
|
currentPage
|
||||||
|
hasNextPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
source/plugins/anilist/queries/favorites.graphql
Normal file
34
source/plugins/anilist/queries/favorites.graphql
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
query Favorites ($name: String, $page:Int) {
|
||||||
|
User(name: $name) {
|
||||||
|
favourites {
|
||||||
|
$type(page: $page) {
|
||||||
|
nodes {
|
||||||
|
title {
|
||||||
|
romaji
|
||||||
|
english
|
||||||
|
native
|
||||||
|
}
|
||||||
|
description
|
||||||
|
type
|
||||||
|
status(version: 2)
|
||||||
|
episodes
|
||||||
|
volumes
|
||||||
|
chapters
|
||||||
|
averageScore
|
||||||
|
countryOfOrigin
|
||||||
|
genres
|
||||||
|
coverImage {
|
||||||
|
medium
|
||||||
|
}
|
||||||
|
startDate {
|
||||||
|
year
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pageInfo {
|
||||||
|
currentPage
|
||||||
|
hasNextPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
source/plugins/anilist/queries/medias.graphql
Normal file
50
source/plugins/anilist/queries/medias.graphql
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
query Medias ($name: String, $type: MediaType) {
|
||||||
|
MediaListCollection(userName: $name, type: $type) {
|
||||||
|
lists {
|
||||||
|
name
|
||||||
|
isCustomList
|
||||||
|
entries {
|
||||||
|
...mediaListEntry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment mediaListEntry on MediaList {
|
||||||
|
status
|
||||||
|
progress
|
||||||
|
progressVolumes
|
||||||
|
score
|
||||||
|
startedAt {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
completedAt {
|
||||||
|
year
|
||||||
|
month
|
||||||
|
day
|
||||||
|
}
|
||||||
|
media {
|
||||||
|
title {
|
||||||
|
romaji
|
||||||
|
english
|
||||||
|
native
|
||||||
|
}
|
||||||
|
description
|
||||||
|
type
|
||||||
|
status(version: 2)
|
||||||
|
episodes
|
||||||
|
volumes
|
||||||
|
chapters
|
||||||
|
averageScore
|
||||||
|
countryOfOrigin
|
||||||
|
genres
|
||||||
|
coverImage {
|
||||||
|
medium
|
||||||
|
}
|
||||||
|
startDate {
|
||||||
|
year
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
source/plugins/anilist/queries/statistics.graphql
Normal file
25
source/plugins/anilist/queries/statistics.graphql
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
query Statistics ($name: String) {
|
||||||
|
User(name: $name) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
about
|
||||||
|
statistics {
|
||||||
|
anime {
|
||||||
|
count
|
||||||
|
minutesWatched
|
||||||
|
episodesWatched
|
||||||
|
genres(limit: 4) {
|
||||||
|
genre
|
||||||
|
}
|
||||||
|
}
|
||||||
|
manga {
|
||||||
|
count
|
||||||
|
chaptersRead
|
||||||
|
volumesRead
|
||||||
|
genres(limit: 4) {
|
||||||
|
genre
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
colors = ["0:#ff0000", "1:#ffa500", "2:#ffff00", "3:#008000", "4:#0000ff", "5:#4b0082", "6:#ee82ee", "7:#162221"]
|
colors = ["0:#ff0000", "1:#ffa500", "2:#ffff00", "3:#008000", "4:#0000ff", "5:#4b0082", "6:#ee82ee", "7:#162221"]
|
||||||
if (`${colors}` === "complementary")
|
if (`${colors}` === "complementary")
|
||||||
colors = ["0:#ff0000", "1:#008000", "2:#ffa500", "3:#0000ff", "4:#ffff00", "5:#4b0082", "6:#162221", "7:#ee82ee"]
|
colors = ["0:#ff0000", "1:#008000", "2:#ffa500", "3:#0000ff", "4:#ffff00", "5:#4b0082", "6:#162221", "7:#ee82ee"]
|
||||||
|
|
||||||
colors = Object.fromEntries(decodeURIComponent(colors).split(",").map(x => x.trim().toLocaleLowerCase()).filter(x => x).map(x => x.split(":").map(x => x.trim())))
|
colors = Object.fromEntries(decodeURIComponent(colors).split(",").map(x => x.trim().toLocaleLowerCase()).filter(x => x).map(x => x.split(":").map(x => x.trim())))
|
||||||
console.debug(`metrics/compute/${login}/plugins > languages > custom colors ${JSON.stringify(colors)}`)
|
console.debug(`metrics/compute/${login}/plugins > languages > custom colors ${JSON.stringify(colors)}`)
|
||||||
//Iterate through user's repositories and retrieve languages data
|
//Iterate through user's repositories and retrieve languages data
|
||||||
|
|||||||
16
source/queries/people.repository.graphql
Normal file
16
source/queries/people.repository.graphql
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
query Repository {
|
||||||
|
user(login: "$login") {
|
||||||
|
repository(name: "$repository") {
|
||||||
|
$type(first: 100) {
|
||||||
|
pageInfo {
|
||||||
|
hasNextPage
|
||||||
|
endCursor
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
avatarUrl(size: 24)
|
||||||
|
login
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,5 +16,6 @@
|
|||||||
"stars",
|
"stars",
|
||||||
"stargazers",
|
"stargazers",
|
||||||
"people",
|
"people",
|
||||||
"activity"
|
"activity",
|
||||||
|
"anilist"
|
||||||
]
|
]
|
||||||
130
source/templates/classic/partials/anilist.ejs
Normal file
130
source/templates/classic/partials/anilist.ejs
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
<% if (plugins.anilist) { %>
|
||||||
|
<section>
|
||||||
|
<h2 class="field">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M4.75 2.5a.25.25 0 00-.25.25v9.91l3.023-2.489a.75.75 0 01.954 0l3.023 2.49V2.75a.25.25 0 00-.25-.25h-6.5zM3 2.75C3 1.784 3.784 1 4.75 1h6.5c.966 0 1.75.784 1.75 1.75v11.5a.75.75 0 01-1.227.579L8 11.722l-3.773 3.107A.75.75 0 013 14.25V2.75z"></path></svg>
|
||||||
|
Anilist
|
||||||
|
</h2>
|
||||||
|
<% if (plugins.anilist.error) { %>
|
||||||
|
<div class="row">
|
||||||
|
<section>
|
||||||
|
<div class="field error">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.343 13.657A8 8 0 1113.657 2.343 8 8 0 012.343 13.657zM6.03 4.97a.75.75 0 00-1.06 1.06L6.94 8 4.97 9.97a.75.75 0 101.06 1.06L8 9.06l1.97 1.97a.75.75 0 101.06-1.06L9.06 8l1.97-1.97a.75.75 0 10-1.06-1.06L8 6.94 6.03 4.97z"></path></svg>
|
||||||
|
<%= plugins.anilist.error.message %>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<% } else { %>
|
||||||
|
<div class="row fill-width">
|
||||||
|
<section>
|
||||||
|
<div class="field">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.5 7.775V2.75a.25.25 0 01.25-.25h5.025a.25.25 0 01.177.073l6.25 6.25a.25.25 0 010 .354l-5.025 5.025a.25.25 0 01-.354 0l-6.25-6.25a.25.25 0 01-.073-.177zm-1.5 0V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 010 2.474l-5.026 5.026a1.75 1.75 0 01-2.474 0l-6.25-6.25A1.75 1.75 0 011 7.775zM6 5a1 1 0 100 2 1 1 0 000-2z"></path></svg>
|
||||||
|
Favorites genres: <%= plugins.anilist.user.genres.join(", ") %>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<section>
|
||||||
|
<div class="field">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M16 3.75a.75.75 0 00-1.136-.643L11 5.425V4.75A1.75 1.75 0 009.25 3h-7.5A1.75 1.75 0 000 4.75v6.5C0 12.216.784 13 1.75 13h7.5A1.75 1.75 0 0011 11.25v-.675l3.864 2.318A.75.75 0 0016 12.25v-8.5zm-5 5.075l3.5 2.1v-5.85l-3.5 2.1v1.65zM9.5 6.75v-2a.25.25 0 00-.25-.25h-7.5a.25.25 0 00-.25.25v6.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-4.5z"></path></svg>
|
||||||
|
<%= f(plugins.anilist.user.stats.anime.minutesWatched) %> minute<%= s(plugins.anilist.user.stats.anime.minutesWatched) %> watched
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<div class="field">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M0 1.75A.75.75 0 01.75 1h4.253c1.227 0 2.317.59 3 1.501A3.744 3.744 0 0111.006 1h4.245a.75.75 0 01.75.75v10.5a.75.75 0 01-.75.75h-4.507a2.25 2.25 0 00-1.591.659l-.622.621a.75.75 0 01-1.06 0l-.622-.621A2.25 2.25 0 005.258 13H.75a.75.75 0 01-.75-.75V1.75zm8.755 3a2.25 2.25 0 012.25-2.25H14.5v9h-3.757c-.71 0-1.4.201-1.992.572l.004-7.322zm-1.504 7.324l.004-5.073-.002-2.253A2.25 2.25 0 005.003 2.5H1.5v9h3.757a3.75 3.75 0 011.994.574z"></path></svg>
|
||||||
|
<%= f(plugins.anilist.user.stats.manga.chaptersRead) %> chapter<%= s(plugins.anilist.user.stats.manga.chaptersRead) %> read
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div class="row fill-width">
|
||||||
|
<section>
|
||||||
|
<% for (const media of Object.keys(plugins.anilist.lists)) { %>
|
||||||
|
<% for (const list of plugins.anilist.sections) { %>
|
||||||
|
<% if (plugins.anilist.lists?.[media]?.[list]?.length) { %>
|
||||||
|
<div class="anilist">
|
||||||
|
<h2 class="field">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2 4a1 1 0 100-2 1 1 0 000 2zm3.75-1.5a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5zm0 5a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5zm0 5a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5zM3 8a1 1 0 11-2 0 1 1 0 012 0zm-1 6a1 1 0 100-2 1 1 0 000 2z"></path></svg>
|
||||||
|
<%= {favorites:`Favorites ${media}s`, watching:"Currently watching", reading:"Currently reading"}[list] %>
|
||||||
|
</h2>
|
||||||
|
<% for (const {name, type, description, release, status, genres, scores, progress, released, artwork} of plugins.anilist.lists[media][list]) { %>
|
||||||
|
<div class="media">
|
||||||
|
<img src="data:image/png;base64,<%= artwork %>" width="48" height="72" alt=""/>
|
||||||
|
<div class="about">
|
||||||
|
<div class="name">
|
||||||
|
<%= name %>
|
||||||
|
</div>
|
||||||
|
<div class="infos">
|
||||||
|
<div>
|
||||||
|
<% if (type === "ANIME") { %>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M16 3.75a.75.75 0 00-1.136-.643L11 5.425V4.75A1.75 1.75 0 009.25 3h-7.5A1.75 1.75 0 000 4.75v6.5C0 12.216.784 13 1.75 13h7.5A1.75 1.75 0 0011 11.25v-.675l3.864 2.318A.75.75 0 0016 12.25v-8.5zm-5 5.075l3.5 2.1v-5.85l-3.5 2.1v1.65zM9.5 6.75v-2a.25.25 0 00-.25-.25h-7.5a.25.25 0 00-.25.25v6.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-4.5z"></path></svg>
|
||||||
|
Anime
|
||||||
|
<% } else if (type === "MANGA") { %>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M0 1.75A.75.75 0 01.75 1h4.253c1.227 0 2.317.59 3 1.501A3.744 3.744 0 0111.006 1h4.245a.75.75 0 01.75.75v10.5a.75.75 0 01-.75.75h-4.507a2.25 2.25 0 00-1.591.659l-.622.621a.75.75 0 01-1.06 0l-.622-.621A2.25 2.25 0 005.258 13H.75a.75.75 0 01-.75-.75V1.75zm8.755 3a2.25 2.25 0 012.25-2.25H14.5v9h-3.757c-.71 0-1.4.201-1.992.572l.004-7.322zm-1.504 7.324l.004-5.073-.002-2.253A2.25 2.25 0 005.003 2.5H1.5v9h3.757a3.75 3.75 0 011.994.574z"></path></svg>
|
||||||
|
Manga
|
||||||
|
<% } else { %>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M3.404 3.404a6.5 6.5 0 109.192 9.192 6.5 6.5 0 00-9.192-9.192zm-1.06 10.253A8 8 0 1113.656 2.343 8 8 0 012.343 13.657z"></path></svg>
|
||||||
|
Other
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M4.75 0a.75.75 0 01.75.75V2h5V.75a.75.75 0 011.5 0V2h1.25c.966 0 1.75.784 1.75 1.75v10.5A1.75 1.75 0 0113.25 16H2.75A1.75 1.75 0 011 14.25V3.75C1 2.784 1.784 2 2.75 2H4V.75A.75.75 0 014.75 0zm0 3.5h8.5a.25.25 0 01.25.25V6h-11V3.75a.25.25 0 01.25-.25h2zm-2.25 4v6.75c0 .138.112.25.25.25h10.5a.25.25 0 00.25-.25V7.5h-11z"></path></svg>
|
||||||
|
<%= status === "NOT_YET_RELEASED" ? "Not yet released" : `${release} ${{FINISHED:"", RELEASING:"(releasing)", NOT_YET_RELEASED:"(unreleased)", CANCELLED:"(cancelled)", HIATUS:"(hiatus)"}[status]}` %>
|
||||||
|
</div>
|
||||||
|
<% if (scores.community) { %>
|
||||||
|
<div>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8.834.066C7.494-.087 6.5 1.048 6.5 2.25v.5c0 1.329-.647 2.124-1.318 2.614-.328.24-.66.403-.918.508A1.75 1.75 0 002.75 5h-1A1.75 1.75 0 000 6.75v7.5C0 15.216.784 16 1.75 16h1a1.75 1.75 0 001.662-1.201c.525.075 1.067.229 1.725.415.152.043.31.088.475.133 1.154.32 2.54.653 4.388.653 1.706 0 2.97-.153 3.722-1.14.353-.463.537-1.042.668-1.672.118-.56.208-1.243.313-2.033l.04-.306c.25-1.869.265-3.318-.188-4.316a2.418 2.418 0 00-1.137-1.2C13.924 5.085 13.353 5 12.75 5h-1.422l.015-.113c.07-.518.157-1.17.157-1.637 0-.922-.151-1.719-.656-2.3-.51-.589-1.247-.797-2.01-.884zM4.5 13.3c.705.088 1.39.284 2.072.478l.441.125c1.096.305 2.334.598 3.987.598 1.794 0 2.28-.223 2.528-.549.147-.193.276-.505.394-1.07.105-.502.188-1.124.295-1.93l.04-.3c.25-1.882.189-2.933-.068-3.497a.922.922 0 00-.442-.48c-.208-.104-.52-.174-.997-.174H11c-.686 0-1.295-.577-1.206-1.336.023-.192.05-.39.076-.586.065-.488.13-.97.13-1.328 0-.809-.144-1.15-.288-1.316-.137-.158-.402-.304-1.048-.378C8.357 1.521 8 1.793 8 2.25v.5c0 1.922-.978 3.128-1.933 3.825a5.861 5.861 0 01-1.567.81V13.3zM2.75 6.5a.25.25 0 01.25.25v7.5a.25.25 0 01-.25.25h-1a.25.25 0 01-.25-.25v-7.5a.25.25 0 01.25-.25h1z"></path></svg>
|
||||||
|
<%= scores.community %>%
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
<% if (Number.isFinite(progress)) { %>
|
||||||
|
<div>
|
||||||
|
<% if (progress === released) { %>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM0 8a8 8 0 1116 0A8 8 0 010 8zm11.78-1.72a.75.75 0 00-1.06-1.06L6.75 9.19 5.28 7.72a.75.75 0 00-1.06 1.06l2 2a.75.75 0 001.06 0l4.5-4.5z"></path></svg>
|
||||||
|
<% } else { %>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0zM8 0a8 8 0 100 16A8 8 0 008 0zM6.379 5.227A.25.25 0 006 5.442v5.117a.25.25 0 00.379.214l4.264-2.559a.25.25 0 000-.428L6.379 5.227z"></path></svg>
|
||||||
|
<% } %>
|
||||||
|
<%= type === "ANIME" ? "Episode" : type === "MANGA" ? "Chapter" : "" %> <%= progress %><%= released ? `/${released}` : "" %>
|
||||||
|
</div>
|
||||||
|
<% } else { %>
|
||||||
|
<div>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM0 8a8 8 0 1116 0A8 8 0 010 8zm6.5-.25A.75.75 0 017.25 7h1a.75.75 0 01.75.75v2.75h.25a.75.75 0 010 1.5h-2a.75.75 0 010-1.5h.25v-2h-.25a.75.75 0 01-.75-.75zM8 6a1 1 0 100-2 1 1 0 000 2z"></path></svg>
|
||||||
|
<%= progress %><%= released %> <%= type === "ANIME" ? `episode${s(released)}` : type === "MANGA" ? `chapter${s(released)}` : "" %>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
<div class="infos">
|
||||||
|
<div>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.5 7.775V2.75a.25.25 0 01.25-.25h5.025a.25.25 0 01.177.073l6.25 6.25a.25.25 0 010 .354l-5.025 5.025a.25.25 0 01-.354 0l-6.25-6.25a.25.25 0 01-.073-.177zm-1.5 0V2.75C1 1.784 1.784 1 2.75 1h5.025c.464 0 .91.184 1.238.513l6.25 6.25a1.75 1.75 0 010 2.474l-5.026 5.026a1.75 1.75 0 01-2.474 0l-6.25-6.25A1.75 1.75 0 011 7.775zM6 5a1 1 0 100 2 1 1 0 000-2z"></path></svg>
|
||||||
|
<%= genres.join(", ") %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="description">
|
||||||
|
<%= description %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
<% } %>
|
||||||
|
<% } %>
|
||||||
|
<% if (plugins.anilist.sections.includes("characters")) { %>
|
||||||
|
<div class="anilist">
|
||||||
|
<h2 class="field">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M4.25 2.5c-1.336 0-2.75 1.164-2.75 3 0 2.15 1.58 4.144 3.365 5.682A20.565 20.565 0 008 13.393a20.561 20.561 0 003.135-2.211C12.92 9.644 14.5 7.65 14.5 5.5c0-1.836-1.414-3-2.75-3-1.373 0-2.609.986-3.029 2.456a.75.75 0 01-1.442 0C6.859 3.486 5.623 2.5 4.25 2.5zM8 14.25l-.345.666-.002-.001-.006-.003-.018-.01a7.643 7.643 0 01-.31-.17 22.075 22.075 0 01-3.434-2.414C2.045 10.731 0 8.35 0 5.5 0 2.836 2.086 1 4.25 1 5.797 1 7.153 1.802 8 3.02 8.847 1.802 10.203 1 11.75 1 13.914 1 16 2.836 16 5.5c0 2.85-2.045 5.231-3.885 6.818a22.08 22.08 0 01-3.744 2.584l-.018.01-.006.003h-.002L8 14.25zm0 0l.345.666a.752.752 0 01-.69 0L8 14.25z"></path></svg>
|
||||||
|
Favorites characters
|
||||||
|
</h2>
|
||||||
|
<div class="characters">
|
||||||
|
<% for (const {name, artwork} of plugins.anilist.characters) { %>
|
||||||
|
<img src="data:image/png;base64,<%= artwork %>" width="36" height="54" alt=""/>
|
||||||
|
<% } %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<% } %>
|
||||||
|
</section>
|
||||||
|
<% } %>
|
||||||
|
|
||||||
|
|
||||||
@@ -527,6 +527,77 @@
|
|||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Anilist */
|
||||||
|
.anilist {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-left: 28px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anilist .media {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
width: 450px;
|
||||||
|
}
|
||||||
|
.anilist .media img {
|
||||||
|
margin: 0 10px;
|
||||||
|
border-radius: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anilist .media .about {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.anilist .media .name {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 14px;
|
||||||
|
color: #58a6ff;
|
||||||
|
}
|
||||||
|
.anilist .media .infos {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
.anilist .media .infos > div {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
.anilist .media .infos svg {
|
||||||
|
fill: currentColor;
|
||||||
|
height: 12px;
|
||||||
|
width: 12px;
|
||||||
|
margin: 0;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anilist .media .description {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: block;
|
||||||
|
width: 380px;
|
||||||
|
max-height: 38px;
|
||||||
|
font-size: 12px;
|
||||||
|
white-space: normal;
|
||||||
|
/* May not work in all browsers */
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anilist .characters {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.anilist .characters img {
|
||||||
|
margin: 2px;
|
||||||
|
border-radius: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Fade animation */
|
/* Fade animation */
|
||||||
.af {
|
.af {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|||||||
@@ -290,6 +290,42 @@
|
|||||||
plugin_people:true,
|
plugin_people:true,
|
||||||
plugin_people_identicons:true,
|
plugin_people_identicons:true,
|
||||||
}, {skip:["terminal", "repository"]}],
|
}, {skip:["terminal", "repository"]}],
|
||||||
|
["Anilist plugin (default)", {
|
||||||
|
plugin_anilist:true,
|
||||||
|
}, {skip:["terminal", "repository"]}],
|
||||||
|
["Anilist plugin (manga only)", {
|
||||||
|
plugin_anilist:true,
|
||||||
|
plugin_anilist_medias:"manga",
|
||||||
|
}, {skip:["terminal", "repository"]}],
|
||||||
|
["Anilist plugin (anime only)", {
|
||||||
|
plugin_anilist:true,
|
||||||
|
plugin_anilist_medias:"anime",
|
||||||
|
}, {skip:["terminal", "repository"]}],
|
||||||
|
["Anilist plugin (favorites section)", {
|
||||||
|
plugin_anilist:true,
|
||||||
|
plugin_anilist_sections:"favorites",
|
||||||
|
}, {skip:["terminal", "repository"]}],
|
||||||
|
["Anilist plugin (watching/reading section)", {
|
||||||
|
plugin_anilist:true,
|
||||||
|
plugin_anilist_sections:"watching, reading",
|
||||||
|
}, {skip:["terminal", "repository"]}],
|
||||||
|
["Anilist plugin (characters section)", {
|
||||||
|
plugin_anilist:true,
|
||||||
|
plugin_anilist_sections:"characters",
|
||||||
|
}, {skip:["terminal", "repository"]}],
|
||||||
|
["Anilist plugin (additional options)", {
|
||||||
|
plugin_anilist:true,
|
||||||
|
plugin_anilist_limit:0,
|
||||||
|
plugin_anilist_shuffle:false,
|
||||||
|
plugin_anilist_user:"anilist",
|
||||||
|
}, {skip:["terminal", "repository"]}],
|
||||||
|
["Anilist plugin (complete)", {
|
||||||
|
plugin_anilist:true,
|
||||||
|
plugin_anilist_medias:"manga, anime",
|
||||||
|
plugin_anilist_sections:"favorites, watching, reading, characters",
|
||||||
|
plugin_anilist_limit:0,
|
||||||
|
plugin_anilist_shuffle:false,
|
||||||
|
}, {skip:["terminal", "repository"]}],
|
||||||
]
|
]
|
||||||
|
|
||||||
//Tests run
|
//Tests run
|
||||||
|
|||||||
Reference in New Issue
Block a user