├── .env.example
├── .github
└── ISSUE_TEMPLATE
│ ├── config.yml
│ └── submit-addon.yaml
├── .gitignore
├── LICENSE
├── README.md
├── build.js
├── config.js
├── lib
├── cache.js
├── discord.js
├── graphql.js
├── html.js
├── issueToMeta.js
└── slug.js
├── netlify.toml
├── out
└── folder
├── package-lock.json
├── package.json
├── resources
├── ionicons.5.5.2.js
├── isMobile.js
├── isotope.3.0.6.pkgd.min.js
├── jquery-3.6.1.min.js
├── stremio_community_logo.png
└── styles.css
├── template
├── addon
│ └── index.html
└── home
│ ├── index.html
│ └── list-addon.html
└── trusted_publishers.json
/.env.example:
--------------------------------------------------------------------------------
1 | TOKEN="github Token"
2 | DISCORD_WEBHOOK="discord webhook"
3 | REPO="stremio-addons-list"
4 | REPO_AUTHOR="danamag"
5 | DOMAIN="stremio-addons.com"
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/submit-addon.yaml:
--------------------------------------------------------------------------------
1 | name: Publish Stremio Addon
2 | description: Submit a new Stremio Addon to the Unofficial Stremio Addons List
3 | title: "Addon Name"
4 | labels: ["pending approval"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | Thank you for submitting a new Stremio addon!
10 | - type: input
11 | id: addon-url
12 | attributes:
13 | label: Addon Manifest URL
14 | description: What is the addon manifest URL? (ends with "/manifest.json")
15 | placeholder: ex. https://addon.server.com/manifest.json
16 | validations:
17 | required: true
18 | - type: textarea
19 | id: addon-description
20 | attributes:
21 | label: Addon Description
22 | description: Describe the addon you are submitting.
23 | placeholder: My amazing new addon!
24 | validations:
25 | required: false
26 | - type: dropdown
27 | id: lang
28 | attributes:
29 | label: Language of Content
30 | description: Choose the language in which the streams / metadata show. (if applicable)
31 | options:
32 | - Multilingual (Default)
33 | - Abkhazian (ab-AB)
34 | - Afar (aa-AA)
35 | - Afrikaans (af-ZA)
36 | - Akan (ak-AK)
37 | - Albanian (sq-AL)
38 | - Amharic (am-AM)
39 | - Arabic (ar-SA)
40 | - Arabic (ar-AE)
41 | - Arabic (ar-DZ)
42 | - Aragonese (an-AN)
43 | - Armenian (hy-HY)
44 | - Assamese (as-AS)
45 | - Avaric (av-AV)
46 | - Avestan (ae-AE)
47 | - Aymara (ay-AY)
48 | - Azerbaijani (az-AZ)
49 | - Bambara (bm-BM)
50 | - Bashkir (ba-BA)
51 | - Basque (eu-ES)
52 | - Belarusian (be-BY)
53 | - Bengali (bn-BD)
54 | - Bislama (bi-BI)
55 | - Bosnian (bs-BS)
56 | - Breton (br-BR)
57 | - Bulgarian (bg-BG)
58 | - Burmese (my-MY)
59 | - Cantonese (cn-CN)
60 | - Catalan; Valencian (ca-ES)
61 | - Central Khmer (km-KM)
62 | - Chamorro (ch-GU)
63 | - Chechen (ce-CE)
64 | - Chichewa; Chewa; Nyanja (ny-NY)
65 | - Chinese (zh-CN)
66 | - Chinese (zh-HK)
67 | - Chinese (zh-SG)
68 | - Chinese (zh-TW)
69 | - Chuvash (cv-CV)
70 | - Cornish (kw-KW)
71 | - Corsican (co-CO)
72 | - Cree (cr-CR)
73 | - Croatian (hr-HR)
74 | - Czech (cs-CZ)
75 | - Danish (da-DK)
76 | - Divehi; Dhivehi; Maldivian (dv-DV)
77 | - Dutch; Flemish (nl-NL)
78 | - Dutch; Flemish (nl-BE)
79 | - Dzongkha (dz-DZ)
80 | - English (en-US)
81 | - English (en-AU)
82 | - English (en-CA)
83 | - English (en-GB)
84 | - English (en-IE)
85 | - English (en-NZ)
86 | - Esperanto (eo-EO)
87 | - Estonian (et-EE)
88 | - Ewe (ee-EE)
89 | - Faroese (fo-FO)
90 | - Fijian (fj-FJ)
91 | - Finnish (fi-FI)
92 | - French (fr-FR)
93 | - French (fr-CA)
94 | - Fulah (ff-FF)
95 | - Gaelic; Scottish Gaelic (gd-GB)
96 | - Galician (gl-ES)
97 | - Ganda (lg-LG)
98 | - Georgian (ka-GE)
99 | - German (de-DE)
100 | - German (de-AT)
101 | - German (de-CH)
102 | - Greek (el-GR)
103 | - Guarani (gn-GN)
104 | - Gujarati (gu-GU)
105 | - Haitian; Haitian Creole (ht-HT)
106 | - Hausa (ha-HA)
107 | - Hebrew (he-IL)
108 | - Herero (hz-HZ)
109 | - Hindi (hi-IN)
110 | - Hiri Motu (ho-HO)
111 | - Hungarian (hu-HU)
112 | - Icelandic (is-IS)
113 | - Ido (io-IO)
114 | - Igbo (ig-IG)
115 | - Indian (General)
116 | - Indonesian (id-ID)
117 | - Inuktitut (iu-IU)
118 | - Inupiaq (ik-IK)
119 | - Irish (ga-IE)
120 | - Italian (it-IT)
121 | - Japanese (ja-JP)
122 | - Javanese (jv-JV)
123 | - Kannada (kn-IN)
124 | - Kanuri (kr-KR)
125 | - Kashmiri (ks-KS)
126 | - Kazakh (kk-KZ)
127 | - Kikuyu; Gikuyu (ki-KI)
128 | - Kinyarwanda (rw-RW)
129 | - Kirghiz; Kyrgyz (ky-KG)
130 | - Komi (kv-KV)
131 | - Kongo (kg-KG)
132 | - Korean (ko-KR)
133 | - Kurdish (ku-KU)
134 | - Lao (lo-LO)
135 | - Latin (la-LA)
136 | - Latvian (lv-LV)
137 | - Lingala (ln-LN)
138 | - Lithuanian (lt-LT)
139 | - Luba-Katanga (lu-LU)
140 | - Luxembourgish; Letzeburgesch (lb-LB)
141 | - Macedonian (mk-MK)
142 | - Malagasy (mg-MG)
143 | - Malay (ms-MY)
144 | - Malay (ms-SG)
145 | - Malayalam (ml-IN)
146 | - Maltese (mt-MT)
147 | - Manx (gv-GV)
148 | - Maori (mi-MI)
149 | - Marathi (mr-IN)
150 | - Marshallese (mh-MH)
151 | - Moldavian; Moldovan (mo-MO)
152 | - Mongolian (mn-MN)
153 | - Nauru (na-NA)
154 | - Navajo; Navaho (nv-NV)
155 | - Ndonga (ng-NG)
156 | - Nepali (ne-NE)
157 | - Northern Sami (se-SE)
158 | - Norwegian (no-NO)
159 | - Occitan (post 1500) (oc-OC)
160 | - Ojibwa (oj-OJ)
161 | - Oriya (or-OR)
162 | - Oromo (om-OM)
163 | - Pali (pi-PI)
164 | - Panjabi; Punjabi (pa-IN)
165 | - Persian (fa-IR)
166 | - Polish (pl-PL)
167 | - Portuguese (pt-PT)
168 | - Brazilian Portuguese (pt-BR)
169 | - Pushto; Pashto (ps-PS)
170 | - Quechua (qu-QU)
171 | - Romanian (ro-RO)
172 | - Romansh (rm-RM)
173 | - Rundi (rn-RN)
174 | - Russian (ru-RU)
175 | - Samoan (sm-SM)
176 | - Sango (sg-SG)
177 | - Sanskrit (sa-SA)
178 | - Sardinian (sc-SC)
179 | - Serbian (sr-RS)
180 | - Serbo-Croatian (sh-SH)
181 | - Shona (sn-SN)
182 | - Sichuan Yi; Nuosu (ii-II)
183 | - Sindhi (sd-SD)
184 | - Sinhala; Sinhalese (si-LK)
185 | - Slovak (sk-SK)
186 | - Slovenian (sl-SI)
187 | - Somali (so-SO)
188 | - Sotho, Southern (st-ST)
189 | - Spanish; Castilian (es-ES)
190 | - Spanish; Castilian (es-MX)
191 | - Sundanese (su-SU)
192 | - Swahili (sw-SW)
193 | - Swati (ss-SS)
194 | - Swedish (sv-SE)
195 | - Tagalog (tl-PH)
196 | - Tahitian (ty-TY)
197 | - Tajik (tg-TG)
198 | - Tamil (ta-IN)
199 | - Tatar (tt-TT)
200 | - Telugu (te-IN)
201 | - Thai (th-TH)
202 | - Tibetan (bo-BO)
203 | - Tigrinya (ti-TI)
204 | - Tonga (Tonga Islands) (to-TO)
205 | - Tsonga (ts-TS)
206 | - Tswana (tn-TN)
207 | - Turkish (tr-TR)
208 | - Turkmen (tk-TK)
209 | - Twi (tw-TW)
210 | - Uighur; Uyghur (ug-UG)
211 | - Ukrainian (uk-UA)
212 | - Urdu (ur-UR)
213 | - Uzbek (uz-UZ)
214 | - Venda (ve-VE)
215 | - Vietnamese (vi-VN)
216 | - Volapük (vo-VO)
217 | - Walloon (wa-WA)
218 | - Welsh (cy-GB)
219 | - Western Frisian (fy-FY)
220 | - Wolof (wo-WO)
221 | - Xhosa (xh-XH)
222 | - Yiddish (yi-YI)
223 | - Yoruba (yo-YO)
224 | - Zhuang; Chuang (za-ZA)
225 | - Zulu (zu-ZA)
226 | - type: checkboxes
227 | id: labels
228 | attributes:
229 | label: Choose Labels
230 | description: Choose the labels that best describe your addon.
231 | options:
232 | - label: anime
233 | - label: asian drama
234 | - label: bollywood
235 | - label: cartoons
236 | - label: debrid support
237 | - label: http streams
238 | - label: live tv
239 | - label: metadata
240 | - label: misc
241 | - label: movies
242 | - label: music
243 | - label: nsfw
244 | - label: podcasts
245 | - label: radios
246 | - label: subtitles
247 | - label: torrents
248 | - label: tv shows
249 | - label: video games
250 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Build
38 | out/
39 |
40 | # Compiled binary addons (https://nodejs.org/api/addons.html)
41 | build/Release
42 |
43 | # Dependency directories
44 | node_modules/
45 | jspm_packages/
46 |
47 | # TypeScript v1 declaration files
48 | typings/
49 |
50 | # TypeScript cache
51 | *.tsbuildinfo
52 |
53 | # Optional npm cache directory
54 | .npm
55 |
56 | # Optional eslint cache
57 | .eslintcache
58 |
59 | # Microbundle cache
60 | .rpt2_cache/
61 | .rts2_cache_cjs/
62 | .rts2_cache_es/
63 | .rts2_cache_umd/
64 |
65 | # Optional REPL history
66 | .node_repl_history
67 |
68 | # Output of 'npm pack'
69 | *.tgz
70 |
71 | # Yarn Integrity file
72 | .yarn-integrity
73 |
74 | # dotenv environment variables file
75 | .env
76 | .env.test
77 |
78 | # parcel-bundler cache (https://parceljs.org/)
79 | .cache
80 |
81 | # Next.js build output
82 | .next
83 |
84 | # Nuxt.js build / generate output
85 | .nuxt
86 | dist
87 |
88 | # Gatsby files
89 | .cache/
90 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
91 | # https://nextjs.org/blog/next-9-1#public-directory-support
92 | # public
93 |
94 | # vuepress build output
95 | .vuepress/dist
96 |
97 | # Serverless directories
98 | .serverless/
99 |
100 | # FuseBox cache
101 | .fusebox/
102 |
103 | # DynamoDB Local files
104 | .dynamodb/
105 |
106 | # TernJS port file
107 | .tern-port
108 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Stripes
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # The Great List of Stremio Addons
2 | > [!WARNING]
3 | > This repository is no longer maintained. Try out the new Stremio Community Addon list here (BETA): [beta.stremio-addons.net](https://beta.stremio-addons.net/).
4 |
5 | > [!NOTE]
6 | > Due to recent abuse of this repository (malicious addons, spam) by a user called [vancengvn](https://github.com/Vance-ng-vn) we were forced to implement a moderation system. New addon submissions now need explicit approval from a contributor to be published - unless you are on the [trusted publishers](./trusted_publishers.json) list. Please contact a moderator on Discord or Reddit if your addon hasn't been approved after 7 days.
7 |
8 | To see the great list of Stremio Addons go to [the website](https://stremio-addons.com/).
9 |
10 | To submit a new addon to the list, use [this link](https://github.com/danamag/stremio-addons-list/issues/new?assignees=&labels=pending+approval&template=submit-addon.yaml&title=Addon+Name).
11 |
12 | To upvote / downvote an addon, find it in [the issues](https://github.com/danamag/stremio-addons-list/issues) and react with a thumbs up / down to the issue comment.
13 |
14 | To comment on an addon, find it in [the issues](https://github.com/danamag/stremio-addons-list/issues) and comment on the issue, this will update the comments on the site too. (you can also comment with GitHub on the website directly)
15 |
16 | To get notifications about new addons press the "Watch" button at the top right of this page. (or [join the Discord](https://discord.gg/zNRf6YF), all new addons are announced on the #bots channel)
17 |
18 |
19 | ## How Can I Help?
20 |
21 | This project is completely automated, what addons get in the list and what addons are removed is decided by each and every one of you, the only requirement is a free GitHub profile.
22 |
23 | So here's how you can help:
24 | - add addons that are working (and not yet) in the list (by creating a new GitHub issue)
25 | - give a thumbs up / down to the addons that are already in the list (through GitHub comment reactions)
26 | - comment and discuss addons (through the GitHub commenting system)
27 |
28 |
29 | ## Project Features
30 |
31 | - anyone can publish an addon
32 | - publishers can choose labels
33 | - publishers can choose content language (if applicable)
34 | - everyone can vote on addons
35 | - all addons are ordered by community votes
36 | - addon labels
37 | - filter by addon labels
38 | - comments for addon pages
39 | - rich text comments
40 | - comments support reactions (emojis)
41 | - "is it online?" real-time check for addons (on the addon page)
42 | - notifications for new addon releases (through GitHub followship)
43 | - search addons
44 | - show comment count for each addon in the list
45 | - Discord chat notifications for new addon releases
46 |
47 | ## Addon Submission Rules
48 |
49 | We do not tolerate any abuse or misuse of user-submitted addons. The following behaviors are strictly prohibited:
50 |
51 | 1. **Prohibited Content**
52 | Users must not submit:
53 | - Abusive, harassing, discriminatory, or threatening content.
54 | - Defamatory, false, or harmful material.
55 | - Malicious code, ads, viruses, or any form of malware.
56 | - Content promoting illegal activities.
57 |
58 | 2. **Prohibited Actions**
59 | - Impersonating others or submitting misleading information.
60 | - Engaging in spamming, phishing, or fraud.
61 | - Attempting to bypass security or moderation measures.
62 |
63 | 3. **Consequences**
64 | Violation of the terms above will result in your addon being removed and unpublished from this repository. Multiple violations may result in a permanent ban. Our moderation team reserves the right to remove you from other community-moderated platforms such as (but not limited to) our Reddit and Discord communities.
65 |
66 | 4. **Reporting**
67 | Please contact a moderator to report any abusive or harmful content, including suspected malware. We will take appropriate action.
68 |
69 | *By submitting an addon to this repository, you automatically agree to the terms above.*
70 |
71 | ## How it works
72 |
73 | When submitting an addon to the list, a github issue is created to represent this submission. If the original poster closes their issue, or someone with access to the project closes the issue, the addon will be removed from the list. If the project detects an invalid submission it will automatically close the issue and set an explanatory label for the reason.
74 |
75 | All addons in the list are ordered by the thumbs up / down votes of the github issues, if an addon has less than -10 votes it is removed from the list.
76 |
77 | If an addon manifest has been unreachable for more than 10 days, it will be removed from the list.
78 |
79 | Labels for addons are a 1:1 copy of github labels used for issues, the colors chosen for these labels on github will also be used on the site.
80 |
81 | Commenting on an issue will also add the comments to the dedicated addon page on the website.
82 |
83 | The site is currently refreshed based on the following triggers:
84 | - a new issue is created (a new addon was submitted)
85 | - a new release was created
86 | - a new commit was made
87 | - a label was created, edited or removed
88 | - a new comment was made to an issue (to update comment count)
89 | - daily at 08:15 by GitHub Actions (to update votes if no other event did)
90 |
91 |
92 | ## Fork me
93 |
94 | This project is available under the MIT license and uses exclusively free resources. (GitHub WebHooks and Netlify)
95 |
96 | To create your own Stremio Addons list:
97 | - fork this project
98 | - enable issues for your fork: `Settings` > `Features` > `Issues`
99 | - edit `/config.js` with your repo information
100 | - connect Netlify to your GitHub fork (on `main` branch)
101 | - in Netlify: `Sites` > `(choose site)` > `Site Settings` > `Build & deploy` > `Build settings`: Base directory = "Not set" ; Build command = "npm run build" ; Publish directory = "out/"
102 | - create a GitHub API token: `Settings` > `Developer Settings` (bottom left) > `Personal access tokens` (left side) > `Tokens` (classic) > `Generate new token` (copy the token to clipboard)
103 | - add GitHub API token to Netlify: `Sites` > `(choose site)` > `Site Settings` > `Build & deploy` > `Environment` > `Environment Variables` > (add key called "TOKEN" and paste GitHUB API token)
104 | - create a Netlify Hook: `Sites` > `(choose site)` > `Site Settings` > `Build & deploy` > `Continuous deployment` > `Build hooks` > `Add build hook` > (copy the URL from the hook)
105 | - create a GitHub WebHook: `Settings` > `WebHooks` (left side menu) > `Add WebHook` (top right button): Payload URL = URL copied from Netlify ; choose "Let me select individual events" ; ensure "Active" is enabled
106 | - choose events that will trigger the website builds: Issues; Labels; Releases; Pushes (optional events: Issue comments)
107 | - press "Add webhook"
108 | - add GitHub Repository Secret for Netlify Hook: `Settings` > `Secrets` (left side menu) > `Actions` > `New repository secret`: Name = "NETLIFY_BUILD_WEBHOOK" ; Paste URL copied from Netlify as Secret (this is needed for the GitHub Action from `/.github/workflows/main.yml` which will do a daily build to update votes)
109 | - on Github go to: `Issues` > `Labels` (top right button) > (add labels that you need, delete labels that you don't need) (if you want to use the default labels, check the `/.github/ISSUE_TEMPLATE/submit-addon.yaml` file to see the list)
110 | - open `/.github/ISSUE_TEMPLATE/submit-addon.yaml` and edit the labels to match the ones you use for your addons list (if not using the default labels)
111 |
112 | You're done!
113 |
114 |
115 | ### Extras
116 |
117 | - you can also set the "DISCORD_WEBHOOK" environment variable in Netlify, this will make it notify on Discord every time a new addon is published (a new issue is created)
118 | - by default this project only allows submitting issues with the "Publish Stremio Addon" issue template, if you want to allow blank issues too, then edit the `/.github/ISSUE_TEMPLATE/config.yml` file and set `blank_issues_enabled: true`
119 |
--------------------------------------------------------------------------------
/build.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const slug = require('./lib/slug')
3 | const needle = require('needle')
4 | const asyncQueue = require('async.queue')
5 | const config = require('./config')
6 | const graphql = require('./lib/graphql')
7 | const getCached = require('./lib/cache')
8 | const processHtml = require('./lib/html')
9 | const issueToMeta = require('./lib/issueToMeta')
10 | const sendDiscordMessage = require('./lib/discord')
11 | const trustedPublishers = require('./trusted_publishers.json')
12 |
13 | getCached().then(cached => {
14 | if (cached.time && cached.time > Date.now() - config['prefer-cached-for']) {
15 | console.log('cache will be preferred over refreshing manifest data')
16 | cached.prefer = true
17 | }
18 | graphql.getAllPosts().then(data => {
19 | const addons = []
20 | const addons_collection = []
21 | const all_labels = [{ color: 'A08C80', name: 'show all' }]
22 | const noDups = []
23 | // issues are ordered chronologically
24 | data.forEach(addon => {
25 | const meta = issueToMeta(addon)
26 |
27 | if (meta && meta.name && meta.url) {
28 | // skip addons that are not approved (yet) by our moderators
29 | if (!(meta.labels ?? []).some(label => label.id === config['label-id-approved'])) {
30 | const publisher = (addon.author || {}).login
31 |
32 | // automatically approve addons from trusted publishers
33 | if (publisher && trustedPublishers.includes(publisher)) {
34 | console.log(`approving addon '${meta.name}' from trusted publisher '${publisher}'`)
35 | graphql.syncLabelsQueue.push({ postId: meta.postId, proposedLabels: ['approved'], allLabels: [{ name: 'approved', id: config['label-id-approved'] }] })
36 | } else {
37 | console.log(`skipping unapproved addon '${meta.name}'`)
38 | }
39 |
40 | return
41 | }
42 |
43 | if (meta.score > config['minimum-score']) {
44 | if (noDups.includes(meta.url)) {
45 | console.log('closing issue due to duplication: ' + meta.name)
46 | graphql.closeIssueQueue.push({ postId: meta.postId, label: config['label-id-for-duplicate'] })
47 | return
48 | }
49 | noDups.push(meta.url)
50 | meta.labels.forEach(label => {
51 | if (label.name && !all_labels.some(el => label.name === el.name))
52 | all_labels.push(label)
53 | })
54 | addons.push(meta)
55 | } else {
56 | console.log('closing issue due to low score: ' + meta.name)
57 | graphql.closeIssueQueue.push({ postId: meta.postId, label: config['label-id-for-low-score'] })
58 | }
59 | } else {
60 | console.log('closing issue due to submission being invalid: ' + meta.name)
61 | graphql.closeIssueQueue.push({ postId: meta.postId, label: config['label-id-for-invalid'] })
62 | }
63 | })
64 |
65 | // ensure that labels are the same as proposed by user submitting
66 | addons.forEach(addon => {
67 | if (addon.postId && addon.proposedLabels.length) {
68 | const addonLabelNames = addon.labels.map(label => label.name)
69 |
70 | // we only sync labels on approved issues
71 | if (!addonLabelNames.includes('approved')) {
72 | console.log(`skipping label sync for unapproved addon '${addon.name}'`)
73 | return
74 | }
75 |
76 | const diff = addon.proposedLabels.filter(x => !addonLabelNames.includes(x))
77 |
78 | if (diff.length) {
79 | // proposed labels are different than issue labels
80 | console.log('syncing labels for: ' + addon.name)
81 | console.log(addon.proposedLabels)
82 | graphql.syncLabelsQueue.push({ postId: addon.postId, proposedLabels: ['approved', ...addon.proposedLabels], allLabels: all_labels })
83 | }
84 |
85 | }
86 | })
87 |
88 | const dir = config['build-dir']
89 |
90 | if (!fs.existsSync(dir)) fs.mkdirSync(dir)
91 |
92 | const listHtml = []
93 | const newAddons = []
94 |
95 | const queue = asyncQueue((task, cb) => {
96 | const processManifest = (addonManifest, meta, rip) => {
97 | if (!addonManifest) {
98 | console.log('warning: could not find addon manifest for: ' + task.name)
99 | graphql.closeIssueQueue.push({ postId: meta.postId, label: rip ? config['label-id-for-inactive'] : config['label-id-for-unreachable'] })
100 | cb()
101 | return
102 | }
103 | if (addonManifest.name && addonManifest.name !== meta.name) {
104 | console.log('warning: github issue name is different than addon name: ' + task.name)
105 | graphql.updateTitleQueue.push({ postId: meta.postId, title: addonManifest.name })
106 | }
107 | addons_collection.push({
108 | transportUrl: task.url,
109 | transportName: 'http',
110 | manifest: addonManifest,
111 | })
112 | if (cached.catalog.length && !cached.catalog.find(el => ((el || {}).manifest || {}).id === addonManifest.id)) {
113 | task.manifest = addonManifest
114 | if (!config.blockedManifests.includes(task.url))
115 | newAddons.push(task)
116 | }
117 |
118 | task.labels.pop('approved') // we shouldn't show the "approved" label on the addon page
119 | let labelsHtml = task.labels.map(el => el.name.split(' ').join('-')).join(' ')
120 | if (labelsHtml) labelsHtml = ' ' + labelsHtml
121 |
122 | const lowerCaseName = addonManifest.name.toLowerCase()
123 | const keywordsForAddonPage = config['addon-keywords'].split('{}').join(lowerCaseName)
124 |
125 | const installButton = !(addonManifest.behaviorHints || {}).configurationRequire ? 'Install Install (Web) Copy Link ' : '';
126 | const configButton = (addonManifest.behaviorHints || {}).configurable ? 'Configure ' : ''
127 | const commentsButton = task.commentCount ? ` ${task.commentCount} ` : ''
128 | const language = task.language && task.language !== 'Multilingual' && task.language !== 'None' ? `
${task.language} Content
` : ''
129 | const addonsScoreFaded = !task.ups && !task.downs ? ' addon-score-faded' : ''
130 |
131 | const labelsForHomeHeader = task.labels.map(el => `${el.name} `).join('')
132 | const labelsForHomeAddon = task.labels.map(el => `${el.name} `).join('')
133 |
134 | const map = {
135 | '{home-netlify-domain}': config['netlify-domain'],
136 | '{addon-page-title-append}': config['meta-addon-title-append'],
137 | '{labels}': labelsHtml,
138 | '{addon-id}': addonManifest.id,
139 | '{addon-version}': addonManifest.version,
140 | '{addon-title}': addonManifest.name,
141 | '{addon-description}': addonManifest.description || '',
142 | '{addon-keywords}': keywordsForAddonPage,
143 | '{addon-logo}': addonManifest.logo || addonManifest.icon,
144 | '{addon-types}': labelsForHomeHeader,
145 | '{addon-types-small}': labelsForHomeAddon,
146 | '{addon-score}': task.score,
147 | '{addon-ups}': task.ups,
148 | '{addon-downs}': task.downs,
149 | '{addons-score-faded}': addonsScoreFaded,
150 | '{install-button}': installButton,
151 | '{configure-button}': configButton,
152 | '{comments-button}': commentsButton,
153 | '{addon-page}': `${slug(addonManifest.name)}.html`,
154 | '{issue-url}': task.issueUrl,
155 | '{issue-number}': task.issueNumber,
156 | '{repo-name}': config.author+'/'+config.repository,
157 | '{addon-language}': language,
158 | '{addon-url}': task.url,
159 | }
160 |
161 | const addonHtml = processHtml('homePageAddon', map)
162 |
163 | task.labels = [{ color: 'A08C80', name: ' all addons' }].concat(task.labels)
164 | const labelsForAddonPage = task.labels.map(el => `<${'a href="https://' + config['netlify-domain'] + '/' + (el.name === ' all addons' ? '' : '?label=' + el.name.split(' ').join('-')) + '"'} class="label label-addon-page" style="background-color: #${el.color}">${el.name}`).join('')
165 | map['{addon-types-links}'] = labelsForAddonPage
166 |
167 | const parsedAddonPage = processHtml('addonPage', map)
168 |
169 | console.log('creating page for addon: ' + addonManifest.name)
170 | fs.writeFileSync(`${dir}/${slug(addonManifest.name)}.html`, parsedAddonPage)
171 | task.labels.shift() // remove "all addons" prefix from labels
172 | listHtml.push(addonHtml)
173 | cb()
174 | }
175 | const findCachedManifest = () => {
176 | let cachedManifest
177 | cached.catalog.some(oldAddon => {
178 | if (oldAddon.transportUrl === task.url) {
179 | if (!cached.prefer)
180 | console.log('warning: using cached manifest for: ' + task.name)
181 | cachedManifest = oldAddon.manifest
182 | return true
183 | }
184 | })
185 | return cachedManifest
186 | }
187 | if (cached.prefer) {
188 | const cachedManifest = findCachedManifest()
189 | if (cachedManifest) {
190 | processManifest(cachedManifest, task)
191 | return
192 | }
193 | }
194 | needle.get(task.url, config.needle, (err, resp, body) => {
195 | let addonManifest
196 | let rip
197 | if ((body || {}).id && body.version) {
198 | addonManifest = body
199 | cached.lastReached[task.url] = Date.now()
200 | } else if (cached.catalog.length) {
201 | if (cached.lastReached[task.url] && Date.now() - cached.lastReached[task.url] > config['maximum-unreachable']) {
202 | rip = true
203 | } else {
204 | addonManifest = findCachedManifest()
205 | }
206 | }
207 | processManifest(addonManifest, task, rip)
208 | })
209 | }, 1)
210 |
211 | queue.drain = () => {
212 | if (config.DISCORD_WEBHOOK && newAddons.length)
213 | sendDiscordMessage(newAddons.filter(addon => !config.blockedManifests.includes(addon.url)))
214 | console.log('copying resources (styles, js, images)')
215 | fs.readdirSync('./resources').forEach(file => {
216 | const filePath = `./resources/${file}`
217 | if (fs.existsSync(filePath) && !fs.lstatSync(filePath).isDirectory()) {
218 | console.log(`copied ${file} resource`)
219 | fs.copyFileSync(filePath, `${dir}/${file}`)
220 | }
221 | })
222 | fs.copyFileSync('./resources/styles.css', `${dir}/styles.css`)
223 |
224 | const manifest = {
225 | id: 'community.stremio.stremio-addons-list',
226 | version: '1.0.0',
227 | name: 'Stremio Community Addons List',
228 | description: 'Stremio Community Addons List',
229 | types: ["movie", "series", "channel", "tv"],
230 | resources: ['addon_catalog'],
231 | catalogs: [],
232 | addonCatalogs: [{
233 | type: 'all',
234 | id: 'community',
235 | name: 'Community (External)',
236 | }],
237 | };
238 |
239 | console.log('creating manifest file')
240 | fs.writeFileSync(`${dir}/manifest.json`, JSON.stringify(manifest))
241 |
242 | console.log('creating addon_catalog file')
243 | fs.mkdirSync(`${dir}/addon_catalog/all`, { recursive: true });
244 | fs.writeFileSync(`${dir}/addon_catalog/all/community.json`, JSON.stringify({
245 | addons: addons_collection
246 | }))
247 |
248 | console.log('creating addons catalog json file')
249 | fs.writeFileSync(`${dir}/catalog.json`, JSON.stringify(addons_collection))
250 |
251 | console.log('creating last reached json file')
252 | fs.writeFileSync(`${dir}/lastReached.json`, JSON.stringify(cached.lastReached))
253 |
254 | console.log('creating home page')
255 | // move "misc" label to end of list
256 | const miscLabelIndex = all_labels.findIndex(label => label.name === 'misc')
257 | if (miscLabelIndex > -1)
258 | all_labels.push(all_labels.splice(miscLabelIndex, 1)[0])
259 | const map = {
260 | '{home-keywords}': config['meta-keywords'],
261 | '{home-page-title}': config['page-title'],
262 | '{home-meta-title}': config['meta-title'],
263 | '{home-netlify-domain}': config['netlify-domain'],
264 | '{home-favicon}': config['meta-favicon'],
265 | '{home-description}': config['meta-description'],
266 | '{repo-name}': config.author+'/'+config.repository,
267 | '{labels-list}': all_labels.filter(label => label.name !== "approved").map((el, ij) => `${el.name} `).join(''),
268 | '{addons-list}': listHtml.join(''),
269 | }
270 | const homePage = processHtml('homePage', map)
271 | fs.writeFileSync(`${dir}/index.html`, homePage)
272 | if (!cached.prefer) {
273 | console.log('saving timestamp of last update to json')
274 | fs.writeFileSync(`${dir}/lastUpdate.json`, JSON.stringify({ time: Date.now() }))
275 | } else {
276 | console.log('persisting last known update time because cache was preferred')
277 | fs.writeFileSync(`${dir}/lastUpdate.json`, JSON.stringify({ time: cached.time }))
278 | }
279 | }
280 |
281 | addons.sort((a,b) => { return a.score > b.score ? -1 : 1 })
282 |
283 | addons.forEach(addon => queue.push(addon))
284 |
285 | }).catch(e => console.error(e))
286 | }).catch(e => console.error(e))
287 |
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config()
2 |
3 | module.exports = {
4 | // env vars
5 | "DISCORD_WEBHOOK": process.env.DISCORD_WEBHOOK,
6 | "GITHUB_TOKEN": process.env.TOKEN,
7 | // for repo: danamag/stremio-addons-list
8 | "repository": process.env.REPO || "stremio-addons-list",
9 | "author": process.env.REPO_AUTHOR || "danamag",
10 | "netlify-domain": process.env.DOMAIN || "stremio-addons.com",
11 | "page-title": "Stremio Community Addons List",
12 | // images are located in ./resources/
13 | "meta-favicon": "stremio_community_logo.png",
14 | "meta-title": "Stremio Addons - Community List",
15 | "meta-description": "Stremio community currated addons list, find the best Stremio addons here.",
16 | "meta-keywords": "stremio addons, no streams, addons, torrentio, piratebay, addons list, what addons",
17 | // for the addon page, {} is replaced with the addon name
18 | "addon-keywords": "{}, {} down, {} down or just me, {} site down, {} not working, {} not found, stremio addons, addons list",
19 | // this gets appended to the title, ex: "TMDB Addon - Stremio Addons"
20 | "meta-addon-title-append": "Stremio Addons",
21 | // for 12h since last full update, it will prefer cached manifests over retrieving new ones
22 | "prefer-cached-for": 12 * 60 * 60 * 1000,
23 | // when min score is reached the addon will be removed / issue closed
24 | "minimum-score": -10,
25 | // when an addon is unreachable for 10 days, remove the submission
26 | "maximum-unreachable": 10 * 24 * 60 * 60 * 1000,
27 | // optional label id, if available it will add a label when closing an issue due to various reasons
28 | // you can only get the label id through the github graphql api
29 | "label-id-for-low-score": "LA_kwDOFVUyTM8AAAABGbO_Bw", // "very low score"
30 | "label-id-for-duplicate": "LA_kwDOFVUyTM8AAAABP8VfgA", // "duplicate"
31 | "label-id-for-invalid": "LA_kwDOFVUyTM8AAAABP8VmVQ", // "invalid addon url"
32 | "label-id-for-inactive": "LA_kwDOFVUyTM8AAAABP8VsIw", // "addon inactive"
33 | "label-id-for-unreachable": "LA_kwDOFVUyTM8AAAABP8Zl7g", // "addon manifest unreachable"
34 | "label-id-approved": "LA_kwDOFVUyTM8AAAABwTaXuQ", // "approved"
35 | "label-id-pending-approval": "LA_kwDOFVUyTM8AAAABwUPwvQ", // "pending approval"
36 | // sane timeouts for needle so it doesn't get stuck in a request
37 | "needle": { "open_timeout": 5000, "response_timeout": 5000, "read_timeout": 5000, "follow_max": 5 },
38 | // output folder for build
39 | "build-dir": "./out",
40 | "blockedManifests": [
41 | "https://comet.elfhosted.com/manifest.json",
42 | "https://comet.stremiofr.com/manifest.json",
43 | ]
44 | }
45 |
--------------------------------------------------------------------------------
/lib/cache.js:
--------------------------------------------------------------------------------
1 | // we get the old files from the deployment
2 | const needle = require('needle')
3 | const config = require('../config')
4 |
5 | function isObject(obj) {
6 | return typeof obj === 'object' &&
7 | !Array.isArray(obj) &&
8 | obj !== null
9 | }
10 |
11 | const getCached = () => {
12 | return new Promise((resolve, reject) => {
13 | const cached = { catalog: [] }
14 | needle.get(`https://${config['netlify-domain']}/lastUpdate.json`, config.needle, (err, resp, body) => {
15 | // if an update was done less than 12h ago, then prefer updating from cache
16 | if ((body || {}).time) {
17 | cached.time = body.time
18 | console.log('got last known website update time')
19 | } else {
20 | console.log('warning: could not get last update time')
21 | }
22 |
23 | needle.get(`https://${config['netlify-domain']}/catalog.json`, config.needle, (err, resp, body) => {
24 | if ((body || []).length && body[0].transportUrl && body[0].transportName && body[0].manifest) {
25 | console.log('loaded old addon catalog')
26 | cached.catalog = body
27 | } else {
28 | console.log('warning: could not load old addon catalog')
29 | }
30 |
31 | needle.get(`https://${config['netlify-domain']}/lastReached.json`, config.needle, (err, resp, body) => {
32 | if (body && isObject(body) && Object.keys(body).length) {
33 | console.log('loaded last reached data')
34 | cached.lastReached = body
35 | } else {
36 | cached.lastReached = {}
37 | console.log('warning: could not load last reached data')
38 | }
39 | resolve(cached)
40 | })
41 | })
42 | })
43 | })
44 | }
45 |
46 | module.exports = getCached
47 |
--------------------------------------------------------------------------------
/lib/discord.js:
--------------------------------------------------------------------------------
1 | const config = require('../config')
2 | const slug = require('../lib/slug')
3 | const needle = require('needle')
4 |
5 | const discordGreeting = () => {
6 | const greetings = ["Hey", "Sup", "Yo", "Knock knock", "Attention", "Yesss", "Oh yeah", "Awesome", "Finally", "Arr", "Ahoy, matey", "Put that cookie down"]
7 | return greetings[Math.floor(Math.random()*greetings.length)]
8 | }
9 |
10 | const sendDiscordMessage = (addons) => {
11 | const payload = {
12 | content: `${discordGreeting()}, ${addons.length > 1 ? ' new addons' : 'a new addon'} appeared in the catalog!`,
13 | embeds: []
14 | }
15 | addons.forEach(addon => {
16 | const manifest = addon.manifest
17 | const addonUrl = `https://${config['netlify-domain']}/${slug(addon.name)}.html`
18 | const embed = {
19 | title: manifest.name,
20 | url: addonUrl,
21 | thumbnail: {
22 | url: manifest.logo,
23 | },
24 | description: manifest.description,
25 | color: 9001641,
26 | provider: {
27 | name: 'Install',
28 | url: addonUrl,
29 | }
30 | }
31 | const labels = addon.proposedLabels.length ? addon.proposedLabels : (addon.labels || []).map(label => label.name).filter(el => !! el)
32 | if ((labels || []).length)
33 | embed.fields = [
34 | {
35 | name: 'Type',
36 | value: labels.map(label => label.toLowerCase()).join(', '),
37 | },
38 | ]
39 | payload.embeds.push(embed)
40 | })
41 | const needleOpts = Object.assign({}, config.needle)
42 | needleOpts.json = true
43 | console.log(`sending discord message about ${addons.length} new addons`)
44 | needle.post(config.DISCORD_WEBHOOK, payload, needleOpts, (err, resp, body) => {
45 | if (!err)
46 | console.log(`sent discord message about ${addons.length} new addons`)
47 | else {
48 | console.log(`error when sending discord message about ${addons.length} new addons`)
49 | console.error(err)
50 | }
51 | })
52 | }
53 |
54 | module.exports = sendDiscordMessage
55 |
--------------------------------------------------------------------------------
/lib/graphql.js:
--------------------------------------------------------------------------------
1 | const { graphql } = require('@octokit/graphql')
2 | const asyncQueue = require('async.queue')
3 | const config = require('../config')
4 |
5 | const request = graphql.defaults({
6 | headers: {
7 | authorization: `token ${config.GITHUB_TOKEN}`,
8 | },
9 | })
10 |
11 | const getPosts = (after) =>
12 | request(
13 | `{
14 | repository(name: "${config.repository}", owner: "${config.author}") {
15 | issues(states: [OPEN], first: 100${after ? ', after: "' + after + '"' : ''}) {
16 | pageInfo {
17 | hasNextPage
18 | endCursor
19 | }
20 | nodes {
21 | id
22 | title
23 | number
24 | createdAt
25 | url
26 | body
27 | labels(first: 20) {
28 | nodes {
29 | color
30 | name
31 | id
32 | }
33 | }
34 | comments {
35 | totalCount
36 | }
37 | reactionGroups {
38 | content
39 | users {
40 | totalCount
41 | }
42 | }
43 | author {
44 | login
45 | }
46 | }
47 | }
48 | }
49 | }
50 | `
51 | ).then((data) => data.repository.issues)
52 |
53 |
54 | const getAllPosts = () => {
55 | return new Promise((resolve, reject) => {
56 | const allItems = []
57 | const loopPages = after => {
58 | getPosts(after).then(data => {
59 | data = data || {}
60 | data.nodes = data.nodes || []
61 | data.nodes.forEach(node => allItems.push(node))
62 | if ((data.pageInfo || {}).hasNextPage && data.pageInfo.endCursor)
63 | loopPages(data.pageInfo.endCursor)
64 | else
65 | resolve(allItems)
66 | })
67 | }
68 | loopPages()
69 | })
70 | }
71 |
72 | const syncLabels = (postId, proposedLabels, allLabels) => {
73 | const labels = proposedLabels.map(el => allLabels.find(elm => elm.name === el)).filter(el => !!el).map(el => el.id)
74 | if (!labels.length) return Promise.reject(Error('error: could not find any label id in order to update issue labels'));
75 | return request(
76 | `mutation {
77 | updateIssue(input: {id : "${postId}" , labelIds: ${JSON.stringify(labels)} }){
78 | issue {
79 | id
80 | title
81 | }
82 | }
83 | }
84 | `
85 | )
86 | }
87 |
88 | const updateTitle = (postId, title) => {
89 | return request(
90 | `mutation {
91 | updateIssue(input: {id : "${postId}" , title: "${title}" }){
92 | issue {
93 | id
94 | title
95 | }
96 | }
97 | }
98 | `
99 | )
100 | }
101 |
102 | const closeIssueQueue = asyncQueue((task, cb) => {
103 | closeIssue(task.postId, task.label).then(() => { cb() }).catch(() => { cb() })
104 | })
105 |
106 | const closeIssue = (postId, label) => {
107 | // also adds label to the issue if label id provided
108 | return request(
109 | `mutation {
110 | updateIssue(input: {id : "${postId}" , state: CLOSED${label ? ', labelIds: ["' + label + '"]' : ''} }){
111 | issue {
112 | id
113 | title
114 | }
115 | }
116 | }
117 | `)
118 | }
119 |
120 | const syncLabelsQueue = asyncQueue((task, cb) => {
121 | syncLabels(task.postId, task.proposedLabels, task.allLabels).then(() => { cb() }).catch(() => { cb() })
122 | })
123 |
124 | const updateTitleQueue = asyncQueue((task, cb) => {
125 | updateTitle(task.postId, task.title).then(() => { cb() }).catch(() => { cb() })
126 | })
127 |
128 | module.exports = {
129 | getPosts, getAllPosts, syncLabels, syncLabelsQueue, updateTitleQueue, closeIssue, closeIssueQueue,
130 | }
131 |
--------------------------------------------------------------------------------
/lib/html.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const path = require('path')
3 |
4 | const templateFolder = path.join(__dirname, '..', 'template')
5 |
6 | const addonPageTemplate = fs.readFileSync(path.join(templateFolder, 'addon', 'index.html')).toString()
7 | const homePageTemplate = fs.readFileSync(path.join(templateFolder, 'home', 'index.html')).toString()
8 | const homePageAddonTemplate = fs.readFileSync(path.join(templateFolder, 'home', 'list-addon.html')).toString()
9 |
10 | const processHtml = (templateName, map) => {
11 | let template
12 | if (templateName === 'addonPage')
13 | template = addonPageTemplate
14 | else if (templateName === 'homePage')
15 | template = homePageTemplate
16 | else if (templateName === 'homePageAddon')
17 | template = homePageAddonTemplate
18 |
19 | const re = new RegExp(Object.keys(map).join('|'),'gi')
20 | return template.replace(re, matched => map[matched])
21 | }
22 |
23 | module.exports = processHtml
24 |
--------------------------------------------------------------------------------
/lib/issueToMeta.js:
--------------------------------------------------------------------------------
1 | const config = require('../config')
2 |
3 | const issueToMeta = issue => {
4 | const meta = {
5 | name: issue.title,
6 | url: '',
7 | description: '',
8 | ups: 0,
9 | downs: 0,
10 | commentCount: 0,
11 | issueUrl: issue.url,
12 | proposedLabels: [],
13 | language: 'Multilingual',
14 | }
15 | const chunks = (issue.body || '').split(/\r?\n/)
16 | let readingFor = false
17 | chunks.forEach(chunk => {
18 | if (chunk === '### Addon Manifest URL')
19 | readingFor = 'url'
20 | else if (chunk === '### Addon Description')
21 | readingFor = 'description'
22 | else if (chunk === '### Language of Content')
23 | readingFor = 'language'
24 | else if (chunk === '### Choose Labels')
25 | readingFor = 'labels'
26 | else if (readingFor && chunk) {
27 | if (readingFor === 'url' && meta.url.endsWith('/manifest.json')) return;
28 | if (readingFor === 'labels' && chunk.toLowerCase().startsWith('- [x] ')) {
29 | meta.proposedLabels.push(chunk.replace('- [X] ','').replace('- [x] ', '').trim())
30 | return
31 | }
32 | if (readingFor === 'language') {
33 | lang = chunk.split('; ')[0].split(' (')[0].trim()
34 | if (lang !== '_No response_')
35 | meta[readingFor] = lang
36 | return
37 | }
38 | meta[readingFor] += chunk
39 | meta[readingFor] = meta[readingFor].trim()
40 | }
41 | })
42 | if (!meta.url.startsWith('https://') || !meta.url.endsWith('/manifest.json'))
43 | meta.url = ''
44 | if (meta.description === '_No response_')
45 | meta.description = ''
46 | if (meta.name && meta.url) {
47 | const reactionGroups = issue.reactionGroups || []
48 | meta.labels = (issue.labels || {}).nodes || []
49 | let score = 0
50 | reactionGroups.forEach(group => {
51 | if ((group.users || {}).totalCount) {
52 | if (group.content === 'THUMBS_UP') {
53 | meta.ups = group.users.totalCount
54 | score += group.users.totalCount
55 | } else if (group.content === 'THUMBS_DOWN') {
56 | meta.downs = group.users.totalCount
57 | score -= group.users.totalCount
58 | }
59 | }
60 | })
61 | meta.issueNumber = issue.number
62 | meta.commentCount = (issue.comments || {}).totalCount || 0
63 | meta.postId = issue.id
64 | meta.score = score
65 | return meta
66 | } else return { postId: issue.id }
67 | }
68 |
69 | module.exports = issueToMeta
70 |
--------------------------------------------------------------------------------
/lib/slug.js:
--------------------------------------------------------------------------------
1 | const _slug = require('slug')
2 |
3 | _slug.charmap['+'] = 'plus'
4 |
5 | const slug = (string, opts) => _slug(string, opts)
6 |
7 | module.exports = slug
8 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [[headers]]
2 | # Define which paths this specific [[headers]] block will cover.
3 | for = "/*"
4 | [headers.values]
5 | Access-Control-Allow-Origin = "*"
6 |
--------------------------------------------------------------------------------
/out/folder:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "stremio-addons-list",
3 | "version": "1.0.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "stremio-addons-list",
9 | "version": "1.0.0",
10 | "license": "MIT",
11 | "dependencies": {
12 | "@octokit/graphql": "^4.5.2",
13 | "async.queue": "0.5.2",
14 | "dotenv": "^16.4.7",
15 | "needle": "3.1.0",
16 | "slug": "8.2.2"
17 | },
18 | "engines": {
19 | "node": "16.18.0"
20 | }
21 | },
22 | "node_modules/@octokit/endpoint": {
23 | "version": "6.0.12",
24 | "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz",
25 | "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==",
26 | "dependencies": {
27 | "@octokit/types": "^6.0.3",
28 | "is-plain-object": "^5.0.0",
29 | "universal-user-agent": "^6.0.0"
30 | }
31 | },
32 | "node_modules/@octokit/graphql": {
33 | "version": "4.5.2",
34 | "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.2.tgz",
35 | "integrity": "sha512-SpB/JGdB7bxRj8qowwfAXjMpICUYSJqRDj26MKJAryRQBqp/ZzARsaO2LEFWzDaps0FLQoPYVGppS0HQXkBhdg==",
36 | "dependencies": {
37 | "@octokit/request": "^5.3.0",
38 | "@octokit/types": "^5.0.0",
39 | "universal-user-agent": "^6.0.0"
40 | }
41 | },
42 | "node_modules/@octokit/graphql/node_modules/@octokit/types": {
43 | "version": "5.5.0",
44 | "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz",
45 | "integrity": "sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ==",
46 | "dependencies": {
47 | "@types/node": ">= 8"
48 | }
49 | },
50 | "node_modules/@octokit/openapi-types": {
51 | "version": "12.11.0",
52 | "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz",
53 | "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ=="
54 | },
55 | "node_modules/@octokit/request": {
56 | "version": "5.6.3",
57 | "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
58 | "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
59 | "dependencies": {
60 | "@octokit/endpoint": "^6.0.1",
61 | "@octokit/request-error": "^2.1.0",
62 | "@octokit/types": "^6.16.1",
63 | "is-plain-object": "^5.0.0",
64 | "node-fetch": "^2.6.7",
65 | "universal-user-agent": "^6.0.0"
66 | }
67 | },
68 | "node_modules/@octokit/request-error": {
69 | "version": "2.1.0",
70 | "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
71 | "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
72 | "dependencies": {
73 | "@octokit/types": "^6.0.3",
74 | "deprecation": "^2.0.0",
75 | "once": "^1.4.0"
76 | }
77 | },
78 | "node_modules/@octokit/types": {
79 | "version": "6.41.0",
80 | "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz",
81 | "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==",
82 | "dependencies": {
83 | "@octokit/openapi-types": "^12.11.0"
84 | }
85 | },
86 | "node_modules/@types/node": {
87 | "version": "18.11.5",
88 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.5.tgz",
89 | "integrity": "sha512-3JRwhbjI+cHLAkUorhf8RnqUbFXajvzX4q6fMn5JwkgtuwfYtRQYI3u4V92vI6NJuTsbBQWWh3RZjFsuevyMGQ=="
90 | },
91 | "node_modules/async.queue": {
92 | "version": "0.5.2",
93 | "resolved": "https://registry.npmjs.org/async.queue/-/async.queue-0.5.2.tgz",
94 | "integrity": "sha512-SX5gCWh47bIRLqHAt/zfJmRl2xpCF4OM4kkT3HehVJLmgQfLzYjL6QhLHe+SS4e7FOYU6NgMy2kXPB2wVchncg==",
95 | "dependencies": {
96 | "async.util.queue": "0.5.2"
97 | }
98 | },
99 | "node_modules/async.util.arrayeach": {
100 | "version": "0.5.2",
101 | "resolved": "https://registry.npmjs.org/async.util.arrayeach/-/async.util.arrayeach-0.5.2.tgz",
102 | "integrity": "sha512-PIb4rVYjwzLqb93XX2wj0+mA9YTgSWtxQRKxtuLqxXvGj1xZMB6qJUfr1NhS+FSaMPJIE1tF40Gl/o/vlfIz/A=="
103 | },
104 | "node_modules/async.util.isarray": {
105 | "version": "0.5.2",
106 | "resolved": "https://registry.npmjs.org/async.util.isarray/-/async.util.isarray-0.5.2.tgz",
107 | "integrity": "sha512-wbUzlrwON8RUgi+v/rhF0U99Ce8Osjcn+JP/mFNg6ymvShcobAOvE6cvLajSY5dPqKCOE1xfdhefgBif11zZgw=="
108 | },
109 | "node_modules/async.util.map": {
110 | "version": "0.5.2",
111 | "resolved": "https://registry.npmjs.org/async.util.map/-/async.util.map-0.5.2.tgz",
112 | "integrity": "sha512-uXZhyNIH9Jo25jn35lUoEwFLAdZWC2ZQKjLO5PLq8VAisfW6qvSfgDLH4H57/WQSKZSo6OOmuqGhtdvIHDTi1Q=="
113 | },
114 | "node_modules/async.util.noop": {
115 | "version": "0.5.2",
116 | "resolved": "https://registry.npmjs.org/async.util.noop/-/async.util.noop-0.5.2.tgz",
117 | "integrity": "sha512-AdwShXwE0KoskgqVJAck8zcM32nIHj3AC8ZN62ZaR5srhrY235Nw18vOJZWxcOfhxdVM0hRVKM8kMx7lcl7cCQ=="
118 | },
119 | "node_modules/async.util.onlyonce": {
120 | "version": "0.5.2",
121 | "resolved": "https://registry.npmjs.org/async.util.onlyonce/-/async.util.onlyonce-0.5.2.tgz",
122 | "integrity": "sha512-UgQvkU9JZ+I0Cm1f56XyGXcII+J3d/5XWUuHpcevlItuA3WFSJcqZrsyAUck2FkRSD8BwYQX1zUTDp3SJMVESg=="
123 | },
124 | "node_modules/async.util.queue": {
125 | "version": "0.5.2",
126 | "resolved": "https://registry.npmjs.org/async.util.queue/-/async.util.queue-0.5.2.tgz",
127 | "integrity": "sha512-DlKOFnhCzERL9D3bLKlNdgXwSckckcj+XkCvNuX4KMs4brBc2lHvPg8MK4NoPIhwAvUBGvE4NECdNRY0I5UOEQ==",
128 | "dependencies": {
129 | "async.util.arrayeach": "0.5.2",
130 | "async.util.isarray": "0.5.2",
131 | "async.util.map": "0.5.2",
132 | "async.util.noop": "0.5.2",
133 | "async.util.onlyonce": "0.5.2",
134 | "async.util.setimmediate": "0.5.2"
135 | }
136 | },
137 | "node_modules/async.util.setimmediate": {
138 | "version": "0.5.2",
139 | "resolved": "https://registry.npmjs.org/async.util.setimmediate/-/async.util.setimmediate-0.5.2.tgz",
140 | "integrity": "sha512-aCYF85ZFCQ9Xn0106GcOVx+LvFguIIzfbfRTOlQoie3G4KeSjURfA6f7CfpFAF09FNP2A1MtdjeFdvYeTGDebw=="
141 | },
142 | "node_modules/debug": {
143 | "version": "3.2.7",
144 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
145 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
146 | "dependencies": {
147 | "ms": "^2.1.1"
148 | }
149 | },
150 | "node_modules/deprecation": {
151 | "version": "2.3.1",
152 | "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
153 | "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
154 | },
155 | "node_modules/dotenv": {
156 | "version": "16.4.7",
157 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
158 | "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
159 | "engines": {
160 | "node": ">=12"
161 | },
162 | "funding": {
163 | "url": "https://dotenvx.com"
164 | }
165 | },
166 | "node_modules/iconv-lite": {
167 | "version": "0.6.3",
168 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
169 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
170 | "dependencies": {
171 | "safer-buffer": ">= 2.1.2 < 3.0.0"
172 | },
173 | "engines": {
174 | "node": ">=0.10.0"
175 | }
176 | },
177 | "node_modules/is-plain-object": {
178 | "version": "5.0.0",
179 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
180 | "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
181 | "engines": {
182 | "node": ">=0.10.0"
183 | }
184 | },
185 | "node_modules/ms": {
186 | "version": "2.1.3",
187 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
188 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
189 | },
190 | "node_modules/needle": {
191 | "version": "3.1.0",
192 | "resolved": "https://registry.npmjs.org/needle/-/needle-3.1.0.tgz",
193 | "integrity": "sha512-gCE9weDhjVGCRqS8dwDR/D3GTAeyXLXuqp7I8EzH6DllZGXSUyxuqqLh+YX9rMAWaaTFyVAg6rHGL25dqvczKw==",
194 | "dependencies": {
195 | "debug": "^3.2.6",
196 | "iconv-lite": "^0.6.3",
197 | "sax": "^1.2.4"
198 | },
199 | "bin": {
200 | "needle": "bin/needle"
201 | },
202 | "engines": {
203 | "node": ">= 4.4.x"
204 | }
205 | },
206 | "node_modules/node-fetch": {
207 | "version": "2.6.7",
208 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
209 | "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
210 | "dependencies": {
211 | "whatwg-url": "^5.0.0"
212 | },
213 | "engines": {
214 | "node": "4.x || >=6.0.0"
215 | },
216 | "peerDependencies": {
217 | "encoding": "^0.1.0"
218 | },
219 | "peerDependenciesMeta": {
220 | "encoding": {
221 | "optional": true
222 | }
223 | }
224 | },
225 | "node_modules/once": {
226 | "version": "1.4.0",
227 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
228 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
229 | "dependencies": {
230 | "wrappy": "1"
231 | }
232 | },
233 | "node_modules/safer-buffer": {
234 | "version": "2.1.2",
235 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
236 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
237 | },
238 | "node_modules/sax": {
239 | "version": "1.2.4",
240 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
241 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
242 | },
243 | "node_modules/slug": {
244 | "version": "8.2.2",
245 | "resolved": "https://registry.npmjs.org/slug/-/slug-8.2.2.tgz",
246 | "integrity": "sha512-5ByW6qXqPeG0Tmlkh24JhdXhvQsbaJSjVr3GgGxUV0BSskZKKBZZfFWxezap8+fh1vxBN9GVbqI1V6nqAFxlBg==",
247 | "bin": {
248 | "slug": "cli.js"
249 | }
250 | },
251 | "node_modules/tr46": {
252 | "version": "0.0.3",
253 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
254 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
255 | },
256 | "node_modules/universal-user-agent": {
257 | "version": "6.0.0",
258 | "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
259 | "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
260 | },
261 | "node_modules/webidl-conversions": {
262 | "version": "3.0.1",
263 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
264 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
265 | },
266 | "node_modules/whatwg-url": {
267 | "version": "5.0.0",
268 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
269 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
270 | "dependencies": {
271 | "tr46": "~0.0.3",
272 | "webidl-conversions": "^3.0.0"
273 | }
274 | },
275 | "node_modules/wrappy": {
276 | "version": "1.0.2",
277 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
278 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
279 | }
280 | },
281 | "dependencies": {
282 | "@octokit/endpoint": {
283 | "version": "6.0.12",
284 | "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz",
285 | "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==",
286 | "requires": {
287 | "@octokit/types": "^6.0.3",
288 | "is-plain-object": "^5.0.0",
289 | "universal-user-agent": "^6.0.0"
290 | }
291 | },
292 | "@octokit/graphql": {
293 | "version": "4.5.2",
294 | "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.2.tgz",
295 | "integrity": "sha512-SpB/JGdB7bxRj8qowwfAXjMpICUYSJqRDj26MKJAryRQBqp/ZzARsaO2LEFWzDaps0FLQoPYVGppS0HQXkBhdg==",
296 | "requires": {
297 | "@octokit/request": "^5.3.0",
298 | "@octokit/types": "^5.0.0",
299 | "universal-user-agent": "^6.0.0"
300 | },
301 | "dependencies": {
302 | "@octokit/types": {
303 | "version": "5.5.0",
304 | "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz",
305 | "integrity": "sha512-UZ1pErDue6bZNjYOotCNveTXArOMZQFG6hKJfOnGnulVCMcVVi7YIIuuR4WfBhjo7zgpmzn/BkPDnUXtNx+PcQ==",
306 | "requires": {
307 | "@types/node": ">= 8"
308 | }
309 | }
310 | }
311 | },
312 | "@octokit/openapi-types": {
313 | "version": "12.11.0",
314 | "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz",
315 | "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ=="
316 | },
317 | "@octokit/request": {
318 | "version": "5.6.3",
319 | "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
320 | "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
321 | "requires": {
322 | "@octokit/endpoint": "^6.0.1",
323 | "@octokit/request-error": "^2.1.0",
324 | "@octokit/types": "^6.16.1",
325 | "is-plain-object": "^5.0.0",
326 | "node-fetch": "^2.6.7",
327 | "universal-user-agent": "^6.0.0"
328 | }
329 | },
330 | "@octokit/request-error": {
331 | "version": "2.1.0",
332 | "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
333 | "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
334 | "requires": {
335 | "@octokit/types": "^6.0.3",
336 | "deprecation": "^2.0.0",
337 | "once": "^1.4.0"
338 | }
339 | },
340 | "@octokit/types": {
341 | "version": "6.41.0",
342 | "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz",
343 | "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==",
344 | "requires": {
345 | "@octokit/openapi-types": "^12.11.0"
346 | }
347 | },
348 | "@types/node": {
349 | "version": "18.11.5",
350 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.5.tgz",
351 | "integrity": "sha512-3JRwhbjI+cHLAkUorhf8RnqUbFXajvzX4q6fMn5JwkgtuwfYtRQYI3u4V92vI6NJuTsbBQWWh3RZjFsuevyMGQ=="
352 | },
353 | "async.queue": {
354 | "version": "0.5.2",
355 | "resolved": "https://registry.npmjs.org/async.queue/-/async.queue-0.5.2.tgz",
356 | "integrity": "sha512-SX5gCWh47bIRLqHAt/zfJmRl2xpCF4OM4kkT3HehVJLmgQfLzYjL6QhLHe+SS4e7FOYU6NgMy2kXPB2wVchncg==",
357 | "requires": {
358 | "async.util.queue": "0.5.2"
359 | }
360 | },
361 | "async.util.arrayeach": {
362 | "version": "0.5.2",
363 | "resolved": "https://registry.npmjs.org/async.util.arrayeach/-/async.util.arrayeach-0.5.2.tgz",
364 | "integrity": "sha512-PIb4rVYjwzLqb93XX2wj0+mA9YTgSWtxQRKxtuLqxXvGj1xZMB6qJUfr1NhS+FSaMPJIE1tF40Gl/o/vlfIz/A=="
365 | },
366 | "async.util.isarray": {
367 | "version": "0.5.2",
368 | "resolved": "https://registry.npmjs.org/async.util.isarray/-/async.util.isarray-0.5.2.tgz",
369 | "integrity": "sha512-wbUzlrwON8RUgi+v/rhF0U99Ce8Osjcn+JP/mFNg6ymvShcobAOvE6cvLajSY5dPqKCOE1xfdhefgBif11zZgw=="
370 | },
371 | "async.util.map": {
372 | "version": "0.5.2",
373 | "resolved": "https://registry.npmjs.org/async.util.map/-/async.util.map-0.5.2.tgz",
374 | "integrity": "sha512-uXZhyNIH9Jo25jn35lUoEwFLAdZWC2ZQKjLO5PLq8VAisfW6qvSfgDLH4H57/WQSKZSo6OOmuqGhtdvIHDTi1Q=="
375 | },
376 | "async.util.noop": {
377 | "version": "0.5.2",
378 | "resolved": "https://registry.npmjs.org/async.util.noop/-/async.util.noop-0.5.2.tgz",
379 | "integrity": "sha512-AdwShXwE0KoskgqVJAck8zcM32nIHj3AC8ZN62ZaR5srhrY235Nw18vOJZWxcOfhxdVM0hRVKM8kMx7lcl7cCQ=="
380 | },
381 | "async.util.onlyonce": {
382 | "version": "0.5.2",
383 | "resolved": "https://registry.npmjs.org/async.util.onlyonce/-/async.util.onlyonce-0.5.2.tgz",
384 | "integrity": "sha512-UgQvkU9JZ+I0Cm1f56XyGXcII+J3d/5XWUuHpcevlItuA3WFSJcqZrsyAUck2FkRSD8BwYQX1zUTDp3SJMVESg=="
385 | },
386 | "async.util.queue": {
387 | "version": "0.5.2",
388 | "resolved": "https://registry.npmjs.org/async.util.queue/-/async.util.queue-0.5.2.tgz",
389 | "integrity": "sha512-DlKOFnhCzERL9D3bLKlNdgXwSckckcj+XkCvNuX4KMs4brBc2lHvPg8MK4NoPIhwAvUBGvE4NECdNRY0I5UOEQ==",
390 | "requires": {
391 | "async.util.arrayeach": "0.5.2",
392 | "async.util.isarray": "0.5.2",
393 | "async.util.map": "0.5.2",
394 | "async.util.noop": "0.5.2",
395 | "async.util.onlyonce": "0.5.2",
396 | "async.util.setimmediate": "0.5.2"
397 | }
398 | },
399 | "async.util.setimmediate": {
400 | "version": "0.5.2",
401 | "resolved": "https://registry.npmjs.org/async.util.setimmediate/-/async.util.setimmediate-0.5.2.tgz",
402 | "integrity": "sha512-aCYF85ZFCQ9Xn0106GcOVx+LvFguIIzfbfRTOlQoie3G4KeSjURfA6f7CfpFAF09FNP2A1MtdjeFdvYeTGDebw=="
403 | },
404 | "debug": {
405 | "version": "3.2.7",
406 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
407 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
408 | "requires": {
409 | "ms": "^2.1.1"
410 | }
411 | },
412 | "deprecation": {
413 | "version": "2.3.1",
414 | "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
415 | "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
416 | },
417 | "dotenv": {
418 | "version": "16.4.7",
419 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
420 | "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="
421 | },
422 | "iconv-lite": {
423 | "version": "0.6.3",
424 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
425 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
426 | "requires": {
427 | "safer-buffer": ">= 2.1.2 < 3.0.0"
428 | }
429 | },
430 | "is-plain-object": {
431 | "version": "5.0.0",
432 | "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
433 | "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
434 | },
435 | "ms": {
436 | "version": "2.1.3",
437 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
438 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
439 | },
440 | "needle": {
441 | "version": "3.1.0",
442 | "resolved": "https://registry.npmjs.org/needle/-/needle-3.1.0.tgz",
443 | "integrity": "sha512-gCE9weDhjVGCRqS8dwDR/D3GTAeyXLXuqp7I8EzH6DllZGXSUyxuqqLh+YX9rMAWaaTFyVAg6rHGL25dqvczKw==",
444 | "requires": {
445 | "debug": "^3.2.6",
446 | "iconv-lite": "^0.6.3",
447 | "sax": "^1.2.4"
448 | }
449 | },
450 | "node-fetch": {
451 | "version": "2.6.7",
452 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
453 | "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
454 | "requires": {
455 | "whatwg-url": "^5.0.0"
456 | }
457 | },
458 | "once": {
459 | "version": "1.4.0",
460 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
461 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
462 | "requires": {
463 | "wrappy": "1"
464 | }
465 | },
466 | "safer-buffer": {
467 | "version": "2.1.2",
468 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
469 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
470 | },
471 | "sax": {
472 | "version": "1.2.4",
473 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
474 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
475 | },
476 | "slug": {
477 | "version": "8.2.2",
478 | "resolved": "https://registry.npmjs.org/slug/-/slug-8.2.2.tgz",
479 | "integrity": "sha512-5ByW6qXqPeG0Tmlkh24JhdXhvQsbaJSjVr3GgGxUV0BSskZKKBZZfFWxezap8+fh1vxBN9GVbqI1V6nqAFxlBg=="
480 | },
481 | "tr46": {
482 | "version": "0.0.3",
483 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
484 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
485 | },
486 | "universal-user-agent": {
487 | "version": "6.0.0",
488 | "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
489 | "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
490 | },
491 | "webidl-conversions": {
492 | "version": "3.0.1",
493 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
494 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
495 | },
496 | "whatwg-url": {
497 | "version": "5.0.0",
498 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
499 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
500 | "requires": {
501 | "tr46": "~0.0.3",
502 | "webidl-conversions": "^3.0.0"
503 | }
504 | },
505 | "wrappy": {
506 | "version": "1.0.2",
507 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
508 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
509 | }
510 | }
511 | }
512 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "stremio-addons-list",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "scripts": {
7 | "build": "node build.js"
8 | },
9 | "dependencies": {
10 | "@octokit/graphql": "^4.5.2",
11 | "async.queue": "0.5.2",
12 | "dotenv": "^16.4.7",
13 | "needle": "3.1.0",
14 | "slug": "8.2.2"
15 | },
16 | "engines": {
17 | "node": "16.18.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/resources/isMobile.js:
--------------------------------------------------------------------------------
1 | window.isMobile = false
2 | if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|ipad|iris|kindle|Android|Silk|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(navigator.userAgent)
3 | || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(navigator.userAgent.substr(0,4)))
4 | window.isMobile = true
5 |
--------------------------------------------------------------------------------
/resources/isotope.3.0.6.pkgd.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Isotope PACKAGED v3.0.6
3 | *
4 | * Licensed GPLv3 for open source use
5 | * or Isotope Commercial License for commercial use
6 | *
7 | * https://isotope.metafizzy.co
8 | * Copyright 2010-2018 Metafizzy
9 | */
10 |
11 | !function(t,e){"function"==typeof define&&define.amd?define("jquery-bridget/jquery-bridget",["jquery"],function(i){return e(t,i)}):"object"==typeof module&&module.exports?module.exports=e(t,require("jquery")):t.jQueryBridget=e(t,t.jQuery)}(window,function(t,e){"use strict";function i(i,s,a){function u(t,e,o){var n,s="$()."+i+'("'+e+'")';return t.each(function(t,u){var h=a.data(u,i);if(!h)return void r(i+" not initialized. Cannot call methods, i.e. "+s);var d=h[e];if(!d||"_"==e.charAt(0))return void r(s+" is not a valid method");var l=d.apply(h,o);n=void 0===n?l:n}),void 0!==n?n:t}function h(t,e){t.each(function(t,o){var n=a.data(o,i);n?(n.option(e),n._init()):(n=new s(o,e),a.data(o,i,n))})}a=a||e||t.jQuery,a&&(s.prototype.option||(s.prototype.option=function(t){a.isPlainObject(t)&&(this.options=a.extend(!0,this.options,t))}),a.fn[i]=function(t){if("string"==typeof t){var e=n.call(arguments,1);return u(this,t,e)}return h(this,t),this},o(a))}function o(t){!t||t&&t.bridget||(t.bridget=i)}var n=Array.prototype.slice,s=t.console,r="undefined"==typeof s?function(){}:function(t){s.error(t)};return o(e||t.jQuery),i}),function(t,e){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",e):"object"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}("undefined"!=typeof window?window:this,function(){function t(){}var e=t.prototype;return e.on=function(t,e){if(t&&e){var i=this._events=this._events||{},o=i[t]=i[t]||[];return o.indexOf(e)==-1&&o.push(e),this}},e.once=function(t,e){if(t&&e){this.on(t,e);var i=this._onceEvents=this._onceEvents||{},o=i[t]=i[t]||{};return o[e]=!0,this}},e.off=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var o=i.indexOf(e);return o!=-1&&i.splice(o,1),this}},e.emitEvent=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){i=i.slice(0),e=e||[];for(var o=this._onceEvents&&this._onceEvents[t],n=0;n1&&i+t>this.cols;i=o?0:i;var n=e.size.outerWidth&&e.size.outerHeight;return this.horizontalColIndex=n?i+t:this.horizontalColIndex,{col:i,y:this._getColGroupY(i,t)}},o._manageStamp=function(t){var i=e(t),o=this._getElementOffset(t),n=this._getOption("originLeft"),s=n?o.left:o.right,r=s+i.outerWidth,a=Math.floor(s/this.columnWidth);a=Math.max(0,a);var u=Math.floor(r/this.columnWidth);u-=r%this.columnWidth?0:1,u=Math.min(this.cols-1,u);for(var h=this._getOption("originTop"),d=(h?o.top:o.bottom)+i.outerHeight,l=a;l<=u;l++)this.colYs[l]=Math.max(d,this.colYs[l])},o._getContainerSize=function(){this.maxY=Math.max.apply(Math,this.colYs);var t={height:this.maxY};return this._getOption("fitWidth")&&(t.width=this._getContainerFitWidth()),t},o._getContainerFitWidth=function(){for(var t=0,e=this.cols;--e&&0===this.colYs[e];)t++;return(this.cols-t)*this.columnWidth-this.gutter},o.needsResizeLayout=function(){var t=this.containerWidth;return this.getContainerWidth(),t!=this.containerWidth},i}),function(t,e){"function"==typeof define&&define.amd?define("isotope-layout/js/layout-modes/masonry",["../layout-mode","masonry-layout/masonry"],e):"object"==typeof module&&module.exports?module.exports=e(require("../layout-mode"),require("masonry-layout")):e(t.Isotope.LayoutMode,t.Masonry)}(window,function(t,e){"use strict";var i=t.create("masonry"),o=i.prototype,n={_getElementOffset:!0,layout:!0,_getMeasurement:!0};for(var s in e.prototype)n[s]||(o[s]=e.prototype[s]);var r=o.measureColumns;o.measureColumns=function(){this.items=this.isotope.filteredItems,r.call(this)};var a=o._getOption;return o._getOption=function(t){return"fitWidth"==t?void 0!==this.options.isFitWidth?this.options.isFitWidth:this.options.fitWidth:a.apply(this.isotope,arguments)},i}),function(t,e){"function"==typeof define&&define.amd?define("isotope-layout/js/layout-modes/fit-rows",["../layout-mode"],e):"object"==typeof exports?module.exports=e(require("../layout-mode")):e(t.Isotope.LayoutMode)}(window,function(t){"use strict";var e=t.create("fitRows"),i=e.prototype;return i._resetLayout=function(){this.x=0,this.y=0,this.maxY=0,this._getMeasurement("gutter","outerWidth")},i._getItemLayoutPosition=function(t){t.getSize();var e=t.size.outerWidth+this.gutter,i=this.isotope.size.innerWidth+this.gutter;0!==this.x&&e+this.x>i&&(this.x=0,this.y=this.maxY);var o={x:this.x,y:this.y};return this.maxY=Math.max(this.maxY,this.y+t.size.outerHeight),this.x+=e,o},i._getContainerSize=function(){return{height:this.maxY}},e}),function(t,e){"function"==typeof define&&define.amd?define("isotope-layout/js/layout-modes/vertical",["../layout-mode"],e):"object"==typeof module&&module.exports?module.exports=e(require("../layout-mode")):e(t.Isotope.LayoutMode)}(window,function(t){"use strict";var e=t.create("vertical",{horizontalAlignment:0}),i=e.prototype;return i._resetLayout=function(){this.y=0},i._getItemLayoutPosition=function(t){t.getSize();var e=(this.isotope.size.innerWidth-t.size.outerWidth)*this.options.horizontalAlignment,i=this.y;return this.y+=t.size.outerHeight,{x:e,y:i}},i._getContainerSize=function(){return{height:this.y}},e}),function(t,e){"function"==typeof define&&define.amd?define(["outlayer/outlayer","get-size/get-size","desandro-matches-selector/matches-selector","fizzy-ui-utils/utils","isotope-layout/js/item","isotope-layout/js/layout-mode","isotope-layout/js/layout-modes/masonry","isotope-layout/js/layout-modes/fit-rows","isotope-layout/js/layout-modes/vertical"],function(i,o,n,s,r,a){return e(t,i,o,n,s,r,a)}):"object"==typeof module&&module.exports?module.exports=e(t,require("outlayer"),require("get-size"),require("desandro-matches-selector"),require("fizzy-ui-utils"),require("isotope-layout/js/item"),require("isotope-layout/js/layout-mode"),require("isotope-layout/js/layout-modes/masonry"),require("isotope-layout/js/layout-modes/fit-rows"),require("isotope-layout/js/layout-modes/vertical")):t.Isotope=e(t,t.Outlayer,t.getSize,t.matchesSelector,t.fizzyUIUtils,t.Isotope.Item,t.Isotope.LayoutMode)}(window,function(t,e,i,o,n,s,r){function a(t,e){return function(i,o){for(var n=0;na||ra?1:-1)*h}}return 0}}var u=t.jQuery,h=String.prototype.trim?function(t){return t.trim()}:function(t){return t.replace(/^\s+|\s+$/g,"")},d=e.create("isotope",{layoutMode:"masonry",isJQueryFiltering:!0,sortAscending:!0});d.Item=s,d.LayoutMode=r;var l=d.prototype;l._create=function(){this.itemGUID=0,this._sorters={},this._getSorters(),e.prototype._create.call(this),this.modes={},this.filteredItems=this.items,this.sortHistory=["original-order"];for(var t in r.modes)this._initLayoutMode(t)},l.reloadItems=function(){this.itemGUID=0,e.prototype.reloadItems.call(this)},l._itemize=function(){for(var t=e.prototype._itemize.apply(this,arguments),i=0;i+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&v(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!y||!y.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ve(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ye(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ve(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],y=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML=" ",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||y.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||y.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||y.push(".#.+[+~]"),e.querySelectorAll("\\\f"),y.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML=" ";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),y=y.length&&new RegExp(y.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),v=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&v(p,e)?-1:t==C||t.ownerDocument==p&&v(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!y||!y.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),v.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",v.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML=" ",v.option=!!ce.lastChild;var ge={thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n",""]);var me=/<|?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),v.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(v.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return B(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=_e(v.pixelPosition,function(e,t){if(t)return t=Be(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return B(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | {addon-title} - {addon-page-title-append}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | {addon-title} v{addon-version}
25 | {addon-types-links}
26 |
47 |
50 |
57 |
58 |
59 |
60 |
69 |
70 |
71 |
124 |
125 |