const torrentRx = /(magnet:){1}|(^[A-F\d]{8,40}$){1}|(.*\.torrent){1}/i,
imageRx = /\.(jpeg|jpg|gif|png|webp)/
window.addEventListener("paste", async e => { //WAIT image lookup on paste, or add torrent on paste
let item = e.clipboardData.items[0];
if (item && item.type.indexOf("image") === 0) {
e.preventDefault();
let reader = new FileReader();
reader.onload = e => {
traceAnime(e.target.result, "uri")
};
reader.readAsDataURL(item.getAsFile());
} else if (item && item.type === "text/plain") {
item.getAsString(text => {
if (torrentRx.exec(text)) {
e.preventDefault();
search.value = ""
addTorrent(text, {});
} else if (imageRx.exec(text)) {
e.preventDefault();
search.value = ""
traceAnime(text)
}
})
} else if (item && item.type === "text/html") {
item.getAsString(text => {
let img = new DOMParser().parseFromString(text, "text/html").querySelectorAll("img")[0]
if (img) {
e.preventDefault();
search.value = ""
traceAnime(img.src)
}
})
}
})
if (searchParams.get("link")) {
traceAnime(searchParams.get("link"))
window.location = "/app/#home"
}
function traceAnime(image, type) { //WAIT lookup logic
halfmoon.initStickyAlert({
content: `Looking Up Anime ${type == "uri" ? "" : `For ${image}`}`
})
let options,
url = `https://trace.moe/api/search?url=${image}`
if (type == "uri") {
options = {
method: "POST",
body: JSON.stringify({ image: image }),
headers: { "Content-Type": "application/json" },
},
url = "https://trace.moe/api/search"
}
fetch(url, options).then((res) => res.json())
.then(async (result) => {
if (result.docs[0].similarity >= 0.85) {
let res = await alRequest({ method: "SearchIDSingle", id: result.docs[0].anilist_id })
viewAnime(res.data.Media)
}
});
}
function searchBox() { // make searchbox behave nicely
search.placeholder = search.value
searchAnime(search.value)
search.value = ""
document.location.hash = "#browse"
}
//events
navNowPlaying.onclick = () => { viewAnime(playerData.nowPlaying[0]) }
//AL lookup logic
async function alRequest(opts) {
let query,
variables = {
type: "ANIME",
sort: opts.sort || "TRENDING_DESC",
page: opts.page || 1,
perPage: opts.perPage || 30,
status_in: opts.status_in || "[CURRENT,PLANNING]",
chunk: opts.chunk || 1,
perchunk: opts.perChunk || 30
},
options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
query: query,
variables: variables
})
},
queryObjects = `
id
title {
romaji
english
native
userPreferred
}
description(
asHtml: true
)
season
seasonYear
format
status
episodes
duration
averageScore
genres
coverImage {
extraLarge
medium
color
}
countryOfOrigin
isAdult
bannerImage
synonyms
nextAiringEpisode {
timeUntilAiring
episode
}
trailer {
id
site
}
streamingEpisodes {
title
thumbnail
}
relations {
edges {
relationType(version:2)
node {
id
title {
userPreferred
}
coverImage {
medium
}
type
status
format
episodes
}
}
}`
if (opts.status) variables.status = opts.status
if (localStorage.getItem("ALtoken")) options.headers['Authorization'] = localStorage.getItem("ALtoken")
if (opts.method == "Trending") {
search.placeholder = "Search"
query = `
query ($page: Int, $perPage: Int, $sort: [MediaSort], $type: MediaType) {
Page (page: $page, perPage: $perPage) {
media(type: $type, sort: $sort) {
${queryObjects}
}
}
}`
} else if (opts.method == "SearchName") {
variables.search = opts.name
query = `
query ($page: Int, $perPage: Int, $sort: [MediaSort], $type: MediaType, $search: String, $status: MediaStatus) {
Page (page: $page, perPage: $perPage) {
media(type: $type, search: $search, sort: $sort, status: $status) {
${queryObjects}
}
}
}`
} else if (opts.method == "SearchIDSingle") {
variables.id = opts.id
query = `
query ($id: Int, $type: MediaType) {
Media (id: $id, type: $type){
${queryObjects}
}
}`
} else if (opts.method == "Viewer") {
query = `
query {
Viewer {
avatar {
medium
},
name,
id
}
}`
} else if (opts.method == "UserLists") {
variables.id = opts.id
query = `
query ($page: Int, $perPage: Int, $id: Int, $type: MediaType, $status_in: [MediaListStatus]){
Page (page: $page, perPage: $perPage) {
mediaList (userId: $id, type: $type, status_in: $status_in) {
media {
${queryObjects}
}
}
}
}`
} else if (opts.method == "SearchIDStatus") {
variables.id = alID
variables.mediaId = opts.id
query = `
query ($id: Int, $mediaId: Int){
MediaList(userId: $id, mediaId: $mediaId) {
status
progress
repeat
}
}`
} else if (opts.method == "Genre") {
variables.genre = opts.genre
query = `
query ($page: Int, $perPage: Int, $sort: [MediaSort], $type: MediaType, $genre: String) {
Page (page: $page, perPage: $perPage) {
media(type: $type, sort: $sort, genre: $genre) {
${queryObjects}
}
}
}`
} else if (opts.method == "AiringSchedule") {
let date = new Date(),
diff = date.getDay() >= 1 ? date.getDay() - 1 : 6 - date.getDay();
date.setDate(date.getDate() - diff)
date.setHours(0, 0, 0, 0)
variables.from = date.getTime() / 1000
variables.to = (date.getTime() + 7 * 24 * 60 * 60 * 1000) / 1000
console.log(variables)
query = `
query ($page: Int, $perPage: Int, $from: Int, $to: Int) {
Page (page: $page, perPage: $perPage) {
airingSchedules(airingAt_greater: $from, airingAt_lesser: $to) {
episode
timeUntilAiring
airingAt
media{
${queryObjects}
}
}
}
}`
}
options.body = JSON.stringify({
query: query,
variables: variables
})
let res = await fetch('https://graphql.anilist.co', options).catch((error) => console.error(error)),
json = await res.json();
console.log(json)
return json
}
async function alEntry() {
if (playerData.nowPlaying && playerData.nowPlaying[0] && localStorage.getItem("ALtoken")) {
let res = await alRequest({ method: "SearchIDStatus", id: playerData.nowPlaying[0].id })
if ((res.errors && res.errors[0].status === 404) || res.data.MediaList.progress <= parseInt(playerData.nowPlaying[1])) {
let query = `
mutation ($id: Int, $status: MediaListStatus, $episode: Int, $repeat: Int) {
SaveMediaListEntry (mediaId: $id, status: $status, progress: $episode, repeat: $repeat) {
id
status
progress
repeat
}
}`,
variables = {
repeat: 0,
id: playerData.nowPlaying[0].id,
status: "CURRENT",
episode: parseInt(playerData.nowPlaying[1])
}
if (parseInt(playerData.nowPlaying[1]) == playerData.nowPlaying[0].episodes) {
variables.status = "COMPLETED"
if (res.data.MediaList.status == "COMPLETED") {
variables.repeat = res.data.MediaList.repeat + 1
}
}
let options = {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + localStorage.getItem("ALtoken"),
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
query: query,
variables: variables
})
}
fetch("https://graphql.anilist.co", options).catch((error) => console.error(error))
}
}
}
let alResponse
async function searchAnime(a) { //search bar functionality
let frag = document.createDocumentFragment(),
browse = document.querySelector(".browse")
browse.innerHTML = '';
browse.appendChild(skeletonCard)
a ? alResponse = await alRequest({ method: "SearchName", name: a }) : alResponse = await alRequest({ method: "Trending" })
try {
alResponse.data.Page.media.forEach(media => {
let template = cardCreator({ media: media })
template.onclick = () => {
viewAnime(media)
}
frag.appendChild(template)
})
} catch (e) {
console.error(e)
}
browse.innerHTML = '';
browse.appendChild(frag)
}
//these really shouldnt be global
let detailsfrag = document.createDocumentFragment()
let details = {
averageScore: "Average Score",
// duration: "Episode Duration",
// episodes: "Episodes",
// format: "Format",
genres: "Genres",
// season: "Season",
// seasonYear: "Year",
status: "Status",
english: "English",
romaji: "Romaji",
native: "Native",
synonyms: "Synonyms"
}
const episodeRx = /Episode (\d+) - (.*)/;
// this is fucked beyond belief, this is why you use frameworks
function viewAnime(media) {
halfmoon.showModal("view")
view.setAttribute("style", `background-image: url(${media.bannerImage}) !important`)
viewImg.src = media.coverImage.extraLarge
viewTitle.innerHTML = media.title.userPreferred
viewDesc.innerHTML = media.description || ""
viewDetails.innerHTML = ""
detailsCreator(media)
viewDetails.appendChild(detailsfrag)
if (media.nextAiringEpisode) {
let temp = document.createElement("p")
temp.innerHTML = `Airing
Episode ${media.nextAiringEpisode.episode}: ${countdown(media.nextAiringEpisode.timeUntilAiring)}`
viewDetails.prepend(temp)
}
viewSeason.innerHTML = `${(media.season ? media.season.toLowerCase() + " " : "") + (media.seasonYear ? media.seasonYear : "")}`
viewMediaInfo.innerHTML = `${media.format ? "" + media.format + "" : ""}${media.episodes ? "" + media.episodes + " Episodes" : ""}${media.duration ? "" + media.duration + " Minutes" : ""}`
viewPlay.onclick = () => { nyaaSearch(media, 1); halfmoon.toggleModal("view") }
if (media.trailer) {
viewTrailer.removeAttribute("disabled", "")
viewTrailer.onclick = () =>
trailerPopup(media.trailer)
} else {
viewTrailer.setAttribute("disabled", "")
}
if (media.status == "NOT_YET_RELEASED") {
viewPlay.setAttribute("disabled", "")
} else {
viewPlay.removeAttribute("disabled", "")
}
if (media.relations.edges.length) {
viewRelationsGallery.classList.remove("d-none")
viewRelationsGallery.innerHTML = ""
let frag = document.createDocumentFragment()
media.relations.edges.forEach(edge => {
let template = document.createElement("div")
template.classList.add("card", "m-0", "p-0")
template.innerHTML = `
${edge.node.title.userPreferred}
${edge.relationType.toLowerCase()}
${(opts.media.format ? (opts.media.format == "TV" ? "" + opts.media.format + " Show" : "" + opts.media.format.toLowerCase().replace(/_/g, " ")) : "") + ""} ${opts.media.episodes ? "" + opts.media.episodes + " Episodes" : opts.media.duration ? "" + opts.media.duration + " Minutes" : ""} ${opts.media.status ? "" + opts.media.status.toLowerCase().replace(/_/g, " ") + "" : ""} ${opts.media.season || opts.media.seasonYear ? "" + ((opts.media.season.toLowerCase() || "") + " ") + (opts.media.seasonYear || "") + "" : ""}
`}