added proxy and proxy result merging system
This commit is contained in:
parent
5ae5e837c6
commit
5e2d94703b
@ -1,8 +1,11 @@
|
|||||||
import type { CrunchyLogin } from './Types'
|
import type { CrunchyLogin } from './Types'
|
||||||
|
|
||||||
export async function crunchyLogin() {
|
export async function crunchyLogin(geo: string) {
|
||||||
const { data, error } = await useFetch<CrunchyLogin>('http://localhost:9941/api/crunchyroll/login', {
|
const { data, error } = await useFetch<CrunchyLogin>('http://localhost:9941/api/crunchyroll/login', {
|
||||||
method: 'POST'
|
method: 'POST',
|
||||||
|
query: {
|
||||||
|
geo: geo
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return { data, error }
|
return { data, error }
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
import type { CrunchyrollSearchResults } from '../Search/Types'
|
import type { CrunchyrollSearchResults } from '../Search/Types'
|
||||||
import { crunchyLogin } from './Account'
|
import { crunchyLogin } from './Account'
|
||||||
|
import { getProxies } from './Proxy'
|
||||||
import type { CrunchyAnimeFetch, CrunchySearchFetch } from './Types'
|
import type { CrunchyAnimeFetch, CrunchySearchFetch } from './Types'
|
||||||
|
|
||||||
export async function searchCrunchy(q: string) {
|
export async function searchCrunchy(q: string) {
|
||||||
const { data: token, error: tokenerror } = await crunchyLogin()
|
|
||||||
|
const { data: proxies } = await getProxies()
|
||||||
|
|
||||||
|
const { data: token, error: tokenerror } = await crunchyLogin('LOCAL')
|
||||||
|
|
||||||
if (!token.value) {
|
if (!token.value) {
|
||||||
return
|
return
|
||||||
@ -27,6 +31,56 @@ export async function searchCrunchy(q: string) {
|
|||||||
throw new Error(JSON.stringify(error.value))
|
throw new Error(JSON.stringify(error.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (proxies.value) {
|
||||||
|
for (const p of proxies.value) {
|
||||||
|
if (p.status !== 'offline') {
|
||||||
|
const { data: tokeng, error: tokenerrorg } = await crunchyLogin(p.code)
|
||||||
|
|
||||||
|
if (!tokeng.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data: fdata, error: ferror } = await useFetch<CrunchySearchFetch>(`https://beta-api.crunchyroll.com/content/v2/discover/search`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${tokeng.value.access_token}`
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
q: q,
|
||||||
|
n: 100,
|
||||||
|
type: 'series',
|
||||||
|
ratings: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ferror.value) {
|
||||||
|
console.error(ferror.value)
|
||||||
|
throw new Error(JSON.stringify(ferror.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fdata.value) {
|
||||||
|
for (const r of fdata.value.data[0].items) {
|
||||||
|
if (!data.value?.data[0].items.find((d) => d.id === r.id)) {
|
||||||
|
r.geo = p.code
|
||||||
|
data.value?.data[0].items.push(r)
|
||||||
|
} else {
|
||||||
|
for (const l of r.series_metadata.audio_locales) {
|
||||||
|
if (!data.value.data[0].items.find(d => d.id === r.id)?.series_metadata.audio_locales.find(loc => loc === l)) {
|
||||||
|
data.value.data[0].items.find(d => d.id === r.id)?.series_metadata.audio_locales.push(l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const l of r.series_metadata.subtitle_locales) {
|
||||||
|
if (!data.value.data[0].items.find(d => d.id === r.id)?.series_metadata.subtitle_locales.find(loc => loc === l)) {
|
||||||
|
data.value.data[0].items.find(d => d.id === r.id)?.series_metadata.subtitle_locales.push(l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!data.value) return
|
if (!data.value) return
|
||||||
|
|
||||||
var results: CrunchyrollSearchResults = []
|
var results: CrunchyrollSearchResults = []
|
||||||
@ -43,7 +97,8 @@ export async function searchCrunchy(q: string) {
|
|||||||
Seasons: result.series_metadata.season_count,
|
Seasons: result.series_metadata.season_count,
|
||||||
PEGI: result.series_metadata.maturity_ratings,
|
PEGI: result.series_metadata.maturity_ratings,
|
||||||
Year: result.series_metadata.series_launch_year,
|
Year: result.series_metadata.series_launch_year,
|
||||||
Images: result.images
|
Images: result.images,
|
||||||
|
Geo: result.geo
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +106,9 @@ export async function searchCrunchy(q: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getCRSeries(q: string) {
|
export async function getCRSeries(q: string) {
|
||||||
const { data: token, error: tokenerror } = await crunchyLogin()
|
const { data: proxies } = await getProxies()
|
||||||
|
|
||||||
|
const { data: token, error: tokenerror } = await crunchyLogin('LOCAL')
|
||||||
|
|
||||||
if (!token.value) {
|
if (!token.value) {
|
||||||
return
|
return
|
||||||
@ -69,6 +126,36 @@ export async function getCRSeries(q: string) {
|
|||||||
throw new Error(JSON.stringify(error.value))
|
throw new Error(JSON.stringify(error.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!data.value && proxies.value) {
|
||||||
|
for (const p of proxies.value) {
|
||||||
|
if (p.status !== 'offline') {
|
||||||
|
const { data: tokeng, error: tokenerrorg } = await crunchyLogin(p.code)
|
||||||
|
|
||||||
|
if (!tokeng.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data: fdata, error: ferror } = await useFetch<CrunchyAnimeFetch>(`https://beta-api.crunchyroll.com/content/v2/cms/series/${q}`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${tokeng.value.access_token}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (ferror.value) {
|
||||||
|
console.error(ferror.value)
|
||||||
|
throw new Error(JSON.stringify(ferror.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fdata.value) {
|
||||||
|
fdata.value.data[0].geo = p.code
|
||||||
|
}
|
||||||
|
|
||||||
|
data.value = fdata.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!data.value) return
|
if (!data.value) return
|
||||||
|
|
||||||
const anime = data.value.data[0]
|
const anime = data.value.data[0]
|
||||||
@ -84,6 +171,7 @@ export async function getCRSeries(q: string) {
|
|||||||
Seasons: anime.season_count,
|
Seasons: anime.season_count,
|
||||||
PEGI: anime.maturity_ratings,
|
PEGI: anime.maturity_ratings,
|
||||||
Year: anime.series_launch_year,
|
Year: anime.series_launch_year,
|
||||||
Images: anime.images
|
Images: anime.images,
|
||||||
|
Geo: undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { crunchyLogin } from './Account'
|
import { crunchyLogin } from './Account'
|
||||||
import type { CrunchyEpisodesFetch } from './Types'
|
import type { CrunchyEpisodesFetch } from './Types'
|
||||||
|
|
||||||
export async function listEpisodeCrunchy(q: string) {
|
export async function listEpisodeCrunchy(q: string, geo: string | undefined) {
|
||||||
const { data: token, error: tokenerror } = await crunchyLogin()
|
const { data: token, error: tokenerror } = await crunchyLogin(geo ? geo : 'LOCAL')
|
||||||
|
|
||||||
if (!token.value) {
|
if (!token.value) {
|
||||||
return
|
return
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import { crunchyLogin } from './Account'
|
import { crunchyLogin } from './Account'
|
||||||
|
import { getProxies } from './Proxy'
|
||||||
import type { CrunchySeasonsFetch } from './Types'
|
import type { CrunchySeasonsFetch } from './Types'
|
||||||
|
|
||||||
export async function listSeasonCrunchy(q: string) {
|
export async function listSeasonCrunchy(q: string, geo: string | undefined) {
|
||||||
const { data: token, error: tokenerror } = await crunchyLogin()
|
const { data: proxies } = await getProxies()
|
||||||
|
|
||||||
|
const { data: token, error: tokenerror } = await crunchyLogin(geo ? geo : 'LOCAL')
|
||||||
|
|
||||||
if (!token.value) {
|
if (!token.value) {
|
||||||
return
|
return
|
||||||
@ -22,5 +25,23 @@ export async function listSeasonCrunchy(q: string) {
|
|||||||
|
|
||||||
if (!data.value) return
|
if (!data.value) return
|
||||||
|
|
||||||
|
if (proxies.value) {
|
||||||
|
for (const p of proxies.value) {
|
||||||
|
if (p.status !== 'offline') {
|
||||||
|
const { data: gdata, error: gerror } = await useFetch<CrunchySeasonsFetch>(`https://beta-api.crunchyroll.com/content/v2/cms/series/${q}/seasons`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token.value.access_token}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (gerror.value) {
|
||||||
|
console.error(error.value)
|
||||||
|
throw new Error(JSON.stringify(error.value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return data.value.data
|
return data.value.data
|
||||||
}
|
}
|
||||||
|
9
components/Crunchyroll/Proxy.ts
Normal file
9
components/Crunchyroll/Proxy.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import type { Proxies } from "./Types"
|
||||||
|
|
||||||
|
export async function getProxies() {
|
||||||
|
const { data, error } = await useFetch<Proxies>('http://localhost:9941/api/service/proxies', {
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
|
||||||
|
return { data, error }
|
||||||
|
}
|
@ -53,6 +53,7 @@ export interface CrunchySearchFetch {
|
|||||||
}
|
}
|
||||||
linked_resource_key: string
|
linked_resource_key: string
|
||||||
type: string
|
type: string
|
||||||
|
geo: string | undefined
|
||||||
}>
|
}>
|
||||||
}>
|
}>
|
||||||
}
|
}
|
||||||
@ -107,6 +108,7 @@ export interface CrunchyAnimeFetch {
|
|||||||
}
|
}
|
||||||
linked_resource_key: string
|
linked_resource_key: string
|
||||||
type: string
|
type: string
|
||||||
|
geo: string | undefined
|
||||||
}>
|
}>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,3 +238,12 @@ export interface CrunchyEpisodesFetch {
|
|||||||
versions_considered: boolean
|
versions_considered: boolean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Proxy {
|
||||||
|
name: string
|
||||||
|
code: string
|
||||||
|
url: string
|
||||||
|
status: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Proxies extends Array<Proxy> {}
|
||||||
|
@ -27,6 +27,7 @@ export interface CrunchyrollSearchResult {
|
|||||||
}>
|
}>
|
||||||
>
|
>
|
||||||
}
|
}
|
||||||
|
Geo: string | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CrunchyrollSearchResults extends Array<CrunchyrollSearchResult> {}
|
export interface CrunchyrollSearchResults extends Array<CrunchyrollSearchResult> {}
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
v-for="l in locales"
|
v-for="l in locales"
|
||||||
@click="toggleDub(l)"
|
@click="toggleDub(l)"
|
||||||
class="flex flex-row items-center justify-center gap-3 py-2 rounded-xl text-sm"
|
class="flex flex-row items-center justify-center gap-3 py-2 rounded-xl text-sm"
|
||||||
:class="dubLocales.find((i) => i.locale === l.locale) ? 'bg-[#424242]' : 'hover:bg-[#747474]'"
|
:class="dubLocales && dubLocales.find((i) => i.locale === l.locale) ? 'bg-[#424242]' : 'hover:bg-[#747474]'"
|
||||||
>
|
>
|
||||||
{{ l.name }}
|
{{ l.name }}
|
||||||
</button>
|
</button>
|
||||||
@ -38,7 +38,7 @@
|
|||||||
v-for="l in locales"
|
v-for="l in locales"
|
||||||
@click="toggleSub(l)"
|
@click="toggleSub(l)"
|
||||||
class="flex flex-row items-center justify-center gap-3 py-2 rounded-xl text-sm"
|
class="flex flex-row items-center justify-center gap-3 py-2 rounded-xl text-sm"
|
||||||
:class="subLocales.find((i) => i.locale === l.locale) ? 'bg-[#424242]' : 'hover:bg-[#747474]'"
|
:class="subLocales && subLocales.find((i) => i.locale === l.locale) ? 'bg-[#424242]' : 'hover:bg-[#747474]'"
|
||||||
>
|
>
|
||||||
{{ l.name }}
|
{{ l.name }}
|
||||||
</button>
|
</button>
|
||||||
|
68
components/Settings/Proxy.vue
Normal file
68
components/Settings/Proxy.vue
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col mt-3 gap-3 font-dm" style="-webkit-app-region: no-drag">
|
||||||
|
<div class="flex flex-col items-center p-3 bg-[#11111189] rounded-xl select-none">
|
||||||
|
<div class="text-sm mb-2"> Proxy Settings </div>
|
||||||
|
<div class="flex flex-row">
|
||||||
|
<input type="checkbox" name="Login Proxy" class="cursor-pointer">
|
||||||
|
<div class="text-sm ml-1.5">
|
||||||
|
Use Login Proxies
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col items-center p-3 bg-[#11111189] rounded-xl select-none" :class="fetchingProxies ? 'h-44' : 'h-auto'">
|
||||||
|
<div class="text-sm mb-2"> Global Proxies </div>
|
||||||
|
<div v-if="fetchingProxies" class="flex flex-row items-center mt-5 text-sm">
|
||||||
|
<Icon name="mdi:loading" class="h-5 w-5 text-white animate-spin mr-2" />
|
||||||
|
Checking Proxies
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="proxy in proxies"
|
||||||
|
class="flex flex-row items-center h-12 p-3 w-full bg-[#4b4b4b89] rounded-xl"
|
||||||
|
:class="proxy.status === 'offline' ? 'bg-[#991e1e89]' : 'bg-[#4f991e89]'"
|
||||||
|
>
|
||||||
|
<Icon name="mdi:proxy" class="h-6 w-6 text-white" />
|
||||||
|
<div class="text-[14px] ml-2">
|
||||||
|
{{ proxy.name }}
|
||||||
|
</div>
|
||||||
|
<div class="text-xs ml-auto uppercase" :class="proxy.status === 'offline' ? 'text-red-400' : 'text-green-400'">
|
||||||
|
{{ proxy.status }}
|
||||||
|
</div>
|
||||||
|
<div class="h-2.5 w-2.5 rounded-full ml-1.5" :class="proxy.status === 'offline' ? 'bg-red-400' : 'bg-green-400'"> </div>
|
||||||
|
<!-- <div class="flex flex-row ml-2">
|
||||||
|
<button @click="deleteAccount(account.id)" class="flex items-center justify-center bg-red-500 hover:bg-red-600 w-8 h-8 rounded-lg transition-all">
|
||||||
|
<Icon name="majesticons:logout" class="h-4 w-4 text-white" />
|
||||||
|
</button>
|
||||||
|
</div> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const proxies = ref<{ name: string; url: string; status: string }[]>()
|
||||||
|
const fetchingProxies = ref<0>(0)
|
||||||
|
|
||||||
|
const getProxies = async () => {
|
||||||
|
fetchingProxies.value++
|
||||||
|
|
||||||
|
const { data, error } = await useFetch<{ name: string; url: string; status: string }[]>(`http://localhost:9941/api/service/proxies`, {
|
||||||
|
method: 'GET'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (error.value) {
|
||||||
|
alert(error.value)
|
||||||
|
fetchingProxies.value--
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchingProxies.value--
|
||||||
|
|
||||||
|
if (!data.value) return
|
||||||
|
|
||||||
|
proxies.value = data.value
|
||||||
|
}
|
||||||
|
|
||||||
|
getProxies()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
@ -594,7 +594,9 @@ const refetchEpisodes = async () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
episodes.value = await listEpisodeCrunchy(selectedSeason.value.id)
|
if (!CRselectedShow.value) return
|
||||||
|
|
||||||
|
episodes.value = await listEpisodeCrunchy(selectedSeason.value.id, CRselectedShow.value.Geo)
|
||||||
if (episodes.value) {
|
if (episodes.value) {
|
||||||
selectedStartEpisode.value = episodes.value[0]
|
selectedStartEpisode.value = episodes.value[0]
|
||||||
selectedEndEpisode.value = episodes.value[0]
|
selectedEndEpisode.value = episodes.value[0]
|
||||||
@ -623,13 +625,13 @@ const switchToSeason = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (CRselectedShow.value) {
|
if (CRselectedShow.value) {
|
||||||
seasons.value = await listSeasonCrunchy(CRselectedShow.value.ID)
|
seasons.value = await listSeasonCrunchy(CRselectedShow.value.ID, CRselectedShow.value.Geo)
|
||||||
if (!seasons.value) {
|
if (!seasons.value) {
|
||||||
isFetchingSeasons.value--
|
isFetchingSeasons.value--
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
selectedSeason.value = seasons.value[0]
|
selectedSeason.value = seasons.value[0]
|
||||||
episodes.value = await listEpisodeCrunchy(selectedSeason.value.id)
|
episodes.value = await listEpisodeCrunchy(selectedSeason.value.id, CRselectedShow.value.Geo)
|
||||||
if (episodes.value) {
|
if (episodes.value) {
|
||||||
selectedStartEpisode.value = episodes.value[0]
|
selectedStartEpisode.value = episodes.value[0]
|
||||||
selectedEndEpisode.value = episodes.value[0]
|
selectedEndEpisode.value = episodes.value[0]
|
||||||
@ -642,13 +644,14 @@ const switchToSeason = async () => {
|
|||||||
if (url.value && url.value.includes('crunchyroll') && !CRselectedShow.value) {
|
if (url.value && url.value.includes('crunchyroll') && !CRselectedShow.value) {
|
||||||
const seriesID = url.value.split('/')
|
const seriesID = url.value.split('/')
|
||||||
CRselectedShow.value = await getCRSeries(seriesID[5])
|
CRselectedShow.value = await getCRSeries(seriesID[5])
|
||||||
seasons.value = await listSeasonCrunchy(seriesID[5])
|
if (!CRselectedShow.value) return
|
||||||
|
seasons.value = await listSeasonCrunchy(CRselectedShow.value.ID, CRselectedShow.value.Geo)
|
||||||
if (!seasons.value) {
|
if (!seasons.value) {
|
||||||
isFetchingSeasons.value--
|
isFetchingSeasons.value--
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
selectedSeason.value = seasons.value[0]
|
selectedSeason.value = seasons.value[0]
|
||||||
episodes.value = await listEpisodeCrunchy(selectedSeason.value.id)
|
episodes.value = await listEpisodeCrunchy(selectedSeason.value.id, CRselectedShow.value.Geo)
|
||||||
if (episodes.value) {
|
if (episodes.value) {
|
||||||
selectedStartEpisode.value = episodes.value[0]
|
selectedStartEpisode.value = episodes.value[0]
|
||||||
selectedEndEpisode.value = episodes.value[0]
|
selectedEndEpisode.value = episodes.value[0]
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
<div class="text-xs">{{ p.quality }}p</div>
|
<div class="text-xs">{{ p.quality }}p</div>
|
||||||
<div class="text-xs uppercase">{{ p.format }}</div>
|
<div class="text-xs uppercase">{{ p.format }}</div>
|
||||||
<div class="text-xs">Dubs: {{ p.dub.map((t) => t.name).join(', ') }}</div>
|
<div class="text-xs">Dubs: {{ p.dub.map((t) => t.name).join(', ') }}</div>
|
||||||
<div class="text-xs">Subs: {{ p.sub.length !== 0 ? p.sub.map((t) => t.name).join(', ') : '-' }}</div>
|
<div class="text-xs mr-14">Subs: {{ p.sub.length !== 0 ? p.sub.map((t) => t.name).join(', ') : '-' }}</div>
|
||||||
<div class="absolute flex flex-col ml-auto gap-0.5 right-0 bottom-0">
|
<div class="absolute flex flex-col ml-auto gap-0.5 right-0 bottom-0">
|
||||||
<div v-if="p.totaldownloaded && p.status === 'downloading'" class="text-xs ml-auto">{{ (p.totaldownloaded / Math.pow(1024, 2)).toFixed(2) }} MB</div>
|
<div v-if="p.totaldownloaded && p.status === 'downloading'" class="text-xs ml-auto">{{ (p.totaldownloaded / Math.pow(1024, 2)).toFixed(2) }} MB</div>
|
||||||
<div v-if="p.partsleft && p.status === 'downloading'" class="text-xs ml-auto">{{ p.partsdownloaded }}/{{ p.partsleft }}</div>
|
<div v-if="p.partsleft && p.status === 'downloading'" class="text-xs ml-auto">{{ p.partsdownloaded }}/{{ p.partsleft }}</div>
|
||||||
|
@ -16,12 +16,13 @@
|
|||||||
<SettingsMain v-if="activeIndex === 0" />
|
<SettingsMain v-if="activeIndex === 0" />
|
||||||
<SettingsCrunchyroll v-if="activeIndex === 1" />
|
<SettingsCrunchyroll v-if="activeIndex === 1" />
|
||||||
<SettingsWidevine v-if="activeIndex === 2" />
|
<SettingsWidevine v-if="activeIndex === 2" />
|
||||||
<SettingsAbout v-if="activeIndex === 3" />
|
<SettingsProxy v-if="activeIndex === 3" />
|
||||||
|
<SettingsAbout v-if="activeIndex === 4" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
const options = ref<Array<string>>(['Main', 'Crunchyroll', 'Widevine', 'About'])
|
const options = ref<Array<string>>(['Main', 'Crunchyroll', 'Widevine', 'Proxy', 'About'])
|
||||||
const activeIndex = ref(0)
|
const activeIndex = ref(0)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -2,14 +2,21 @@ import type { FastifyReply, FastifyRequest } from 'fastify'
|
|||||||
import { crunchyLogin } from './crunchyroll.service'
|
import { crunchyLogin } from './crunchyroll.service'
|
||||||
import { loggedInCheck } from '../service/service.service'
|
import { loggedInCheck } from '../service/service.service'
|
||||||
|
|
||||||
export async function loginController(request: FastifyRequest, reply: FastifyReply) {
|
export async function loginController(request: FastifyRequest<{
|
||||||
|
Querystring: {
|
||||||
|
geo: string
|
||||||
|
}
|
||||||
|
}>, reply: FastifyReply) {
|
||||||
|
|
||||||
|
const query = request.query
|
||||||
|
|
||||||
const account = await loggedInCheck('CR')
|
const account = await loggedInCheck('CR')
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
return reply.code(401).send({ message: 'Not Logged in' })
|
return reply.code(401).send({ message: 'Not Logged in' })
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data, error } = await crunchyLogin(account.username, account.password)
|
const { data, error } = await crunchyLogin(account.username, account.password, query.geo)
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
reply.code(400).send(error)
|
reply.code(400).send(error)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { messageBox } from '../../../electron/background'
|
import { messageBox } from '../../../electron/background'
|
||||||
import { server } from '../../api'
|
import { server } from '../../api'
|
||||||
import { VideoPlaylist } from '../../types/crunchyroll'
|
import { VideoPlaylist, VideoPlaylistNoGEO } from '../../types/crunchyroll'
|
||||||
import { useFetch } from '../useFetch'
|
import { useFetch } from '../useFetch'
|
||||||
import { parse as mpdParse } from 'mpd-parser'
|
import { parse as mpdParse } from 'mpd-parser'
|
||||||
import { loggedInCheck } from '../service/service.service'
|
import { loggedInCheck } from '../service/service.service'
|
||||||
@ -14,8 +14,18 @@ const crErrors = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// Login Proxies
|
||||||
|
const proxies: { name: string; code: string; url: string; status: string | undefined }[] = [
|
||||||
|
{
|
||||||
|
name: 'US Proxy',
|
||||||
|
code: 'US',
|
||||||
|
url: 'https://us-proxy.crd.cx/',
|
||||||
|
status: undefined
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
// Crunchyroll Login Handler
|
// Crunchyroll Login Handler
|
||||||
export async function crunchyLogin(user: string, passw: string) {
|
export async function crunchyLogin(user: string, passw: string, geo: string) {
|
||||||
const cachedData:
|
const cachedData:
|
||||||
| {
|
| {
|
||||||
access_token: string
|
access_token: string
|
||||||
@ -27,10 +37,11 @@ export async function crunchyLogin(user: string, passw: string) {
|
|||||||
account_id: string
|
account_id: string
|
||||||
profile_id: string
|
profile_id: string
|
||||||
}
|
}
|
||||||
| undefined = server.CacheController.get('crtoken')
|
| undefined = server.CacheController.get(`crtoken-${geo}`)
|
||||||
|
|
||||||
if (!cachedData) {
|
if (!cachedData) {
|
||||||
var { data, error } = await crunchyLoginFetch(user, passw)
|
if (geo === 'LOCAL') {
|
||||||
|
const { data, error } = await crunchyLoginFetch(user, passw)
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
messageBox(
|
messageBox(
|
||||||
@ -54,16 +65,51 @@ export async function crunchyLogin(user: string, passw: string) {
|
|||||||
return { data: null, error: 'Crunchyroll returned malformed data' }
|
return { data: null, error: 'Crunchyroll returned malformed data' }
|
||||||
}
|
}
|
||||||
|
|
||||||
server.CacheController.set('crtoken', data, data.expires_in - 30)
|
server.CacheController.set(`crtoken-${geo}`, data, data.expires_in - 30)
|
||||||
|
|
||||||
return { data: data, error: null }
|
return { data: data, error: null }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (geo !== 'LOCAL') {
|
||||||
|
const { data, error } = await crunchyLoginFetchProxy(user, passw, geo)
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
messageBox(
|
||||||
|
'error',
|
||||||
|
['Cancel'],
|
||||||
|
2,
|
||||||
|
'Failed to login',
|
||||||
|
'Failed to login to Crunchyroll',
|
||||||
|
crErrors.find((r) => r.error === (error?.error as string)) ? crErrors.find((r) => r.error === (error?.error as string))?.response : (error.error as string)
|
||||||
|
)
|
||||||
|
return { data: null, error: error.error }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
messageBox('error', ['Cancel'], 2, 'Failed to login', 'Failed to login to Crunchyroll', 'Crunchyroll returned null')
|
||||||
|
return { data: null, error: 'Crunchyroll returned null' }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data.access_token) {
|
||||||
|
messageBox('error', ['Cancel'], 2, 'Failed to login', 'Failed to login to Crunchyroll', 'Crunchyroll returned malformed data')
|
||||||
|
return { data: null, error: 'Crunchyroll returned malformed data' }
|
||||||
|
}
|
||||||
|
|
||||||
|
server.CacheController.set(`crtoken-${geo}`, data, data.expires_in - 30)
|
||||||
|
|
||||||
|
return { data: data, error: null }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return { data: cachedData, error: null }
|
return { data: cachedData, error: null }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crunchyroll Login Fetch
|
// Crunchyroll Login Fetch Proxy
|
||||||
async function crunchyLoginFetch(user: string, passw: string) {
|
async function crunchyLoginFetchProxy(user: string, passw: string, geo: string) {
|
||||||
|
var host: string | undefined
|
||||||
|
|
||||||
|
host = proxies.find((p) => p.code === geo)?.url
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
Authorization: 'Basic dC1rZGdwMmg4YzNqdWI4Zm4wZnE6eWZMRGZNZnJZdktYaDRKWFMxTEVJMmNDcXUxdjVXYW4=',
|
Authorization: 'Basic dC1rZGdwMmg4YzNqdWI4Zm4wZnE6eWZMRGZNZnJZdktYaDRKWFMxTEVJMmNDcXUxdjVXYW4=',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@ -88,7 +134,7 @@ async function crunchyLoginFetch(user: string, passw: string) {
|
|||||||
country: string
|
country: string
|
||||||
account_id: string
|
account_id: string
|
||||||
profile_id: string
|
profile_id: string
|
||||||
}>('https://crd.cx/auth/v1/token', {
|
}>(host + 'auth/v1/token', {
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
header: headers,
|
header: headers,
|
||||||
@ -108,19 +154,61 @@ async function crunchyLoginFetch(user: string, passw: string) {
|
|||||||
return { data: data, error: null }
|
return { data: data, error: null }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crunchyroll Playlist Fetch
|
async function crunchyLoginFetch(user: string, passw: string) {
|
||||||
export async function crunchyGetPlaylist(q: string) {
|
const headers = {
|
||||||
|
Authorization: 'Basic dC1rZGdwMmg4YzNqdWI4Zm4wZnE6eWZMRGZNZnJZdktYaDRKWFMxTEVJMmNDcXUxdjVXYW4=',
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
'User-Agent': 'Crunchyroll/3.46.2 Android/13 okhttp/4.12.0'
|
||||||
|
}
|
||||||
|
|
||||||
var endpoint = await settings.get('CREndpoint');
|
const body: any = {
|
||||||
const drmL3blob = await settings.get('l3blob');
|
username: user,
|
||||||
const drmL3key = await settings.get('l3key');
|
password: passw,
|
||||||
|
grant_type: 'password',
|
||||||
|
scope: 'offline_access',
|
||||||
|
device_name: 'RMX2170',
|
||||||
|
device_type: 'realme RMX2170'
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await useFetch<{
|
||||||
|
access_token: string
|
||||||
|
refresh_token: string
|
||||||
|
expires_in: number
|
||||||
|
token_type: string
|
||||||
|
scope: string
|
||||||
|
country: string
|
||||||
|
account_id: string
|
||||||
|
profile_id: string
|
||||||
|
}>('https://beta-api.crunchyroll.com/auth/v1/token', {
|
||||||
|
type: 'POST',
|
||||||
|
body: new URLSearchParams(body).toString(),
|
||||||
|
header: headers,
|
||||||
|
credentials: 'same-origin'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return { data: null, error: error }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
|
return { data: null, error: null }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { data: data, error: null }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crunchyroll Playlist Fetch
|
||||||
|
export async function crunchyGetPlaylist(q: string, geo: string | undefined) {
|
||||||
|
var endpoint = await settings.get('CREndpoint')
|
||||||
|
const drmL3blob = await settings.get('l3blob')
|
||||||
|
const drmL3key = await settings.get('l3key')
|
||||||
|
|
||||||
if (!drmL3blob || !drmL3key) {
|
if (!drmL3blob || !drmL3key) {
|
||||||
await settings.set('CREndpoint', 1);
|
await settings.set('CREndpoint', 1)
|
||||||
endpoint = 1
|
endpoint = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
const endpoints: { id: number, name: string, url: string }[] = [
|
const endpoints: { id: number; name: string; url: string }[] = [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Switch',
|
name: 'Switch',
|
||||||
@ -185,28 +273,32 @@ export async function crunchyGetPlaylist(q: string) {
|
|||||||
id: 13,
|
id: 13,
|
||||||
name: 'Samsung TV',
|
name: 'Samsung TV',
|
||||||
url: `/tv/samsung/play`
|
url: `/tv/samsung/play`
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const account = await loggedInCheck('CR')
|
const account = await loggedInCheck('CR')
|
||||||
|
|
||||||
if (!account) return
|
if (!account) return
|
||||||
|
|
||||||
const { data: login, error } = await crunchyLogin(account.username, account.password)
|
const { data: loginLocal, error } = await crunchyLogin(account.username, account.password, geo ? geo : 'LOCAL')
|
||||||
|
|
||||||
if (!login) return
|
if (!loginLocal) return
|
||||||
|
|
||||||
const headers = {
|
const headersLoc = {
|
||||||
Authorization: `Bearer ${login.access_token}`,
|
Authorization: `Bearer ${loginLocal.access_token}`,
|
||||||
'X-Cr-Disable-Drm': 'true'
|
'X-Cr-Disable-Drm': 'true'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var playlist: VideoPlaylist
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`https://cr-play-service.prd.crunchyrollsvc.com/v1/${q}${endpoints.find(e=> e.id === endpoint) ? endpoints.find(e=> e.id === endpoint)?.url : '/console/switch/play'}`,
|
`https://cr-play-service.prd.crunchyrollsvc.com/v1/${q}${
|
||||||
|
endpoints.find((e) => e.id === endpoint) ? endpoints.find((e) => e.id === endpoint)?.url : '/console/switch/play'
|
||||||
|
}`,
|
||||||
{
|
{
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: headers
|
headers: headersLoc
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -217,9 +309,10 @@ export async function crunchyGetPlaylist(q: string) {
|
|||||||
|
|
||||||
data.subtitles = Object.values((data as any).subtitles)
|
data.subtitles = Object.values((data as any).subtitles)
|
||||||
|
|
||||||
return { data: data, account_id: login.account_id }
|
data.geo = geo
|
||||||
} else {
|
|
||||||
|
|
||||||
|
playlist = data
|
||||||
|
} else {
|
||||||
const error = await response.text()
|
const error = await response.text()
|
||||||
|
|
||||||
messageBox('error', ['Cancel'], 2, 'Failed to get MPD Playlist', 'Failed to get MPD Playlist', error)
|
messageBox('error', ['Cancel'], 2, 'Failed to get MPD Playlist', 'Failed to get MPD Playlist', error)
|
||||||
@ -228,6 +321,66 @@ export async function crunchyGetPlaylist(q: string) {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new Error(e as string)
|
throw new Error(e as string)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const p of proxies) {
|
||||||
|
if (p.code !== loginLocal.country) {
|
||||||
|
const { data: login, error } = await crunchyLogin(account.username, account.password, p.code)
|
||||||
|
|
||||||
|
if (!login) return
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
Authorization: `Bearer ${login.access_token}`,
|
||||||
|
'X-Cr-Disable-Drm': 'true'
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`https://cr-play-service.prd.crunchyrollsvc.com/v1/${q}${
|
||||||
|
endpoints.find((e) => e.id === endpoint) ? endpoints.find((e) => e.id === endpoint)?.url : '/console/switch/play'
|
||||||
|
}`,
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
headers: headers
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const data: VideoPlaylistNoGEO = JSON.parse(await response.text())
|
||||||
|
|
||||||
|
data.hardSubs = Object.values((data as any).hardSubs)
|
||||||
|
|
||||||
|
data.subtitles = Object.values((data as any).subtitles)
|
||||||
|
|
||||||
|
for (const v of data.versions) {
|
||||||
|
if (!playlist.versions.find((ver) => ver.guid === v.guid)) {
|
||||||
|
playlist.versions.push({ ...v, geo: p.code })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const v of data.subtitles) {
|
||||||
|
if (!playlist.subtitles.find((ver) => ver.language === v.language)) {
|
||||||
|
playlist.subtitles.push({ ...v, geo: p.code })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const v of data.hardSubs) {
|
||||||
|
if (!playlist.hardSubs.find((ver) => ver.hlang === v.hlang)) {
|
||||||
|
playlist.hardSubs.push({ ...v, geo: p.code })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const error = await response.text()
|
||||||
|
|
||||||
|
messageBox('error', ['Cancel'], 2, 'Failed to get MPD Playlist', 'Failed to get MPD Playlist', error)
|
||||||
|
throw new Error(error)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(e as string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { data: playlist, account_id: loginLocal.account_id }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crunchyroll Delete Video Token Fetch
|
// Crunchyroll Delete Video Token Fetch
|
||||||
@ -236,7 +389,7 @@ export async function deleteVideoToken(content: string, token: string) {
|
|||||||
|
|
||||||
if (!account) return
|
if (!account) return
|
||||||
|
|
||||||
const { data: login, error } = await crunchyLogin(account.username, account.password)
|
const { data: login, error } = await crunchyLogin(account.username, account.password, 'LOCAL')
|
||||||
|
|
||||||
if (!login) return
|
if (!login) return
|
||||||
|
|
||||||
@ -262,12 +415,12 @@ export async function deleteVideoToken(content: string, token: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Crunchyroll MPD Fetch
|
// Crunchyroll MPD Fetch
|
||||||
export async function crunchyGetPlaylistMPD(q: string) {
|
export async function crunchyGetPlaylistMPD(q: string, geo: string | undefined) {
|
||||||
const account = await loggedInCheck('CR')
|
const account = await loggedInCheck('CR')
|
||||||
|
|
||||||
if (!account) return
|
if (!account) return
|
||||||
|
|
||||||
const { data } = await crunchyLogin(account.username, account.password)
|
const { data } = await crunchyLogin(account.username, account.password, geo ? geo : 'LOCAL')
|
||||||
|
|
||||||
if (!data) return
|
if (!data) return
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import { crunchyLogin } from '../crunchyroll/crunchyroll.service'
|
|||||||
import { addEpisodeToPlaylist, deleteAccountID, getAllAccounts, getDownloading, getPlaylist, loggedInCheck, safeLoginData } from './service.service'
|
import { addEpisodeToPlaylist, deleteAccountID, getAllAccounts, getDownloading, getPlaylist, loggedInCheck, safeLoginData } from './service.service'
|
||||||
import { CrunchyEpisodes } from '../../types/crunchyroll'
|
import { CrunchyEpisodes } from '../../types/crunchyroll'
|
||||||
import { adnLogin } from '../adn/adn.service'
|
import { adnLogin } from '../adn/adn.service'
|
||||||
|
import { server } from '../../api'
|
||||||
|
|
||||||
export async function checkLoginController(
|
export async function checkLoginController(
|
||||||
request: FastifyRequest<{
|
request: FastifyRequest<{
|
||||||
@ -46,7 +47,7 @@ export async function loginController(
|
|||||||
var responseError
|
var responseError
|
||||||
|
|
||||||
if (params.id === 'CR') {
|
if (params.id === 'CR') {
|
||||||
const { data, error } = await crunchyLogin(body.user, body.password)
|
const { data, error } = await crunchyLogin(body.user, body.password, 'LOCAL')
|
||||||
;(responseError = error), (responseData = data)
|
;(responseError = error), (responseData = data)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,3 +134,38 @@ export async function getPlaylistController(request: FastifyRequest, reply: Fast
|
|||||||
|
|
||||||
return reply.code(200).send(playlist.reverse())
|
return reply.code(200).send(playlist.reverse())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function checkProxiesController(
|
||||||
|
request: FastifyRequest,
|
||||||
|
reply: FastifyReply
|
||||||
|
) {
|
||||||
|
|
||||||
|
const cachedData = server.CacheController.get('proxycheck');
|
||||||
|
|
||||||
|
if (!cachedData) {
|
||||||
|
const proxies: { name: string, code: string, url: string, status: string | undefined }[] = [{
|
||||||
|
name: 'US Proxy', code: 'US', url: 'https://us-proxy.crd.cx/', status: undefined
|
||||||
|
}]
|
||||||
|
|
||||||
|
for (const p of proxies) {
|
||||||
|
const response = await fetch(
|
||||||
|
p.url + 'health',
|
||||||
|
{
|
||||||
|
method: 'GET',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
p.status = 'online'
|
||||||
|
} else {
|
||||||
|
p.status = 'offline'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.CacheController.set('proxycheck', proxies, 60)
|
||||||
|
|
||||||
|
return reply.code(200).send(proxies)
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply.code(200).send(cachedData)
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { FastifyInstance } from 'fastify'
|
import { FastifyInstance } from 'fastify'
|
||||||
import { addPlaylistController, checkLoginController, deleteAccountHandler, getAllAccountsHandler, getPlaylistController, loginController } from './service.controller'
|
import { addPlaylistController, checkLoginController, checkProxiesController, deleteAccountHandler, getAllAccountsHandler, getPlaylistController, loginController } from './service.controller'
|
||||||
|
|
||||||
async function serviceRoutes(server: FastifyInstance) {
|
async function serviceRoutes(server: FastifyInstance) {
|
||||||
server.post(
|
server.post(
|
||||||
@ -73,6 +73,7 @@ async function serviceRoutes(server: FastifyInstance) {
|
|||||||
},
|
},
|
||||||
getAllAccountsHandler
|
getAllAccountsHandler
|
||||||
)
|
)
|
||||||
|
|
||||||
server.delete(
|
server.delete(
|
||||||
'/account/:id',
|
'/account/:id',
|
||||||
{
|
{
|
||||||
@ -87,6 +88,21 @@ async function serviceRoutes(server: FastifyInstance) {
|
|||||||
},
|
},
|
||||||
deleteAccountHandler
|
deleteAccountHandler
|
||||||
)
|
)
|
||||||
|
|
||||||
|
server.get(
|
||||||
|
'/proxies',
|
||||||
|
{
|
||||||
|
schema: {
|
||||||
|
response: {
|
||||||
|
'4xx': {
|
||||||
|
error: { type: 'string' },
|
||||||
|
message: { type: 'string' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
checkProxiesController
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default serviceRoutes
|
export default serviceRoutes
|
||||||
|
@ -81,8 +81,8 @@ async function deletePlaylistandTMP() {
|
|||||||
deletePlaylistandTMP()
|
deletePlaylistandTMP()
|
||||||
|
|
||||||
// Update Playlist Item
|
// Update Playlist Item
|
||||||
export async function updatePlaylistByID(id: number, status: 'waiting' | 'preparing' | 'downloading' | 'merging' | 'decrypting' | 'completed' | 'failed') {
|
export async function updatePlaylistByID(id: number, status?: 'waiting' | 'preparing' | 'downloading' | 'merging' | 'decrypting' | 'completed' | 'failed', quality?: 1080 | 720 | 480 | 360 | 240) {
|
||||||
await Playlist.update({ status: status }, { where: { id: id } })
|
await Playlist.update({ status: status, quality: quality }, { where: { id: id } })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Episode to Playlist
|
// Add Episode to Playlist
|
||||||
@ -154,7 +154,8 @@ async function checkPlaylists() {
|
|||||||
(e.dataValues.media as CrunchyEpisode).episode_number,
|
(e.dataValues.media as CrunchyEpisode).episode_number,
|
||||||
e.dataValues.quality,
|
e.dataValues.quality,
|
||||||
e.dataValues.dir,
|
e.dataValues.dir,
|
||||||
e.dataValues.format
|
e.dataValues.format,
|
||||||
|
(e.dataValues.media as CrunchyEpisode).geo,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (e.dataValues.service === 'ADN') {
|
if (e.dataValues.service === 'ADN') {
|
||||||
@ -314,7 +315,8 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
episode: number,
|
episode: number,
|
||||||
quality: 1080 | 720 | 480 | 360 | 240,
|
quality: 1080 | 720 | 480 | 360 | 240,
|
||||||
downloadPath: string,
|
downloadPath: string,
|
||||||
format: 'mp4' | 'mkv'
|
format: 'mp4' | 'mkv',
|
||||||
|
geo: string | undefined
|
||||||
) {
|
) {
|
||||||
downloading.push({
|
downloading.push({
|
||||||
id: downloadID,
|
id: downloadID,
|
||||||
@ -326,7 +328,7 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
|
|
||||||
await updatePlaylistByID(downloadID, 'downloading')
|
await updatePlaylistByID(downloadID, 'downloading')
|
||||||
|
|
||||||
var playlist = await crunchyGetPlaylist(e)
|
var playlist = await crunchyGetPlaylist(e, geo)
|
||||||
|
|
||||||
if (!playlist) {
|
if (!playlist) {
|
||||||
await updatePlaylistByID(downloadID, 'failed')
|
await updatePlaylistByID(downloadID, 'failed')
|
||||||
@ -339,7 +341,7 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
const found = playlist.data.versions.find((v) => v.audio_locale === 'ja-JP')
|
const found = playlist.data.versions.find((v) => v.audio_locale === 'ja-JP')
|
||||||
if (found) {
|
if (found) {
|
||||||
await deleteVideoToken(episodeID, playlist.data.token)
|
await deleteVideoToken(episodeID, playlist.data.token)
|
||||||
playlist = await crunchyGetPlaylist(found.guid)
|
playlist = await crunchyGetPlaylist(found.guid, found.geo)
|
||||||
} else {
|
} else {
|
||||||
console.log('Exact Playlist not found, taking what crunchy gives.'),
|
console.log('Exact Playlist not found, taking what crunchy gives.'),
|
||||||
messageBox(
|
messageBox(
|
||||||
@ -381,6 +383,7 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
original: boolean
|
original: boolean
|
||||||
season_guid: string
|
season_guid: string
|
||||||
variant: string
|
variant: string
|
||||||
|
geo: string | undefined
|
||||||
}> = []
|
}> = []
|
||||||
|
|
||||||
const subDownloadList: Array<{
|
const subDownloadList: Array<{
|
||||||
@ -396,7 +399,7 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
if (playlist.data.audioLocale !== 'ja-JP') {
|
if (playlist.data.audioLocale !== 'ja-JP') {
|
||||||
const foundStream = playlist.data.versions.find((v) => v.audio_locale === 'ja-JP')
|
const foundStream = playlist.data.versions.find((v) => v.audio_locale === 'ja-JP')
|
||||||
if (foundStream) {
|
if (foundStream) {
|
||||||
subPlaylist = await crunchyGetPlaylist(foundStream.guid)
|
subPlaylist = await crunchyGetPlaylist(foundStream.guid, foundStream.geo)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
subPlaylist = playlist
|
subPlaylist = playlist
|
||||||
@ -426,7 +429,7 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
const list = await crunchyGetPlaylist(found.guid)
|
const list = await crunchyGetPlaylist(found.guid, found.geo)
|
||||||
if (list) {
|
if (list) {
|
||||||
const foundSub = list.data.subtitles.find((sub) => sub.language === d)
|
const foundSub = list.data.subtitles.find((sub) => sub.language === d)
|
||||||
if (foundSub) {
|
if (foundSub) {
|
||||||
@ -453,7 +456,8 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
media_guid: 'adas',
|
media_guid: 'adas',
|
||||||
original: false,
|
original: false,
|
||||||
season_guid: 'asdasd',
|
season_guid: 'asdasd',
|
||||||
variant: 'asd'
|
variant: 'asd',
|
||||||
|
geo: undefined
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
console.warn(`Audio ${d}.aac not found, skipping`)
|
console.warn(`Audio ${d}.aac not found, skipping`)
|
||||||
@ -481,11 +485,11 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
const audioDownload = async () => {
|
const audioDownload = async () => {
|
||||||
const audios: Array<string> = []
|
const audios: Array<string> = []
|
||||||
for (const v of dubDownloadList) {
|
for (const v of dubDownloadList) {
|
||||||
const list = await crunchyGetPlaylist(v.guid)
|
const list = await crunchyGetPlaylist(v.guid, v.geo)
|
||||||
|
|
||||||
if (!list) return
|
if (!list) return
|
||||||
|
|
||||||
const playlist = await crunchyGetPlaylistMPD(list.data.url)
|
const playlist = await crunchyGetPlaylistMPD(list.data.url, list.data.geo)
|
||||||
|
|
||||||
if (!playlist) return
|
if (!playlist) return
|
||||||
|
|
||||||
@ -572,7 +576,7 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const play = await crunchyGetPlaylist(code)
|
const play = await crunchyGetPlaylist(code, geo)
|
||||||
|
|
||||||
if (!play) {
|
if (!play) {
|
||||||
await updatePlaylistByID(downloadID, 'failed')
|
await updatePlaylistByID(downloadID, 'failed')
|
||||||
@ -582,22 +586,29 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
|
|
||||||
var downloadURL
|
var downloadURL
|
||||||
|
|
||||||
|
var downloadGEO
|
||||||
|
|
||||||
if (hardsub) {
|
if (hardsub) {
|
||||||
const hardsubURL = play.data.hardSubs.find((h) => h.hlang === subs[0])?.url
|
const hardsubURL = play.data.hardSubs.find((h) => h.hlang === subs[0])?.url
|
||||||
|
|
||||||
|
const hardsubGEO = play.data.hardSubs.find((h) => h.hlang === subs[0])?.geo
|
||||||
|
|
||||||
if (hardsubURL) {
|
if (hardsubURL) {
|
||||||
downloadURL = hardsubURL
|
downloadURL = hardsubURL
|
||||||
|
downloadGEO = hardsubGEO
|
||||||
console.log('Hardsub Playlist found')
|
console.log('Hardsub Playlist found')
|
||||||
} else {
|
} else {
|
||||||
downloadURL = play.data.url
|
downloadURL = play.data.url
|
||||||
|
downloadGEO = play.data.geo
|
||||||
console.log('Hardsub Playlist not found')
|
console.log('Hardsub Playlist not found')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
downloadURL = play.data.url
|
downloadURL = play.data.url
|
||||||
|
downloadGEO = play.data.geo
|
||||||
console.log('Hardsub disabled, skipping')
|
console.log('Hardsub disabled, skipping')
|
||||||
}
|
}
|
||||||
|
|
||||||
var mdp = await crunchyGetPlaylistMPD(downloadURL)
|
var mdp = await crunchyGetPlaylistMPD(downloadURL, downloadGEO)
|
||||||
|
|
||||||
if (!mdp) return
|
if (!mdp) return
|
||||||
|
|
||||||
@ -615,6 +626,9 @@ export async function downloadCrunchyrollPlaylist(
|
|||||||
`Resolution ${quality}p not found`,
|
`Resolution ${quality}p not found`,
|
||||||
`Resolution ${quality}p not found, using resolution ${mdp.playlists[0].attributes.RESOLUTION?.height}p instead`
|
`Resolution ${quality}p not found, using resolution ${mdp.playlists[0].attributes.RESOLUTION?.height}p instead`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await updatePlaylistByID(downloadID, undefined, mdp.playlists[0].attributes.RESOLUTION?.height as 1080 | 720 | 480 | 360 | 240)
|
||||||
|
|
||||||
hq = mdp.playlists[0]
|
hq = mdp.playlists[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,11 +102,55 @@ export interface CrunchyEpisode {
|
|||||||
description: string
|
description: string
|
||||||
episode_air_date: string
|
episode_air_date: string
|
||||||
eligible_region: string
|
eligible_region: string
|
||||||
|
geo: string | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CrunchyEpisodes extends Array<CrunchyEpisode> {}
|
export interface CrunchyEpisodes extends Array<CrunchyEpisode> {}
|
||||||
|
|
||||||
export interface VideoPlaylist {
|
export interface VideoPlaylist {
|
||||||
|
assetId: number
|
||||||
|
audioLocale: string
|
||||||
|
bifs: string
|
||||||
|
burnedInLocale: string
|
||||||
|
captions: string
|
||||||
|
hardSubs: Array<{
|
||||||
|
hlang: string
|
||||||
|
url: string
|
||||||
|
quality: string,
|
||||||
|
geo: string | undefined
|
||||||
|
}>
|
||||||
|
playbackType: string
|
||||||
|
session: {
|
||||||
|
renewSeconds: number
|
||||||
|
noNetworkRetryIntervalSeconds: number
|
||||||
|
noNetworkTimeoutSeconds: number
|
||||||
|
maximumPauseSeconds: number
|
||||||
|
endOfVideoUnloadSeconds: number
|
||||||
|
sessionExpirationSeconds: number
|
||||||
|
usesStreamLimits: boolean
|
||||||
|
}
|
||||||
|
subtitles: Array<{
|
||||||
|
format: string
|
||||||
|
language: string
|
||||||
|
url: string,
|
||||||
|
geo: string | undefined
|
||||||
|
}>
|
||||||
|
token: string
|
||||||
|
url: string
|
||||||
|
versions: Array<{
|
||||||
|
audio_locale: string
|
||||||
|
guid: string
|
||||||
|
is_premium_only: boolean
|
||||||
|
media_guid: string
|
||||||
|
original: boolean
|
||||||
|
season_guid: string
|
||||||
|
variant: string,
|
||||||
|
geo: string | undefined
|
||||||
|
}>
|
||||||
|
geo: string | undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VideoPlaylistNoGEO {
|
||||||
assetId: number
|
assetId: number
|
||||||
audioLocale: string
|
audioLocale: string
|
||||||
bifs: string
|
bifs: string
|
||||||
@ -143,4 +187,5 @@ export interface VideoPlaylist {
|
|||||||
season_guid: string
|
season_guid: string
|
||||||
variant: string
|
variant: string
|
||||||
}>
|
}>
|
||||||
|
geo: string | undefined
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user