diff --git a/KitsuAPI.js b/KitsuAPI.js new file mode 100644 index 0000000..ebf1e58 --- /dev/null +++ b/KitsuAPI.js @@ -0,0 +1,8 @@ +const { http } = require('powercord/webpack'); + +module.exports = { + searchAnime (title) { + const url = `https://kitsu.io/api/edge/anime?filter[text]=${encodeURIComponent(title)}&page[limit]=5`; + return http.get(url).then(r => JSON.parse(r.text)); + } +}; diff --git a/Settings.jsx b/Settings.jsx new file mode 100644 index 0000000..f3c2830 --- /dev/null +++ b/Settings.jsx @@ -0,0 +1,14 @@ +const { React } = require('powercord/webpack'); +const { SwitchItem } = require('powercord/components/settings'); + +module.exports = ({ getSetting, toggleSetting }) => ( +
+ toggleSetting('nsfwFilter')} + > + Filter NSFW + +
+); diff --git a/commands/index.js b/commands/index.js new file mode 100644 index 0000000..57127a1 --- /dev/null +++ b/commands/index.js @@ -0,0 +1,7 @@ +require('fs') + .readdirSync(__dirname) + .filter(file => file !== 'index.js') + .forEach(filename => { + const moduleName = filename.split('.')[0]; + exports[moduleName] = require(`${__dirname}/${filename}`); + }); diff --git a/components/AnimeModal.jsx b/components/AnimeModal.jsx new file mode 100644 index 0000000..b69efb2 --- /dev/null +++ b/components/AnimeModal.jsx @@ -0,0 +1,117 @@ +const { React } = require('powercord/webpack'); +const { Button, Icon } = require('powercord/components'); +const { Modal } = require('powercord/components/modal'); +const { TextInput } = require('powercord/components/settings'); +const KitsuAPI = require('../KitsuAPI'); + +class AnimeResult extends React.Component { + constructor () { + super(); + this.state = { small: true }; + } + + handleClick () { + const { small } = this.state; + this.setState({ small: !small }); + } + + render () { + const { small } = this.state; + const { item } = this.props; + let height, cutoff, imgHeight, title, airingStatus; + const vid = item.youtubeVideoId ? : ''; + if (small) { + height = '100px'; + cutoff = 350; + imgHeight = '100px'; + title = item.titles.en; + airingStatus = ''; + } else { + height = vid === '' ? '250px' : '700px'; + cutoff = 1000; + imgHeight = '250px'; + title = `${item.titles.en || ''} ${item.titles.ja_jp || ''}${item.titles.en_jp ? ' (' + item.titles.en_jp + ')' : ''}`; + airingStatus = {item.status === 'current' ? 'Currently Airing' : 'Finished Airing'}; + } + const synopsis = item.synopsis.length > cutoff ? `${item.synopsis.slice(0, cutoff - 6)}[...]` : item.synopsis; + const img = item.posterImage.medium ? : ''; + return ( +
this.handleClick() } + style={{ height }} + > +
+ {img} +
+ {title} + {synopsis} +
+ {item.showType} + {item.ageRating || 'NR'} + {item.averageRating}% + {airingStatus} +
+
+
+ {!small ? vid : ''} +
+ ); + } +} + +module.exports = class AnimeModal extends React.Component { + constructor (props) { + super(props); + this.state = { entries: [], + textValue: '' + }; + } + + setText (t) { + this.setState({ textValue: t }); + } + + handleSearch () { + KitsuAPI.searchAnime(this.state.textValue).then((resp) => { + this.setState({ entries: resp.data }); + }); + } + + render () { + const { entries } = this.state; + const animeResultList = []; + entries.forEach(animeResult => { + if (this.props.getSetting('nsfwFilter', true) && !animeResult.attributes.nsfw) { + animeResultList.push(); + } + }); + return ( + + + + Anime Search + + + +
+ this.setText(t)} + > + Title + + +
+ +
+ {animeResultList} +
+
+
+ ); + } +}; diff --git a/index.js b/index.js new file mode 100644 index 0000000..42e1350 --- /dev/null +++ b/index.js @@ -0,0 +1,47 @@ +const { Tooltip } = require('powercord/components'); +const { Plugin } = require('powercord/entities'); +const { open: openModal } = require('powercord/modal'); +const { inject, uninject } = require('powercord/injector'); +const { React, getModuleByDisplayName } = require('powercord/webpack'); + +const { resolve } = require('path'); + +const commands = require('./commands'); +const AnimeModal = require('./components/AnimeModal'); +const Settings = require('./Settings'); + +module.exports = class Anime extends Plugin { + async startPlugin () { + this.loadCSS(resolve(__dirname, 'style.scss')); + this._injectModal(); + this.registerSettings('anime', 'Anime Search', (props) => + React.createElement(Settings, { + ...props + }) + ); + + Object.values(commands).forEach(command => + this.registerCommand(command.command, command.aliases || [], command.description, command.usage, command.func) + ); + } + + pluginWillUnload () { + uninject('anime-headerbar'); + } + + async _injectModal () { + const HeaderBarContainer = await getModuleByDisplayName('HeaderBarContainer'); + inject('anime-headerbar', HeaderBarContainer.prototype, 'renderToolbar', (args, res) => { + res.props.children.push( + React.createElement(Tooltip, { + text: 'Anime lookup', + position: 'bottom' + }, React.createElement('div', { + className: 'anime-header-icon-s', + onClick: () => openModal(() => React.createElement(this.settings.connectStore(AnimeModal))) + })) + ); + return res; + }); + } +}; diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..687af3e --- /dev/null +++ b/manifest.json @@ -0,0 +1,7 @@ +{ + "name": "Anime", + "version": "1.0.0", + "description": "Anime lookup inside of Powercord", + "author": "CyberRonin", + "license": "MIT" +} diff --git a/style.scss b/style.scss new file mode 100644 index 0000000..0b9a2a5 --- /dev/null +++ b/style.scss @@ -0,0 +1,103 @@ +.anime-header-icon { + &-s, &-a { + cursor: pointer; + height: 24px; + margin: 0 8px; + width: 24px; + opacity: .6; + + &:hover { + opacity: .8; + } + } + + &-a { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' viewBox='0 0 255 255'%3E%3Cpath fill='%23E75E45' d='M179.6,64.3c-6.8-2.5-14.1-4.1-21.8-4.4c-12.7-0.6-24.8,2.2-35.4,7.6c-0.6,0.3-1.3,0.6-2,1v36.4 c0,0.5,0,2.4-0.3,4c-0.7,3.7-2.9,7-6,9.1c-2.6,1.8-5.6,2.6-8.8,2.5c-0.6,0-1.3-0.1-1.9-0.2c-1.6-0.3-3.3-0.9-3.8-1.1 c-1.4-0.5-21.8-8.4-31.6-12.2c-1.2-0.5-2.2-0.9-3-1.2c-11.7,9.9-24,21.7-35.5,35.6c-0.1,0.1-0.6,0.7-0.7,0.8 c-1.5,2.1-1.6,5.1,0,7.4c1.2,1.7,3.1,2.7,5,2.8c1.3,0.1,2.7-0.3,3.9-1.1c0.1-0.1,0.2-0.2,0.4-0.3c12.2-8.8,25.6-15.9,39.8-21.6 c1-0.5,2.2-0.8,3.3-0.7c1.6,0.1,3.1,0.7,4.3,1.9c2.3,2.3,2.4,6,0.5,8.5c-0.8,1.2-1.5,2.4-2.2,3.6c-7.6,12.4-13.7,25.9-18.3,40 c-0.1,0.4-0.2,0.7-0.3,1.1v0.1c-0.4,1.7-0.1,3.5,1,5c1.2,1.7,3.1,2.7,5.1,2.8c1.4,0.1,2.7-0.3,3.9-1.1c0.5-0.4,1-0.8,1.4-1.3 c0.1-0.2,0.3-0.4,0.4-0.6c5-7.1,10.5-13.8,16.4-20c26.3-28.2,61.2-48.1,100.3-55.9c0.3-0.1,0.6-0.1,0.9-0.1c2.2,0.1,3.9,2,3.8,4.2 c-0.1,1.9-1.4,3.3-3.2,3.7c-36.3,7.7-101.7,50.8-78.8,113.4c0.4,1,0.7,1.6,1.2,2.5c1.2,1.7,3.1,2.7,5,2.8c1.1,0,4.2-0.3,6.1-3.7 c3.7-7,10.7-14.8,30.9-23.2c56.3-23.3,65.6-56.6,66.6-77.7v-1.2C227.1,102.1,207.6,74.7,179.6,64.3L179.6,64.3z M123.1,229.3 c-5.2-15.5-4.7-30.5,1.4-44.8c11.7,18.9,32.1,20.5,32.1,20.5C135.7,213.6,127.5,222.3,123.1,229.3z'%2F%3E%3Cpath fill='%23E75E45' d='M28,66.4c0.1,0.2,0.3,0.4,0.4,0.5c5.3,7.2,11.3,13.5,17.8,19.1c0.1,0.1,0.2,0.1,0.2,0.2 c4.2,3.6,12.2,8.8,18,10.9c0,0,36.1,13.9,38,14.7c0.7,0.3,1.7,0.6,2.2,0.7c1.6,0.3,3.3,0,4.8-1s2.4-2.5,2.7-4.1 c0.1-0.6,0.2-1.6,0.2-2.3V64.3c0.1-6.2-1.9-15.6-3.7-20.8c0-0.1-0.1-0.2-0.1-0.3c-2.8-8.1-6.6-16-11.4-23.5l-0.3-0.6L96.7,19 c-2-2.8-6-3.5-8.9-1.5c-0.5,0.3-0.8,0.7-1.2,1.1c-0.3,0.4-0.5,0.7-0.8,1.1c-6.4,9.3-9,20.6-7.3,31.7c-3.3,1.7-6.8,4-7.2,4.3 s-3.9,2.7-6.6,5.2c-9.7-5.5-21.3-7.2-32.2-4.6c-0.4,0.1-0.9,0.2-1.3,0.3c-0.5,0.2-1,0.4-1.4,0.7c-2.9,2-3.7,5.9-1.8,8.9 C28,66.2,28,66.4,28,66.4z M91.5,26.3c3.4,5.7,6.3,11.6,8.6,17.8c-4.6,0.8-9.1,2-13.5,3.6C86,40.2,87.7,32.8,91.5,26.3z M58.4,67.1c-3.2,3.5-5.9,7.3-8.3,11.3c-4.9-4.3-9.4-9.2-13.5-14.4C44.1,62.7,51.6,63.8,58.4,67.1z'%2F%3E%3C%2Fsvg%3E"); } + + &-s { + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' viewBox='0 0 255 255'%3E%3Cpath fill='%23E75E45' d='M179.6,64.3c-6.8-2.5-14.1-4.1-21.8-4.4c-12.7-0.6-24.8,2.2-35.4,7.6c-0.6,0.3-1.3,0.6-2,1v36.4 c0,0.5,0,2.4-0.3,4c-0.7,3.7-2.9,7-6,9.1c-2.6,1.8-5.6,2.6-8.8,2.5c-0.6,0-1.3-0.1-1.9-0.2c-1.6-0.3-3.3-0.9-3.8-1.1 c-1.4-0.5-21.8-8.4-31.6-12.2c-1.2-0.5-2.2-0.9-3-1.2c-11.7,9.9-24,21.7-35.5,35.6c-0.1,0.1-0.6,0.7-0.7,0.8 c-1.5,2.1-1.6,5.1,0,7.4c1.2,1.7,3.1,2.7,5,2.8c1.3,0.1,2.7-0.3,3.9-1.1c0.1-0.1,0.2-0.2,0.4-0.3c12.2-8.8,25.6-15.9,39.8-21.6 c1-0.5,2.2-0.8,3.3-0.7c1.6,0.1,3.1,0.7,4.3,1.9c2.3,2.3,2.4,6,0.5,8.5c-0.8,1.2-1.5,2.4-2.2,3.6c-7.6,12.4-13.7,25.9-18.3,40 c-0.1,0.4-0.2,0.7-0.3,1.1v0.1c-0.4,1.7-0.1,3.5,1,5c1.2,1.7,3.1,2.7,5.1,2.8c1.4,0.1,2.7-0.3,3.9-1.1c0.5-0.4,1-0.8,1.4-1.3 c0.1-0.2,0.3-0.4,0.4-0.6c5-7.1,10.5-13.8,16.4-20c26.3-28.2,61.2-48.1,100.3-55.9c0.3-0.1,0.6-0.1,0.9-0.1c2.2,0.1,3.9,2,3.8,4.2 c-0.1,1.9-1.4,3.3-3.2,3.7c-36.3,7.7-101.7,50.8-78.8,113.4c0.4,1,0.7,1.6,1.2,2.5c1.2,1.7,3.1,2.7,5,2.8c1.1,0,4.2-0.3,6.1-3.7 c3.7-7,10.7-14.8,30.9-23.2c56.3-23.3,65.6-56.6,66.6-77.7v-1.2C227.1,102.1,207.6,74.7,179.6,64.3L179.6,64.3z M123.1,229.3 c-5.2-15.5-4.7-30.5,1.4-44.8c11.7,18.9,32.1,20.5,32.1,20.5C135.7,213.6,127.5,222.3,123.1,229.3z'%2F%3E%3Cpath fill='%23E75E45' d='M28,66.4c0.1,0.2,0.3,0.4,0.4,0.5c5.3,7.2,11.3,13.5,17.8,19.1c0.1,0.1,0.2,0.1,0.2,0.2 c4.2,3.6,12.2,8.8,18,10.9c0,0,36.1,13.9,38,14.7c0.7,0.3,1.7,0.6,2.2,0.7c1.6,0.3,3.3,0,4.8-1s2.4-2.5,2.7-4.1 c0.1-0.6,0.2-1.6,0.2-2.3V64.3c0.1-6.2-1.9-15.6-3.7-20.8c0-0.1-0.1-0.2-0.1-0.3c-2.8-8.1-6.6-16-11.4-23.5l-0.3-0.6L96.7,19 c-2-2.8-6-3.5-8.9-1.5c-0.5,0.3-0.8,0.7-1.2,1.1c-0.3,0.4-0.5,0.7-0.8,1.1c-6.4,9.3-9,20.6-7.3,31.7c-3.3,1.7-6.8,4-7.2,4.3 s-3.9,2.7-6.6,5.2c-9.7-5.5-21.3-7.2-32.2-4.6c-0.4,0.1-0.9,0.2-1.3,0.3c-0.5,0.2-1,0.4-1.4,0.7c-2.9,2-3.7,5.9-1.8,8.9 C28,66.2,28,66.4,28,66.4z M91.5,26.3c3.4,5.7,6.3,11.6,8.6,17.8c-4.6,0.8-9.1,2-13.5,3.6C86,40.2,87.7,32.8,91.5,26.3z M58.4,67.1c-3.2,3.5-5.9,7.3-8.3,11.3c-4.9-4.3-9.4-9.2-13.5-14.4C44.1,62.7,51.6,63.8,58.4,67.1z'%2F%3E%3C%2Fsvg%3E"); } +} + +.anime-modal { + &-search { + margin-top: 20px; + display: flex; + align-items: center; + position: sticky; + top: 20px; + background-color: rgba(54, 57, 63, .9); + > div { + flex: 1; + + + button { + margin-left: 15px; + } + } + + .pc-input { + height: 32px; + } + + .pc-divider { + display: none; + } + } + &-header { + color: white; + font-weight: bold; + text-transform: uppercase; + } + &-results { + display: flex; + flex-direction: column; + } + &-result { + display: flex; + flex-direction: column; + padding: 4px; + border: 1px solid #2f3136; + border-radius: 4px; + margin-top: 5px; + margin-bottom: 5px; + color: white; + + &-image { + border-radius: 4px; + } + &-details { + margin-left: 5px; + display: flex; + flex-direction: column; + } + &-tags { + display: flex; + justify-content: space-evenly; + align-items: center; + height: 100%; + } + &-title { + margin-bottom: 5px; + font-size: 1.1em; + } + &-tag { + padding: 4px; + background-color: #7289DA; + border-radius: 4px; + } + &-rating, &-tv-rating { + display: flex; + align-items: center; + + &-icon { + height: 20px; + width: 20px; + margin-right: 5px; + } + } + } + &-result:hover { + background-color: #2f3136; + cursor: pointer; + } +} +