update FAQ, change to .webmanifest, start work on new viewAnime, fix playnext functionality, fix main menu awaits, update whitespace

This commit is contained in:
ThaUnknown 2021-08-10 22:14:52 +02:00
parent 180f040709
commit 68d0818f38
12 changed files with 710 additions and 581 deletions

View file

@ -265,38 +265,6 @@ section:target:not(#player),
transition-property: top, height;
}
nav {
transition: top .4s cubic-bezier(.25, .8, .25, 1);
}
#view {
background: no-repeat center center fixed;
background-size: cover;
}
#view>div {
backdrop-filter: blur(10px) brightness(0.5);
}
#viewEpisodesWrapper.hidden {
opacity: 0;
height: 0
}
#viewEpisodesWrapper {
opacity: 1;
height: auto;
transition: opacity .2s cubic-bezier(.25, .8, .25, 1);
overflow: hidden;
}
#viewDetails,
#view .card {
background-color: var(--dm-button-bg-color) !important;
background-image: var(--dm-button-bg-image) !important;
box-shadow: var(--dm-button-box-shadow) !important;
}
.modal-dialog {
top: 0 !important
}
@ -434,4 +402,47 @@ nav {
.sidebar [data-toggle="tooltip"]:not([data-target-breakpoint]):focus::after {
opacity: 1;
top: 50%
}
/* view */
#view, #viewAnime .banner {
background: no-repeat center center;
background-size: cover;
}
#view>div, #viewAnime .main {
backdrop-filter: blur(10px);
background: linear-gradient(0deg, rgba(17,20,23,1) 0%, rgba(17,20,23,0.80) 25%, rgba(17,20,23,0.40) 50%, rgba(37,40,44,0) 100%)
}
#viewEpisodesWrapper.hidden {
opacity: 0;
height: 0
}
#viewEpisodesWrapper {
opacity: 1;
height: auto;
transition: opacity .2s cubic-bezier(.25, .8, .25, 1);
overflow: hidden;
}
#viewDetails,
#view .card {
background-color: var(--dm-button-bg-color) !important;
background-image: var(--dm-button-bg-image) !important;
box-shadow: var(--dm-button-box-shadow) !important;
}
#viewAnime .title {
font-size: 4rem
}
#viewAnime .badge {
background-color: var(--dm-button-bg-color) !important;
padding: .6rem 2rem;
font-size: 1.4rem;
border: none;
margin-right: 1rem;
}

View file

@ -52,9 +52,72 @@
</div>
</div>
</div>
<div class="modal modal-full" id="viewAnime" tabindex="-1" role="dialog">
<div class="h-half w-full position-absolute banner bg-dark"
style="background-image: url(https://s4.anilist.co/file/anilistcdn/media/anime/banner/16498-8jpFCOcDmneX.jpg) !important">
</div>
<div class="h-full modal-content bg-very-dark p-0 overflow-y-auto">
<button class="close pointer" data-dismiss="modal" type="button" aria-label="Close">
<span>×</span>
</button>
<div class="h-half main d-flex">
<div class="container-xl row w-full px-15 px-xl-0">
<div class="col-md-3 d-flex justify-content-end flex-column">
<img class="contain-img rounded w-full mb-15 shadow"
src="https://s4.anilist.co/file/anilistcdn/media/anime/cover/large/bx16498-m5ZMNtFioc7j.png">
</div>
<div class="col-md-7 d-flex justify-content-end flex-column">
<div class="px-20 ml-20 d-flex flex-column font-size-12">
<span class="title font-weight-bold pb-15 text-white">Shingeki no Kyojin</span>
<div class="d-flex flex-row font-size-18 pb-15">
<span class="material-icons mr-10 font-size-24">
trending_up
</span>
<span>Rating: <span class="font-weight-bold mr-20">85%</span></span>
<span class="material-icons mx-10 font-size-24">
monitor
</span>
<span>Format: <span class="font-weight-bold mr-20">TV</span></span>
<span class="material-icons mx-10 font-size-24">
theaters
</span>
<span>Episodes: <span class="font-weight-bold mr-20">25</span></span>
</div>
<div class="pb-10 pt-5">
<span class="badge badge-pill badge-color mt-5 font-weight-bold shadow">Action</span>
<span class="badge badge-pill badge-color mt-5 font-weight-bold shadow">Drama</span>
<span class="badge badge-pill badge-color mt-5 font-weight-bold shadow">Fantasy</span>
<span class="badge badge-pill badge-color mt-5 font-weight-bold shadow">Mystery</span>
</div>
</div>
</div>
<div class="col-md-2 d-flex justify-content-end flex-column">
<div class="d-flex flex-column flex-wrap">
<button class="btn btn-primary btn-lg d-flex align-items-end mb-5 font-weight-bold" type="button">
<span class="material-icons mr-10 font-size-20">
play_arrow
</span>
Play
</button>
<button class="btn d-flex align-items-end mb-5 font-weight-bold" type="button">
<span class="material-icons mr-10 font-size-20">
live_tv
</span>
Trailer
</button>
</div>
</div>
</div>
</div>
<div class="content bg-very-dark">
</div>
</div>
</div>
<div class="modal modal-full bg-dark-light" id="view" tabindex="-1" role="dialog">
<div class="modal-dialog bg-transparent" role="document">
<div class="view modal-content bg-transparent">
<div class="h-full modal-content bg-transparent">
<button class="close pointer" data-dismiss="modal" type="button" aria-label="Close">
<span>×</span>
</button>
@ -76,7 +139,7 @@
</button>
</div>
</div>
<div class="col-md-7">
<div class="col-md-7 d-flex justify-content-end flex-column">
<div class="px-20 ml-20 d-flex flex-column font-size-12 text-muted">
<span class="title font-weight-bold font-size-24 text-white pb-5" id="viewTitle"></span>
<span id="viewSeason" class="text-capitalize"></span>
@ -739,11 +802,12 @@ wss://tracker.sloppyta.co:443/announce
wss://hub.bugout.link:443/announce</textarea>
</div>
<div class="input-group mb-10 form-control-lg w-600 d-none" data-toggle="tooltip" data-placement="top"
data-title="Specifies Folder To Store Torrents In, Which Is Accessible Externally." id="torrent5">
data-title="Specifies Folder To Store Torrents In, Which Is Accessible Externally." id="torrent5">
<div class="input-group-prepend">
<button class="btn btn-lg btn-primary" type="button">Choose Folder</button>
</div>
<input type="text" id='torrent5label' readonly class="form-control form-control-lg pl-10" placeholder="Folder">
<input type="text" id='torrent5label' readonly class="form-control form-control-lg pl-10"
placeholder="Folder">
</div>
<div class="input-group w-300 mb-10 form-control-lg" data-toggle="tooltip" data-placement="top"
data-title="Download/Upload Speed Limit For Torrents, Higher Values Increase CPU Usage">
@ -772,9 +836,11 @@ wss://hub.bugout.link:443/announce</textarea>
<input id="subtitle1" type="text" list="subtitle1list" class="form-control form-control-lg"
autocomplete="off" value="SubsPlease">
<datalist id="subtitle1list">
<option value="SubsPlease">Roboto Medium,26,&H00FFFFFF,&H000000FF,&H00020713,&H00000000,0,0,0,0,100,100,0,0,1,1.3,0,2,20,20,23,1
<option value="SubsPlease">Roboto
Medium,26,&H00FFFFFF,&H000000FF,&H00020713,&H00000000,0,0,0,0,100,100,0,0,1,1.3,0,2,20,20,23,1
</option>
<option value="Erai-raws">Open Sans Semibold,45,&H00FFFFFF,&H000000FF,&H00020713,&H00000000,-1,0,0,0,100,100,0,0,1,1.7,0,2,10,10,25,1
<option value="Erai-raws">Open Sans
Semibold,45,&H00FFFFFF,&H000000FF,&H00020713,&H00000000,-1,0,0,0,100,100,0,0,1,1.7,0,2,10,10,25,1
</option>
</datalist>
</div>
@ -874,8 +940,8 @@ wss://hub.bugout.link:443/announce</textarea>
</div>
</div>
<div class="content my-10">
<button class="btn btn-lg btn-danger mb-5" type="button" id="setRes" data-toggle="tooltip" data-placement="top"
data-title="Restores All Settings Back To Their Recommended Defaults">Restore
<button class="btn btn-lg btn-danger mb-5" type="button" id="setRes" data-toggle="tooltip"
data-placement="top" data-title="Restores All Settings Back To Their Recommended Defaults">Restore
Defaults
</button>
<button class="btn btn-lg btn-danger mb-5" type="button" id="clearRelCache" data-toggle="tooltip"

View file

@ -92,7 +92,7 @@ const details = {
native: 'Native',
synonyms: 'Synonyms'
}
const episodeRx = /Episode (\d+) - (.*)/
export const episodeRx = /Episode (\d+) - (.*)/
// this is fucked beyond belief, this is why you use frameworks
/* global view, viewImg, viewTitle, viewDesc, viewDetails, viewSeason, viewMediaInfo, viewPlay, viewTrailer, viewRelationsGallery, viewSynonym, viewSynonymText, viewEpisodesWrapper, episodes, trailerVideo, trailerClose */
export function viewAnime (media) {

View file

@ -289,6 +289,7 @@ query ($page: Int, $perPage: Int, $sort: [MediaSort], $type: MediaType, $search:
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "episodeRx": () => (/* binding */ episodeRx),
/* harmony export */ "viewAnime": () => (/* binding */ viewAnime),
/* harmony export */ "nyaaSearch": () => (/* binding */ nyaaSearch),
/* harmony export */ "resolveFileMedia": () => (/* binding */ resolveFileMedia),
@ -716,54 +717,50 @@ function loadHomePage () {
const homeSearchElements = [searchText, searchGenre, searchYear, searchSeason, searchFormat, searchStatus, searchSort]
const browseGallery = document.querySelector('.browse')
const homeLoadFunctions = {
continue: function (page) {
continue: async function (page) {
if (!page) gallerySkeleton(browseGallery)
;(0,_anilist_js__WEBPACK_IMPORTED_MODULE_0__.alRequest)({ method: 'UserLists', status_in: 'CURRENT', id: alID, page: page || 1 }).then(res => {
galleryAppend({ media: res.data.Page.mediaList.map(i => i.media), gallery: browseGallery, method: 'continue', page: page || 1 })
})
const mediaList = (await (0,_anilist_js__WEBPACK_IMPORTED_MODULE_0__.alRequest)({ method: 'UserLists', status_in: 'CURRENT', id: alID, page: page || 1 })).data.Page.mediaList
galleryAppend({ media: mediaList.map(i => i.media), gallery: browseGallery, method: 'continue', page: page || 1 })
},
releases: function () {
releases: async function () {
gallerySkeleton(browseGallery)
;(0,_rss_js__WEBPACK_IMPORTED_MODULE_2__.releasesRSS)().then(cards => {
browseGallery.textContent = ''
browseGallery.append(...cards)
home.classList.remove('loading')
home.onscroll = undefined
})
const cards = await (0,_rss_js__WEBPACK_IMPORTED_MODULE_2__.releasesRSS)()
browseGallery.textContent = ''
browseGallery.append(...cards)
home.classList.remove('loading')
home.onscroll = undefined
},
planning: function (page) {
planning: async function (page) {
if (!page) gallerySkeleton(browseGallery)
;(0,_anilist_js__WEBPACK_IMPORTED_MODULE_0__.alRequest)({ method: 'UserLists', status_in: 'PLANNING', id: alID, page: page || 1 }).then(res => {
galleryAppend({ media: res.data.Page.mediaList.map(i => i.media), gallery: browseGallery, method: 'planning', page: page || 1 })
})
const mediaList = (await (0,_anilist_js__WEBPACK_IMPORTED_MODULE_0__.alRequest)({ method: 'UserLists', status_in: 'PLANNING', id: alID, page: page || 1 })).data.Page.mediaList
galleryAppend({ media: mediaList.map(i => i.media), gallery: browseGallery, method: 'planning', page: page || 1 })
},
trending: function () {
trending: async function () {
gallerySkeleton(browseGallery)
clearSearch()
searchSort.value = 'TRENDING_DESC'
homeLoadFunctions.search()
await homeLoadFunctions.search()
},
romance: function () {
romance: async function () {
gallerySkeleton(browseGallery)
clearSearch()
searchGenre.value = 'romance'
searchSort.value = 'TRENDING_DESC'
homeLoadFunctions.search()
await homeLoadFunctions.search()
},
action: function () {
action: async function () {
gallerySkeleton(browseGallery)
clearSearch()
searchGenre.value = 'action'
searchSort.value = 'TRENDING_DESC'
homeLoadFunctions.search()
await homeLoadFunctions.search()
},
schedule: function (page) {
schedule: async function (page) {
if (!page) gallerySkeleton(browseGallery)
;(0,_anilist_js__WEBPACK_IMPORTED_MODULE_0__.alRequest)({ method: 'AiringSchedule', page: page || 1 }).then(res => {
galleryAppend({ media: res.data.Page.airingSchedules.filter(entry => entry.media.countryOfOrigin !== 'CN' && entry.media.isAdult === false), gallery: browseGallery, method: 'schedule', page: page || 1, schedule: true })
})
const mediaList = (await (0,_anilist_js__WEBPACK_IMPORTED_MODULE_0__.alRequest)({ method: 'AiringSchedule', page: page || 1 })).data.Page.airingSchedules
galleryAppend({ media: mediaList.filter(entry => entry.media.countryOfOrigin !== 'CN' && entry.media.isAdult === false), gallery: browseGallery, method: 'schedule', page: page || 1, schedule: true })
},
search: function (page) {
search: async function (page) {
const opts = {
method: 'Search',
page: page || 1
@ -771,9 +768,8 @@ function loadHomePage () {
for (const element of homeSearchElements) {
if (element.value) opts[element.dataset.option] = element.value
}
(0,_anilist_js__WEBPACK_IMPORTED_MODULE_0__.alRequest)(opts).then(res => {
galleryAppend({ media: res.data.Page.media, gallery: browseGallery, method: 'search', page: page || 1 })
})
const mediaList = (await (0,_anilist_js__WEBPACK_IMPORTED_MODULE_0__.alRequest)(opts)).data.Page.media
galleryAppend({ media: mediaList, gallery: browseGallery, method: 'search', page: page || 1 })
}
}
const homePreviewFunctions = {
@ -1086,7 +1082,7 @@ client.on('playlist', () => {
})
client.on('next', ({ filemedia }) => {
if (filemedia.media) {
(0,_anime_js__WEBPACK_IMPORTED_MODULE_2__.nyaaSearch)(filemedia.media, filemedia.episodeNumber + 1)
(0,_anime_js__WEBPACK_IMPORTED_MODULE_2__.nyaaSearch)(filemedia.media, parseInt(filemedia.episodeNumber) + 1)
} else {
halfmoon__WEBPACK_IMPORTED_MODULE_6___default().initStickyAlert({
content: 'Couldn\'t find anime name! Try specifying a torrent manually.',
@ -1182,6 +1178,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _main_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./main.js */ "./app/js/main.js");
/* harmony import */ var halfmoon__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! halfmoon */ "halfmoon");
/* harmony import */ var halfmoon__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(halfmoon__WEBPACK_IMPORTED_MODULE_4__);
/* harmony import */ var _anime_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./anime.js */ "./app/js/anime.js");
/* provided dependency */ var console = __webpack_require__(/*! ./node_modules/console-browserify/index.js */ "./node_modules/console-browserify/index.js");
/* eslint-env browser */
/* global torrent4list */
@ -1192,6 +1189,7 @@ __webpack_require__.r(__webpack_exports__);
const exclusions = {
edge: ['DTS'],
chromium: ['DTS', 'AC3', 'HEVC', 'x265', 'H.265'],
@ -1237,22 +1235,32 @@ async function nyaaRss (media, episode) {
}
}
entries.sort((a, b) => b.seeders - a.seeders)
const streamingEpisode = media?.streamingEpisodes.filter(episode => _anime_js__WEBPACK_IMPORTED_MODULE_5__.episodeRx.exec(episode.title) && Number(_anime_js__WEBPACK_IMPORTED_MODULE_5__.episodeRx.exec(episode.title)[1]) === Number(episode))[0]
const fileMedia = {
mediaTitle: media?.title.userPreferred,
episodeNumber: Number(episode),
episodeTitle: streamingEpisode ? _anime_js__WEBPACK_IMPORTED_MODULE_5__.episodeRx.exec(streamingEpisode.title)[2] : undefined,
episodeThumbnail: streamingEpisode?.thumbnail,
mediaCover: media?.coverImage.medium,
name: 'Miru',
media: media
}
if (_settings_js__WEBPACK_IMPORTED_MODULE_0__.settings.torrent2) {
_main_js__WEBPACK_IMPORTED_MODULE_3__.client.playTorrent(entries[0].hash, { media: media, episode: episode })
_main_js__WEBPACK_IMPORTED_MODULE_3__.client.playTorrent(entries[0].hash, { media: fileMedia, episode: episode })
halfmoon__WEBPACK_IMPORTED_MODULE_4___default().hideModal('tsearch')
}
entries.forEach((entry, index) => {
const template = document.createElement('tr')
template.innerHTML += `
<th>${(index + 1)}</th>
<td>${entry.title}</td>
<td>${entry.size}</td>
<td>${entry.seeders}</td>
<td>${entry.leechers}</td>
<td>${entry.downloads}</td>
<td class="pointer">Play</td>`
<th>${(index + 1)}</th>
<td>${entry.title}</td>
<td>${entry.size}</td>
<td>${entry.seeders}</td>
<td>${entry.leechers}</td>
<td>${entry.downloads}</td>
<td class="pointer">Play</td>`
template.onclick = () => {
_main_js__WEBPACK_IMPORTED_MODULE_3__.client.playTorrent(entry.hash, { media: media, episode: episode })
_main_js__WEBPACK_IMPORTED_MODULE_3__.client.playTorrent(entry.hash, { media: fileMedia, episode: episode })
halfmoon__WEBPACK_IMPORTED_MODULE_4___default().hideModal('tsearch')
}
frag.appendChild(template)
@ -6858,7 +6866,7 @@ PEMEncoder.prototype.encode = function encode(data, options) {
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
/* provided dependency */ var console = __webpack_require__(/*! ./node_modules/console-browserify/index.js */ "./node_modules/console-browserify/index.js");
// Currently in sync with Node.js lib/assert.js
// https://github.com/nodejs/node/commit/2a51ae424a513ec9a6aa3466baa0cc1d55dd4f3b
@ -7501,7 +7509,7 @@ assert.strict.strict = assert.strict;
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
// Currently in sync with Node.js lib/internal/assert/assertion_error.js
// https://github.com/nodejs/node/commit/0817840f775032169ddd70c85ac059f18ffcc81c
@ -10787,7 +10795,7 @@ module.exports = Wire
\***************************************************/
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
/* provided dependency */ var Buffer = __webpack_require__(/*! ./node_modules/buffer/index.js */ "./node_modules/buffer/index.js")["Buffer"];
const debug = __webpack_require__(/*! debug */ "./node_modules/debug/src/browser.js")('bittorrent-tracker:client')
const EventEmitter = __webpack_require__(/*! events */ "./node_modules/events/events.js")
@ -17063,7 +17071,7 @@ module.exports = verify
"use strict";
/* provided dependency */ var Buffer = __webpack_require__(/*! ./node_modules/buffer/index.js */ "./node_modules/buffer/index.js")["Buffer"];
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
/* eslint camelcase: "off" */
@ -17483,7 +17491,7 @@ exports.Zlib = Zlib;
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
var Buffer = __webpack_require__(/*! buffer */ "./node_modules/buffer/index.js").Buffer;
@ -31197,7 +31205,7 @@ exports.constants = {
/***/ ((module, exports, __webpack_require__) => {
/* provided dependency */ var console = __webpack_require__(/*! ./node_modules/console-browserify/index.js */ "./node_modules/console-browserify/index.js");
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
/* eslint-env browser */
/**
@ -45182,7 +45190,7 @@ module.exports = JSON.parse('{"name":"elliptic","version":"6.5.4","description":
\*********************************************/
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
var once = __webpack_require__(/*! once */ "./node_modules/once/once.js");
var noop = function() {};
@ -48788,7 +48796,7 @@ class HybridChunkStore {
close (cb = () => {}) {
const promises = []
for (const store of this.stores) {
promises.push(new Promise(resolve => store.destroy(resolve)))
promises.push(new Promise(resolve => store.close(resolve)))
}
Promise.all(promises).then(values => {
values = values.filter(value => value)
@ -48799,7 +48807,7 @@ class HybridChunkStore {
destroy (cb = () => {}) {
const promises = []
for (const store of this.stores) {
promises.push(new Promise(resolve => store.close(resolve)))
promises.push(new Promise(resolve => store.destroy(resolve)))
}
Promise.all(promises).then(values => {
values = values.filter(value => value)
@ -50070,9 +50078,9 @@ exports.TokenBucket = __webpack_require__(/*! ./lib/tokenBucket */ "./node_modul
/*!*******************************************!*\
!*** ./node_modules/limiter/lib/clock.js ***!
\*******************************************/
/***/ ((module) => {
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
var getMilliseconds = function() {
if (typeof process !== 'undefined' && process.hrtime) {
var hrtime = process.hrtime();
@ -50096,7 +50104,7 @@ module.exports = getMilliseconds;
\*************************************************/
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
var TokenBucket = __webpack_require__(/*! ./tokenBucket */ "./node_modules/limiter/lib/tokenBucket.js");
var getMilliseconds = __webpack_require__(/*! ./clock */ "./node_modules/limiter/lib/clock.js");
@ -50242,9 +50250,9 @@ module.exports = RateLimiter;
/*!*************************************************!*\
!*** ./node_modules/limiter/lib/tokenBucket.js ***!
\*************************************************/
/***/ ((module) => {
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
/**
* A hierarchical token bucket for rate limiting. See
@ -58499,10 +58507,10 @@ function ensure (bool, fieldName) {
/*!***********************************************!*\
!*** ./node_modules/path-browserify/index.js ***!
\***********************************************/
/***/ ((module) => {
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
// 'path' module extracted from Node.js v8.11.1 (only the posix part)
// transplited with Babel
@ -59182,7 +59190,7 @@ module.exports = function (password, salt, iterations, keylen, digest, callback)
\*****************************************************/
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
var defaultEncoding
/* istanbul ignore next */
if (__webpack_require__.g.process && __webpack_require__.g.process.browser) {
@ -59379,6 +59387,45 @@ function length (bytes) {
}
/***/ }),
/***/ "./node_modules/process-fast/index.js":
/*!********************************************!*\
!*** ./node_modules/process-fast/index.js ***!
\********************************************/
/***/ ((module) => {
const process = module.exports = {}
process.title = 'browser'
process.browser = true
process.env = {}
process.argv = []
process.version = ''
process.versions = {}
function noop () {}
process.on = noop
process.addListener = noop
process.once = noop
process.off = noop
process.removeListener = noop
process.removeAllListeners = noop
process.emit = noop
process.prependListener = noop
process.prependOnceListener = noop
process.nextTick = (func, ...args) => window.queueMicrotask(() => func(...args))
process.listeners = (name) => []
process.cwd = () => '/'
process.umask = () => 0
process.binding = (name) => { throw new Error('process.binding is not supported') }
process.chdir = (dir) => { throw new Error('process.chdir is not supported') }
/***/ }),
/***/ "./node_modules/public-encrypt/browser.js":
@ -63146,7 +63193,7 @@ module.exports = function xor (a, b) {
\************************************/
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
var once = __webpack_require__(/*! once */ "./node_modules/once/once.js")
var eos = __webpack_require__(/*! end-of-stream */ "./node_modules/end-of-stream/index.js")
var fs = __webpack_require__(/*! fs */ "?7874") // we only need fs to get the ReadStream and WriteStream prototypes
@ -63469,7 +63516,7 @@ module.exports = iterate
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
// limit of Crypto.getRandomValues()
@ -63531,7 +63578,7 @@ function randomBytes (size, cb) {
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
function oldBrowser () {
@ -64302,7 +64349,7 @@ module.exports.codes = codes;
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
@ -64501,7 +64548,7 @@ PassThrough.prototype._transform = function (chunk, encoding, cb) {
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
@ -65847,7 +65894,7 @@ function done(stream, er, data) {
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
@ -66555,7 +66602,7 @@ Writable.prototype._destroy = function (err, cb) {
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
var _Object$setPrototypeO;
@ -66990,10 +67037,10 @@ function () {
/*!**********************************************************************!*\
!*** ./node_modules/readable-stream/lib/internal/streams/destroy.js ***!
\**********************************************************************/
/***/ ((module) => {
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
// undocumented cb() API, needed for core, not for public API
function destroy(err, cb) {
@ -69202,7 +69249,7 @@ SafeBuffer.allocUnsafeSlow = function (size) {
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
"use strict";
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
/* eslint-disable node/no-deprecated-api */
@ -72403,7 +72450,7 @@ xhr = null // Help gc
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
/* provided dependency */ var Buffer = __webpack_require__(/*! ./node_modules/buffer/index.js */ "./node_modules/buffer/index.js")["Buffer"];
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
var capability = __webpack_require__(/*! ./capability */ "./node_modules/stream-http/lib/capability.js")
var inherits = __webpack_require__(/*! inherits */ "./node_modules/inherits/inherits_browser.js")
var response = __webpack_require__(/*! ./response */ "./node_modules/stream-http/lib/response.js")
@ -72766,7 +72813,7 @@ var unsafeHeaders = [
\**************************************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
/* provided dependency */ var Buffer = __webpack_require__(/*! ./node_modules/buffer/index.js */ "./node_modules/buffer/index.js")["Buffer"];
var capability = __webpack_require__(/*! ./capability */ "./node_modules/stream-http/lib/capability.js")
var inherits = __webpack_require__(/*! inherits */ "./node_modules/inherits/inherits_browser.js")
@ -73067,7 +73114,7 @@ module.exports = function getBuffer (stream, length, cb) {
\***************************************/
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
const { EventEmitter } = __webpack_require__(/*! events */ "./node_modules/events/events.js")
const STREAM_DESTROYED = new Error('Stream was destroyed')
const PREMATURE_CLOSE = new Error('Premature close')
@ -74581,7 +74628,7 @@ module.exports = function (buf) {
\*************************************************/
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
/*! torrent-discovery. MIT License. WebTorrent LLC <https://webtorrent.io/opensource> */
const debug = __webpack_require__(/*! debug */ "./node_modules/debug/src/browser.js")('torrent-discovery')
const DHT = __webpack_require__(/*! bittorrent-dht/client */ "?42e5") // empty object in browser
@ -77024,7 +77071,7 @@ exports.isAnyArrayBuffer = isAnyArrayBuffer;
\***********************************/
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
/* provided dependency */ var console = __webpack_require__(/*! ./node_modules/console-browserify/index.js */ "./node_modules/console-browserify/index.js");
// Copyright Joyent, Inc. and other Node contributors.
//
@ -78424,7 +78471,7 @@ class WebTorrentPlayer extends (webtorrent__WEBPACK_IMPORTED_MODULE_0___default(
})
}
window.addEventListener('unload', () => {
this.cleanupTorrents()
this.destroy()
this.cleanupVideo()
})
// kind of a fetch event from service worker but for the main thread.
@ -79492,7 +79539,7 @@ Style: Default,${options.defaultSSAStyles || 'Roboto Medium,26,&H00FFFFFF,&H0000
// cleanup torrent and store
cleanupTorrents () {
// creates an array of all non-offline store torrents and removes them
this.torrents.filter(torrent => !this.offlineTorrents[torrent.infoHash]).forEach(torrent => torrent.destroy({ destroyStore: this.destroyStore }))
this.torrents.filter(torrent => !this.offlineTorrents[torrent.infoHash]).forEach(torrent => torrent.destroy())
}
// add torrent for offline download
@ -81575,7 +81622,7 @@ module.exports = RarityMap
\************************************************/
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
/* provided dependency */ var process = Object(function webpackMissingModule() { var e = new Error("Cannot find module 'process-fast'"); e.code = 'MODULE_NOT_FOUND'; throw e; }());
/* provided dependency */ var process = __webpack_require__(/*! process-fast */ "./node_modules/process-fast/index.js");
/* global Blob */
const addrToIPPort = __webpack_require__(/*! addr-to-ip-port */ "./node_modules/addr-to-ip-port/index.js")

File diff suppressed because one or more lines are too long

View file

@ -13,54 +13,50 @@ export function loadHomePage () {
const homeSearchElements = [searchText, searchGenre, searchYear, searchSeason, searchFormat, searchStatus, searchSort]
const browseGallery = document.querySelector('.browse')
const homeLoadFunctions = {
continue: function (page) {
continue: async function (page) {
if (!page) gallerySkeleton(browseGallery)
alRequest({ method: 'UserLists', status_in: 'CURRENT', id: alID, page: page || 1 }).then(res => {
galleryAppend({ media: res.data.Page.mediaList.map(i => i.media), gallery: browseGallery, method: 'continue', page: page || 1 })
})
const mediaList = (await alRequest({ method: 'UserLists', status_in: 'CURRENT', id: alID, page: page || 1 })).data.Page.mediaList
galleryAppend({ media: mediaList.map(i => i.media), gallery: browseGallery, method: 'continue', page: page || 1 })
},
releases: function () {
releases: async function () {
gallerySkeleton(browseGallery)
releasesRSS().then(cards => {
browseGallery.textContent = ''
browseGallery.append(...cards)
home.classList.remove('loading')
home.onscroll = undefined
})
const cards = await releasesRSS()
browseGallery.textContent = ''
browseGallery.append(...cards)
home.classList.remove('loading')
home.onscroll = undefined
},
planning: function (page) {
planning: async function (page) {
if (!page) gallerySkeleton(browseGallery)
alRequest({ method: 'UserLists', status_in: 'PLANNING', id: alID, page: page || 1 }).then(res => {
galleryAppend({ media: res.data.Page.mediaList.map(i => i.media), gallery: browseGallery, method: 'planning', page: page || 1 })
})
const mediaList = (await alRequest({ method: 'UserLists', status_in: 'PLANNING', id: alID, page: page || 1 })).data.Page.mediaList
galleryAppend({ media: mediaList.map(i => i.media), gallery: browseGallery, method: 'planning', page: page || 1 })
},
trending: function () {
trending: async function () {
gallerySkeleton(browseGallery)
clearSearch()
searchSort.value = 'TRENDING_DESC'
homeLoadFunctions.search()
await homeLoadFunctions.search()
},
romance: function () {
romance: async function () {
gallerySkeleton(browseGallery)
clearSearch()
searchGenre.value = 'romance'
searchSort.value = 'TRENDING_DESC'
homeLoadFunctions.search()
await homeLoadFunctions.search()
},
action: function () {
action: async function () {
gallerySkeleton(browseGallery)
clearSearch()
searchGenre.value = 'action'
searchSort.value = 'TRENDING_DESC'
homeLoadFunctions.search()
await homeLoadFunctions.search()
},
schedule: function (page) {
schedule: async function (page) {
if (!page) gallerySkeleton(browseGallery)
alRequest({ method: 'AiringSchedule', page: page || 1 }).then(res => {
galleryAppend({ media: res.data.Page.airingSchedules.filter(entry => entry.media.countryOfOrigin !== 'CN' && entry.media.isAdult === false), gallery: browseGallery, method: 'schedule', page: page || 1, schedule: true })
})
const mediaList = (await alRequest({ method: 'AiringSchedule', page: page || 1 })).data.Page.airingSchedules
galleryAppend({ media: mediaList.filter(entry => entry.media.countryOfOrigin !== 'CN' && entry.media.isAdult === false), gallery: browseGallery, method: 'schedule', page: page || 1, schedule: true })
},
search: function (page) {
search: async function (page) {
const opts = {
method: 'Search',
page: page || 1
@ -68,9 +64,8 @@ export function loadHomePage () {
for (const element of homeSearchElements) {
if (element.value) opts[element.dataset.option] = element.value
}
alRequest(opts).then(res => {
galleryAppend({ media: res.data.Page.media, gallery: browseGallery, method: 'search', page: page || 1 })
})
const mediaList = (await alRequest(opts)).data.Page.media
galleryAppend({ media: mediaList, gallery: browseGallery, method: 'search', page: page || 1 })
}
}
const homePreviewFunctions = {

View file

@ -68,7 +68,7 @@ client.on('playlist', () => {
})
client.on('next', ({ filemedia }) => {
if (filemedia.media) {
nyaaSearch(filemedia.media, filemedia.episodeNumber + 1)
nyaaSearch(filemedia.media, parseInt(filemedia.episodeNumber) + 1)
} else {
halfmoon.initStickyAlert({
content: 'Couldn\'t find anime name! Try specifying a torrent manually.',

View file

@ -6,6 +6,7 @@ import { releasesCards } from './interface.js'
import { userBrowser, DOMPARSER } from './util.js'
import { client } from './main.js'
import halfmoon from 'halfmoon'
import { episodeRx } from './anime.js'
const exclusions = {
edge: ['DTS'],
@ -52,22 +53,32 @@ export async function nyaaRss (media, episode) {
}
}
entries.sort((a, b) => b.seeders - a.seeders)
const streamingEpisode = media?.streamingEpisodes.filter(episode => episodeRx.exec(episode.title) && Number(episodeRx.exec(episode.title)[1]) === Number(episode))[0]
const fileMedia = {
mediaTitle: media?.title.userPreferred,
episodeNumber: Number(episode),
episodeTitle: streamingEpisode ? episodeRx.exec(streamingEpisode.title)[2] : undefined,
episodeThumbnail: streamingEpisode?.thumbnail,
mediaCover: media?.coverImage.medium,
name: 'Miru',
media: media
}
if (settings.torrent2) {
client.playTorrent(entries[0].hash, { media: media, episode: episode })
client.playTorrent(entries[0].hash, { media: fileMedia, episode: episode })
halfmoon.hideModal('tsearch')
}
entries.forEach((entry, index) => {
const template = document.createElement('tr')
template.innerHTML += `
<th>${(index + 1)}</th>
<td>${entry.title}</td>
<td>${entry.size}</td>
<td>${entry.seeders}</td>
<td>${entry.leechers}</td>
<td>${entry.downloads}</td>
<td class="pointer">Play</td>`
<th>${(index + 1)}</th>
<td>${entry.title}</td>
<td>${entry.size}</td>
<td>${entry.seeders}</td>
<td>${entry.leechers}</td>
<td>${entry.downloads}</td>
<td class="pointer">Play</td>`
template.onclick = () => {
client.playTorrent(entry.hash, { media: media, episode: episode })
client.playTorrent(entry.hash, { media: fileMedia, episode: episode })
halfmoon.hideModal('tsearch')
}
frag.appendChild(template)

183
faq.html
View file

@ -2,105 +2,104 @@
<html lang="en">
<head>
<link rel="manifest" href="manifest.json">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Miru">
<meta name="description" content="Anime torrent streaming, ad free in a simple solution.">
<meta name="theme-color" content="#111417" />
<link rel="manifest" href="manifest.json">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Miru">
<meta name="description" content="Anime torrent streaming, ad free in a simple solution.">
<meta name="theme-color" content="#111417" />
<link rel="apple-touch-icon" href="logo.png">
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
<meta name="viewport" content="width=device-width" />
<link rel="apple-touch-icon" href="logo.png">
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" href="logo.png">
<title>Miru - FAQ</title>
<link href="https://cdn.jsdelivr.net/npm/halfmoon@1.1.0/css/halfmoon-variables.min.css" rel="stylesheet" />
<link rel="icon" href="logo.png">
<title>Miru - FAQ</title>
<link href="https://cdn.jsdelivr.net/npm/halfmoon@1.1.0/css/halfmoon-variables.min.css" rel="stylesheet" />
</head>
<body class="with-custom-webkit-scrollbars with-custom-css-scrollbars">
<div class="page-wrapper">
<div class="content-wrapper d-flex flex-column">
<a href="/" class="w-200 align-self-center"><img src="logo.png" class="w-200" alt="logo"></a>
<div class="container-lg">
<div class="content">
<h2 class="content-title">
How does it work?
</h2>
<p>
Miru uses WebTorrent and black magic to turn your browser into a P2P client which allows you to
stream existing torrents created by other people, without any third-party servers.
</p>
</div>
<div class="content">
<h2 class="content-title">
Is it legal?
</h2>
<p>
Miru only provides the tools for people to share anime with eachother. Miru doesn't host or
transmit any data.
</p>
</div>
<div class="content">
<h2 class="content-title">
Is it safe?
</h2>
<p>
Since the user downloads and shares copyrighted content, in theory their ISP might be angry
about, in practice streaming a single episode won't do much, however if you want to be safe you
can use a VPN, but it's in no way required.
</p>
</div>
<div class="content">
<h2 class="content-title">
But is it safe?
</h2>
<p>
Yes. At one point in time, WebRTC did have an issue where it would allow a website to discover
your true public IP address, but this was fixed a long time ago. This unfortunate misinformation
keeps bouncing around the internet.<br>
There's now a spec that defines exactly which IP addresses are exposed with WebRTC. If you're
interested in further reading, you can read the IP <a
href="https://tools.ietf.org/html/draft-ietf-rtcweb-ip-handling-12">handling spec</a> for
yourself.
</p>
</div>
<div class="content">
<h2 class="content-title">
Are there any limitations?
</h2>
<p>
Yes, since this relies on P2P streaming, sharing content entirely depends on the download and
upload speed of the user and how many available peers a torrent has. You can help increase the
number of initial peers by upvoting <a href="https://github.com/nyaadevs/nyaa/pull/623" target="_blank">this</a> pull request on GitHub.
</p>
</div>
<div class="content">
<h2 class="content-title">
What's your subtitle support?
</h2>
<p>
Miru uses supports softcoded subtitles, meaning it should work on any platform and support many
devices. Miru only supports subtitles embedded inside the video file [SSA, UTF8, VTT, SRT*]
in almost any video format, but doesn't support external subtitle files [yet].
</p>
</div>
<div class="content">
<h2 class="content-title">
How do you download?
</h2>
<p>
As you select an anime to play it automatically starts downloading, then you need to wait for
WebTorrent to download it fully to be able to save it to your drive.
</p>
</div>
</div>
<div class="page-wrapper">
<div class="content-wrapper d-flex flex-column">
<a href="/" class="w-200 align-self-center"><img src="logo.png" class="w-200" alt="logo"></a>
<div class="container-lg">
<div class="content">
<h2 class="content-title">
How does it work?
</h2>
<p>
Miru uses WebTorrent and black magic to turn your browser into a P2P client which allows you to
stream existing torrents created by other people, without any third-party servers.
</p>
</div>
<div class="content">
<h2 class="content-title">
Is it legal?
</h2>
<p>
Miru only provides the tools for people to share anime with eachother. Miru doesn't host or
transmit any data.
</p>
</div>
<div class="content">
<h2 class="content-title">
Is it safe?
</h2>
<p>
Since the user downloads and shares copyrighted content, in theory their ISP might be angry
about, in practice streaming a single episode won't do much, however if you want to be safe you
can use a VPN, but it's in no way required.
</p>
</div>
<div class="content">
<h2 class="content-title">
But is it safe?
</h2>
<p>
Yes. At one point in time, WebRTC did have an issue where it would allow a website to discover
your true public IP address tru your VPN, but this was fixed a long time ago. This unfortunate
misinformation keeps bouncing around the internet.<br>
There's now a spec that defines exactly which IP addresses are exposed with WebRTC. If you're
interested in further reading, you can read the IP
<a href="https://tools.ietf.org/html/draft-ietf-rtcweb-ip-handling-12">handling spec</a> for
yourself.
</p>
</div>
<div class="content">
<h2 class="content-title">
Are there any limitations?
</h2>
<p>
Yes, since this relies on P2P streaming, sharing content entirely depends on the download and
upload speed of the user and how many available peers a torrent has.
</p>
</div>
<div class="content">
<h2 class="content-title">
What's your subtitle support?
</h2>
<p>
Miru uses supports softcoded subtitles, meaning it should work on any platform and support many
devices. Miru only supports subtitles embedded inside the video file [ASS, SSA, UTF8, VTT, SRT]
in almost any video format, and external subtitles.
</p>
</div>
<div class="content">
<h2 class="content-title">
How do you download files?
</h2>
<p>
As you select an anime to play it automatically starts downloading, then you can at any point save it to
your drive. If you're using a chromium browser you can alternatively specify the folder where the player
should store it's torrent files [recommended to use a HDD].
</p>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/halfmoon@1.1.0/js/halfmoon.min.js"></script>
</div>
</body>
</html>

View file

@ -2,91 +2,91 @@
<html lang="en">
<head>
<link rel="manifest" href="manifest.json">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Miru">
<meta name="description" content="Anime torrent streaming, ad free in a simple solution.">
<meta name="theme-color" content="#111417" />
<link rel="manifest" href="site.webmanifest">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Miru">
<meta name="description" content="Anime torrent streaming, ad free in a simple solution.">
<meta name="theme-color" content="#111417" />
<link rel="apple-touch-icon" href="logo.png">
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
<meta name="viewport" content="width=device-width" />
<meta property="og:title" content="Miru">
<meta property="og:url" content="https://mirumoe.netlify.app/">
<meta property="og:description" content="Miru - Torrent streaming made simple!">
<meta property="og:type" content="video.other">
<meta property="og:image" content="logo.png">
<link rel="apple-touch-icon" href="logo.png">
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
<meta name="viewport" content="width=device-width" />
<meta property="og:title" content="Miru">
<meta property="og:url" content="https://mirumoe.netlify.app/">
<meta property="og:description" content="Miru - Torrent streaming made simple!">
<meta property="og:type" content="video.other">
<meta property="og:image" content="logo.png">
<link rel="icon" href="logo.png">
<title>Miru - Torrent streaming made simple!</title>
<link href="https://cdn.jsdelivr.net/npm/halfmoon@1.1.0/css/halfmoon-variables.min.css" rel="stylesheet" />
<link rel="search" type="application/opensearchdescription+xml" title="Content Search" href="search.xml">
<script>
window.onload = () => {
navigator.serviceWorker.register('sw.js')
let deferredPrompt;
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
deferredPrompt = e;
});
add.addEventListener('click', (e) => {
deferredPrompt.prompt();
deferredPrompt.userChoice.then((choiceResult) => {
if (choiceResult.outcome === 'accepted') {
document.location.href = "/app/#browse"
}
});
});
}
</script>
<link rel="icon" href="logo.png">
<title>Miru - Torrent streaming made simple!</title>
<link href="https://cdn.jsdelivr.net/npm/halfmoon@1.1.0/css/halfmoon-variables.min.css" rel="stylesheet" />
<link rel="search" type="application/opensearchdescription+xml" title="Content Search" href="search.xml">
<script>
window.onload = () => {
navigator.serviceWorker.register('sw.js')
let deferredPrompt
window.addEventListener('beforeinstallprompt', e => {
e.preventDefault()
deferredPrompt = e
})
add.addEventListener('click', e => {
deferredPrompt.prompt()
deferredPrompt.userChoice.then(choiceResult => {
if (choiceResult.outcome === 'accepted') {
document.location.href = "/app/#browse"
}
})
})
}
</script>
</head>
<body class="with-custom-webkit-scrollbars with-custom-css-scrollbars dark-mode">
<div class="page-wrapper with-navbar-fixed-bottom">
<div class="content-wrapper d-lg-flex align-items-lg-center">
<div class="container-lg">
<div class="row">
<div class="col-lg-8">
<div class="content">
<h1 class="font-weight-medium">
Miru - Anime torrent streaming, ad free in a simple solution.
</h1>
<p class="font-size-18">
Miru is an otaku's dream, a website which allows you to stream and download anime
torrents
directly in the browser as soon as they are released. Accessible on any platform and
device. Anime in original quality with
no buffering, no ads, no delays, everything done client-side for free.
</p>
<div class="mt-20">
<div class="btn btn-primary" role="button" id="add">
Add App
</div>
<a class="btn" role="button" href="/app/#home">
Open In Browser
</a>
</div>
</div>
</div>
<div class="col-lg-4 d-flex align-items-center justify-content-center">
<img class="w-400" alt="char" src="/char.webp">
</div>
<div class="page-wrapper with-navbar-fixed-bottom">
<div class="content-wrapper d-lg-flex align-items-lg-center">
<div class="container-lg">
<div class="row">
<div class="col-lg-8">
<div class="content">
<h1 class="font-weight-medium">
Miru - Anime torrent streaming, ad free in a simple solution.
</h1>
<p class="font-size-18">
Miru is an otaku's dream, a website which allows you to stream and download anime
torrents
directly in the browser as soon as they are released. Accessible on any platform and
device. Anime in original quality with
no buffering, no ads, no delays, everything done client-side for free.
</p>
<div class="mt-20">
<div class="btn btn-primary" role="button" id="add">
Add App
</div>
<a class="btn" role="button" href="/app/#home">
Open In Browser
</a>
</div>
</div>
</div>
<div class="col-lg-4 d-flex align-items-center justify-content-center">
<img class="w-400" alt="char" src="/char.webp">
</div>
</div>
<nav class="navbar navbar-fixed-bottom justify-content-center">
<a href="/faq.html">
FAQ
</a>
<span class="navbar-text">
© Copyright 2020, Miru
</span>
</nav>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/halfmoon@1.1.0/js/halfmoon.min.js"></script>
<nav class="navbar navbar-fixed-bottom justify-content-center">
<a href="/faq.html">
FAQ
</a>
<span class="navbar-text">
© Copyright 2020, Miru
</span>
</nav>
</div>
<script src="https://cdn.jsdelivr.net/npm/halfmoon@1.1.0/js/halfmoon.min.js"></script>
</body>
</html>

View file

@ -2,270 +2,270 @@
<html lang="en">
<head>
<link rel="manifest" href="manifest.json">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Miru">
<meta name="description" content="Anime torrent streaming, ad free in a simple solution.">
<meta name="theme-color" content="#111417" />
<link rel="manifest" href="manifest.json">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Miru">
<meta name="description" content="Anime torrent streaming, ad free in a simple solution.">
<meta name="theme-color" content="#111417" />
<link rel="apple-touch-icon" href="logo.png">
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
<meta name="viewport" content="width=device-width" />
<link rel="apple-touch-icon" href="logo.png">
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" href="logo.png">
<title>Miru - FAQ</title>
<link href="https://cdn.jsdelivr.net/npm/halfmoon@1.1.0/css/halfmoon-variables.min.css" rel="stylesheet" />
<link rel="icon" href="logo.png">
<title>Miru - FAQ</title>
<link href="https://cdn.jsdelivr.net/npm/halfmoon@1.1.0/css/halfmoon-variables.min.css" rel="stylesheet" />
</head>
<body class="dark-mode with-custom-webkit-scrollbars with-custom-css-scrollbars">
<div class="page-wrapper">
<div class="content-wrapper d-flex flex-column">
<a href="/" class="w-200 align-self-center"><img src="logo.png" class="w-200" alt="logo"></a>
<div class="container-lg">
<table class="table table-striped bg-dark rounded my-5">
<thead>
<tr>
<th>Containers</th>
<th class="text-center">Chromium</th>
<th class="text-center">Mobile Chromium</th>
<th class="text-center">Edge Chromium</th>
<th class="text-center">Firefox</th>
</tr>
</thead>
<tbody>
<tr>
<th>3g2</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>3gp</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>avi</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>m2ts</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-secondary text-center">✓**</td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>m4v etc.</th>
<td class="text-secondary text-center">✓*</td>
<td class="text-secondary text-center">✓*</td>
<td class="text-secondary text-center">✓*</td>
<td class="text-secondary text-center">✓*</td>
</tr>
<tr>
<th>mp4</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>mpeg</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>mov</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>ogm ogv</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>webm</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>mkv</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-danger text-center"></td>
</tr>
</tbody>
</table>
* Container might be supported, but the container's codecs might not be.<br>
** Documented as working, but can't reproduce.<br>
Full list of all tested video extensions: <br>
.3g2 .3gp .asf .avi .dv .flv .gxf .m2ts .m4a .m4b .m4p .m4r .m4v .mkv .mov .mp4 .mpd .mpeg .mpg .mxf
.nut .ogm .ogv .swf .ts .vob .webm .wmv .wtv<br>
Are any missing?
<table class="table table-striped bg-dark rounded mt-15 mb-5">
<thead>
<tr>
<th>Video Codecs</th>
<th class="text-center">Chromium</th>
<th class="text-center">Mobile Chromium</th>
<th class="text-center">Edge Chromium</th>
<th class="text-center">Firefox</th>
</tr>
</thead>
<tbody>
<tr>
<th>AV1</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>H.263</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>H.264</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>H.265</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-secondary text-center">✓*</td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>MPEG-2/4</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>Theora</th>
<td class="text-success text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>VP8/9</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
</tbody>
</table>
* Requires MSStore extension which you can get by clicking <a
href="ms-windows-store://pdp/?ProductId=9n4wgh0z6vhq">this link</a> while using Edge.
<table class="table table-striped bg-dark rounded mt-15 mb-5">
<thead>
<tr>
<th>Audio Codecs</th>
<th class="text-center">Chromium</th>
<th class="text-center">Mobile Chromium</th>
<th class="text-center">Edge Chromium</th>
<th class="text-center">Firefox</th>
</tr>
</thead>
<tbody>
<tr>
<th>AAC</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>AC3</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-success text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>DTS</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>EAC3</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-success text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>FLAC</th>
<td class="text-success text-center"></td>
<td class="text-secondary text-center">✓*</td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>MP3</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>Opus</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>TrueHD</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>Vorbis</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-secondary text-center">✓*</td>
</tr>
</tbody>
</table>
* Might not work in some video containers.<br><br>
</div>
</div>
<div class="page-wrapper">
<div class="content-wrapper d-flex flex-column">
<a href="/" class="w-200 align-self-center"><img src="logo.png" class="w-200" alt="logo"></a>
<div class="container-lg">
<table class="table table-striped bg-dark rounded my-5">
<thead>
<tr>
<th>Containers</th>
<th class="text-center">Chromium</th>
<th class="text-center">Mobile Chromium</th>
<th class="text-center">Edge Chromium</th>
<th class="text-center">Firefox</th>
</tr>
</thead>
<tbody>
<tr>
<th>3g2</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>3gp</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>avi</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>m2ts</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-secondary text-center">✓**</td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>m4v etc.</th>
<td class="text-secondary text-center">✓*</td>
<td class="text-secondary text-center">✓*</td>
<td class="text-secondary text-center">✓*</td>
<td class="text-secondary text-center">✓*</td>
</tr>
<tr>
<th>mp4</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>mpeg</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>mov</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>ogm ogv</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>webm</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>mkv</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-danger text-center"></td>
</tr>
</tbody>
</table>
* Container might be supported, but the container's codecs might not be.<br>
** Documented as working, but can't reproduce.<br>
Full list of all tested video extensions: <br>
.3g2 .3gp .asf .avi .dv .flv .gxf .m2ts .m4a .m4b .m4p .m4r .m4v .mkv .mov .mp4 .mpd .mpeg .mpg .mxf
.nut .ogm .ogv .swf .ts .vob .webm .wmv .wtv<br>
Are any missing?
<table class="table table-striped bg-dark rounded mt-15 mb-5">
<thead>
<tr>
<th>Video Codecs</th>
<th class="text-center">Chromium</th>
<th class="text-center">Mobile Chromium</th>
<th class="text-center">Edge Chromium</th>
<th class="text-center">Firefox</th>
</tr>
</thead>
<tbody>
<tr>
<th>AV1</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>H.263</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>H.264</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>H.265</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-secondary text-center">✓*</td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>MPEG-2/4</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>Theora</th>
<td class="text-success text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>VP8/9</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
</tbody>
</table>
* Requires MSStore extension which you can get by clicking <a
href="ms-windows-store://pdp/?ProductId=9n4wgh0z6vhq">this link</a> while using Edge.
<table class="table table-striped bg-dark rounded mt-15 mb-5">
<thead>
<tr>
<th>Audio Codecs</th>
<th class="text-center">Chromium</th>
<th class="text-center">Mobile Chromium</th>
<th class="text-center">Edge Chromium</th>
<th class="text-center">Firefox</th>
</tr>
</thead>
<tbody>
<tr>
<th>AAC</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>AC3</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-success text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>DTS</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>EAC3</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-success text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>FLAC</th>
<td class="text-success text-center"></td>
<td class="text-secondary text-center">✓*</td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>MP3</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>Opus</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
</tr>
<tr>
<th>TrueHD</th>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
<td class="text-danger text-center"></td>
</tr>
<tr>
<th>Vorbis</th>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-success text-center"></td>
<td class="text-secondary text-center">✓*</td>
</tr>
</tbody>
</table>
* Might not work in some video containers.<br><br>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/halfmoon@1.1.0/js/halfmoon.min.js"></script>
</div>
<script src="https://cdn.jsdelivr.net/npm/halfmoon@1.1.0/js/halfmoon.min.js"></script>
</body>
</html>