added more adn stuff

This commit is contained in:
Daniel Haller 2024-04-24 03:22:29 +02:00
parent 63080b70f9
commit e1d36be2d7
9 changed files with 248 additions and 67 deletions

View File

@ -1,12 +1,12 @@
import type { ADNSearchFetch } from "./Types"; import type { ADNSearchFetch } from "./Types";
export async function searchADN(q: string) { export async function searchADN(q: string) {
const { data, error } = await useFetch<ADNSearchFetch>( const { data: deData, error: deError } = await useFetch<ADNSearchFetch>(
`https://gw.api.animationdigitalnetwork.fr/show/catalog`, `https://gw.api.animationdigitalnetwork.fr/show/catalog`,
{ {
method: "GET", method: "GET",
headers: { headers: {
"x-target-distribution": "de", "x-target-distribution": 'de',
}, },
query: { query: {
"maxAgeCategory": "18", "maxAgeCategory": "18",
@ -15,11 +15,64 @@ export async function searchADN(q: string) {
} }
); );
if (error.value) { if (deError.value) {
throw new Error(error.value?.data.message as string) throw new Error(deError.value?.data.message as string)
} }
if (!data.value) return if (!deData.value) return
return data.value.shows const { data: frData, error: frError } = await useFetch<ADNSearchFetch>(
`https://gw.api.animationdigitalnetwork.fr/show/catalog`,
{
method: "GET",
headers: {
"x-target-distribution": 'fr',
},
query: {
"maxAgeCategory": "18",
"search": q
}
}
);
if (frError.value) {
throw new Error(frError.value?.data.message as string)
}
if (!frData.value) return
const deShows = deData.value.shows;
const frShows = frData.value.shows;
const mergeLanguagesOfDuplicates = (shows: {
id: number
url: string
title: string
image2x: string
episodeCount: number,
languages: Array<string>
}[]) => {
shows.forEach(show => {
const existingShow = shows.find(s => s.id === show.id);
if (existingShow) {
const existingShowIndex = shows.findIndex(s=> s === existingShow);
const rawLanguages = [...show.languages, ...existingShow.languages];
const languages: Array<string> = []
for (const l of rawLanguages) {
if (!languages.includes(l)) {
languages.push(l)
}
}
show.languages = languages
}
});
return shows;
};
const allShows = mergeLanguagesOfDuplicates([...deShows, ...frShows]);
const unique = [...new Map(allShows.map((s) => [s.id, s])).values()];
return unique
} }

View File

@ -1,10 +1,10 @@
import type { ADNEpisodes, ADNEpisodesFetch } from './Types' import type { ADNEpisodes, ADNEpisodesFetch } from './Types'
export async function getEpisodesWithShowIdADN(id: number) { export async function getEpisodesWithShowIdADN(id: number, lang: 'de' | 'fr') {
const { data, error } = await useFetch<ADNEpisodesFetch>(`https://gw.api.animationdigitalnetwork.fr/video/show/${id}?offset=0&limit=-1&order=asc`, { const { data, error } = await useFetch<ADNEpisodesFetch>(`https://gw.api.animationdigitalnetwork.fr/video/show/${id}?offset=0&limit=-1&order=asc`, {
method: 'GET', method: 'GET',
headers: { headers: {
"x-target-distribution": "de", "x-target-distribution": lang,
}, },
}) })

View File

@ -4,7 +4,8 @@ export interface ADNSearchFetch {
url: string url: string
title: string title: string
image2x: string image2x: string
episodeCount: number episodeCount: number,
languages: Array<string>
}> }>
} }

View File

@ -1,43 +1,43 @@
export interface CrunchyrollSearchResult { export interface CrunchyrollSearchResult {
ID: string; ID: string
Url: string; Url: string
Title: string; Title: string
Description: string; Description: string
Dubs: Array<string>; Dubs: Array<string>
Subs: Array<string>; Subs: Array<string>
Episodes: number; Episodes: number
Seasons: number; Seasons: number
PEGI: Array<string>; PEGI: Array<string>
Year: number; Year: number
Images: { Images: {
poster_tall: Array< poster_tall: Array<
Array<{ Array<{
height: number; height: number
source: string; source: string
type: string; type: string
width: number; width: number
}> }>
>; >
poster_wide: Array< poster_wide: Array<
Array<{ Array<{
height: number; height: number
source: string; source: string
type: string; type: string
width: number; width: number
}> }>
>; >
}; }
} }
export interface CrunchyrollSearchResults export interface CrunchyrollSearchResults extends Array<CrunchyrollSearchResult> {}
extends Array<CrunchyrollSearchResult> {}
export interface ADNSearchResult { export interface ADNSearchResult {
id: number; id: number
url: string; url: string
title: string; title: string
image2x: string; image2x: string
episodeCount: number; episodeCount: number
languages: Array<string>
} }
export interface ADNSearchResults extends Array<ADNSearchResult> {} export interface ADNSearchResults extends Array<ADNSearchResult> {}

View File

@ -19,9 +19,6 @@
<option value="adn">ADN</option> <option value="adn">ADN</option>
</select> </select>
</div> </div>
<div v-if="service === 'adn'" class="text-sm text-center">
ADN downloader is still in beta and can only download ADN Germany
</div>
<div v-if="(isLoggedInCR && service === 'crunchyroll') || (isLoggedInADN && service === 'adn')" class="relative flex flex-col"> <div v-if="(isLoggedInCR && service === 'crunchyroll') || (isLoggedInADN && service === 'adn')" class="relative flex flex-col">
<input <input
v-model="search" v-model="search"
@ -188,6 +185,12 @@
</button> </button>
</div> </div>
</div> </div>
<div v-if="service === 'adn'" class="relative flex flex-col select-none">
<div class="bg-[#5c5b5b] focus:outline-none px-3 py-2 rounded-xl text-sm text-center cursor-pointer">
Dub:
{{ selectedDubs.map((t) => t.name).join(', ') }}
</div>
</div>
<div v-if="service === 'crunchyroll'" class="relative flex flex-col select-none"> <div v-if="service === 'crunchyroll'" class="relative flex flex-col select-none">
<div @click="selectSub ? (selectSub = false) : (selectSub = true)" class="bg-[#5c5b5b] focus:outline-none px-3 py-2 rounded-xl text-sm text-center cursor-pointer"> <div @click="selectSub ? (selectSub = false) : (selectSub = true)" class="bg-[#5c5b5b] focus:outline-none px-3 py-2 rounded-xl text-sm text-center cursor-pointer">
Subs: Subs:
@ -206,6 +209,12 @@
</button> </button>
</div> </div>
</div> </div>
<div v-if="service === 'adn'" class="relative flex flex-col select-none">
<div class="bg-[#5c5b5b] focus:outline-none px-3 py-2 rounded-xl text-sm text-center cursor-pointer">
Sub:
{{ selectedSubs.length !== 0 ? selectedSubs.map((t) => t.name).join(', ') : 'No Subs selected' }}
</div>
</div>
<div class="flex flex-row gap-3"> <div class="flex flex-row gap-3">
<div v-if="service === 'crunchyroll'" class="relative flex flex-col w-full"> <div v-if="service === 'crunchyroll'" class="relative flex flex-col w-full">
<select v-model="hardsub" name="episode" class="bg-[#5c5b5b] focus:outline-none px-3 py-2 rounded-xl text-sm text-center cursor-pointer" :disabled="isHardsubDisabled"> <select v-model="hardsub" name="episode" class="bg-[#5c5b5b] focus:outline-none px-3 py-2 rounded-xl text-sm text-center cursor-pointer" :disabled="isHardsubDisabled">
@ -557,16 +566,15 @@ const switchToSeason = async () => {
} }
if (ADNselectedShow.value) { if (ADNselectedShow.value) {
episodesADN.value = await getEpisodesWithShowIdADN(ADNselectedShow.value.id) episodesADN.value = await getEpisodesWithShowIdADN(ADNselectedShow.value.id, 'de')
if (!episodesADN.value) { if (!episodesADN.value) {
isFetchingSeasons.value-- isFetchingSeasons.value--
return return
} };
selectedStartEpisodeADN.value = episodesADN.value[0] selectedStartEpisodeADN.value = episodesADN.value[0]
selectedEndEpisodeADN.value = episodesADN.value[0] selectedEndEpisodeADN.value = episodesADN.value[0]
tab.value = 2 tab.value = 2
selectedDubs.value = [{ locale: 'ja-JP', name: 'JP' }], selectedDubs.value = [{ locale: 'ja-JP', name: 'JP' }];
selectedSubs.value = [{ locale: 'de-DE', name: 'DE' }]
} }
if (CRselectedShow.value) { if (CRselectedShow.value) {

View File

@ -217,9 +217,11 @@ export async function adnGetPlaylist(animeid: number) {
const data: ADNLink = await JSON.parse(await response.text()) const data: ADNLink = await JSON.parse(await response.text())
return { data: data, secret: token.random } return { data: data, secret: token.random }
} else { } else {
const data: ADNLink = JSON.parse(await response.text()) const data: { message: string, code: string, statusCode: string} = JSON.parse(await response.text())
return { data: data, secret: token.random } messageBox('error', ['Cancel'], 2, 'Failed to fetch Playlist', 'Failed to fetch ADN Playlist', `${data.message} - ${data.code}`)
return null
} }
} catch (e) { } catch (e) {
throw new Error(e as string) throw new Error(e as string)

View File

@ -1,7 +1,7 @@
import { Account, Playlist } from '../../db/database' import { Account, Playlist } from '../../db/database'
import { downloadMPDAudio } from '../../services/audio' import { downloadMPDAudio } from '../../services/audio'
import { concatenateTSFiles } from '../../services/concatenate' import { concatenateTSFiles } from '../../services/concatenate'
import { createFolder, createFolderName, deleteFolder } from '../../services/folder' import { createFolder, createFolderName, deleteFolder, deleteTemporaryFolders } from '../../services/folder'
import { downloadADNSub, downloadCRSub } from '../../services/subs' import { downloadADNSub, downloadCRSub } from '../../services/subs'
import { CrunchyEpisode } from '../../types/crunchyroll' import { CrunchyEpisode } from '../../types/crunchyroll'
import { crunchyGetPlaylist, crunchyGetPlaylistMPD } from '../crunchyroll/crunchyroll.service' import { crunchyGetPlaylist, crunchyGetPlaylistMPD } from '../crunchyroll/crunchyroll.service'
@ -44,6 +44,16 @@ export async function getPlaylist() {
return episodes return episodes
} }
// Delete Playlist and TEMP folders After Start
async function deletePlaylistandTMP() {
await Playlist.truncate()
deleteTemporaryFolders()
}
deletePlaylistandTMP()
// Update Playlist Item // Update Playlist Item
export async function updatePlaylistByID(id: number, status: 'waiting' | 'preparing' | 'downloading' | 'completed' | 'merging' | 'failed') { export async function updatePlaylistByID(id: number, status: 'waiting' | 'preparing' | 'downloading' | 'completed' | 'merging' | 'failed') {
await Playlist.update({ status: status }, { where: { id: id } }) await Playlist.update({ status: status }, { where: { id: id } })
@ -177,7 +187,10 @@ export async function downloadADNPlaylist(
const seasonFolder = await createFolderName(`${name.replace(/[/\\?%*:|"<>]/g, '')} Season ${season}`, downloadPath) const seasonFolder = await createFolderName(`${name.replace(/[/\\?%*:|"<>]/g, '')} Season ${season}`, downloadPath)
const subDownload = async () => { const subDownload = async () => {
if (!playlist) return if (!playlist) {
await updatePlaylistByID(downloadID, 'failed')
return
}
const sbs: Array<string> = [] const sbs: Array<string> = []
const name = await downloadADNSub(playlist.data.links.subtitles.all, subFolder, playlist.secret) const name = await downloadADNSub(playlist.data.links.subtitles.all, subFolder, playlist.secret)
sbs.push(name) sbs.push(name)
@ -185,9 +198,10 @@ export async function downloadADNPlaylist(
} }
const downloadVideo = async () => { const downloadVideo = async () => {
var code if (!playlist) {
await updatePlaylistByID(downloadID, 'failed')
if (!playlist) return return
}
var link: string = ''; var link: string = '';
@ -222,9 +236,17 @@ export async function downloadADNPlaylist(
const [subss, file] = await Promise.all([subDownload(), downloadVideo()]) const [subss, file] = await Promise.all([subDownload(), downloadVideo()])
if (!subss) return if (!subss) {
await updatePlaylistByID(downloadID, 'failed')
return
}
await mergeVideoFile(file as string, [], [], String(playlist?.data.video.guid), seasonFolder, `${name.replace(/[/\\?%*:|"<>]/g, '')} Season ${season} Episode ${episode}`, format) if (!file) {
await updatePlaylistByID(downloadID, 'failed')
return
}
await mergeVideoFile(file as string, [], subss, String(playlist?.data.video.guid), seasonFolder, `${name.replace(/[/\\?%*:|"<>]/g, '')} Season ${season} Episode ${episode}`, format)
await updatePlaylistByID(downloadID, 'completed') await updatePlaylistByID(downloadID, 'completed')

View File

@ -3,7 +3,7 @@ import { app } from 'electron'
import fs from 'fs' import fs from 'fs'
export async function createFolder() { export async function createFolder() {
const tempFolderPath = path.join(app.getPath('documents'), (Math.random() + 1).toString(36).substring(2)) const tempFolderPath = path.join(app.getPath('documents'), `crd-tmp-${(Math.random() + 1).toString(36).substring(2)}`)
try { try {
await fs.promises.mkdir(tempFolderPath, { recursive: true }) await fs.promises.mkdir(tempFolderPath, { recursive: true })
return tempFolderPath return tempFolderPath
@ -52,3 +52,22 @@ export async function createFolderName(name: string, dir: string) {
export async function deleteFolder(folderPath: string) { export async function deleteFolder(folderPath: string) {
fs.rmSync(folderPath, { recursive: true, force: true }) fs.rmSync(folderPath, { recursive: true, force: true })
} }
export async function deleteTemporaryFolders() {
const documentsPath = app.getPath('documents');
const folderPrefix = 'crd-tmp-';
try {
const files = await fs.promises.readdir(documentsPath);
const tempFolders = files.filter(file => file.startsWith(folderPrefix));
for (const folder of tempFolders) {
const folderPath = path.join(documentsPath, folder);
await deleteFolder(folderPath);
console.log(`Temporary folder ${folder} deleted.`);
}
} catch (error) {
console.error('Error deleting temporary folders:', error);
throw error;
}
}

View File

@ -39,11 +39,32 @@ export async function downloadCRSub(
return path return path
} }
export async function downloadADNSub( export async function downloadADNSub(link: string, dir: string, secret: string) {
link: string, var templateASS = `[Script Info]
dir: string, ; Script generated by Aegisub 3.2.2
secret: string ; http://www.aegisub.org/
) { Title: Deutsch
Original Script: crd [https://github.com/stratuma/]
Original Translation:
Original Editing:
Original Timing:
Synch Point:
Script Updated By:
Update Details:
ScriptType: v4.00+
PlayResX: 1920
PlayResY: 1080
Timer: 0.0000
WrapStyle: 0
[Aegisub Project Garbage]
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Arial,56,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,4,0,2,0,0,20,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n`
const path = `${dir}/de-DE.ass` const path = `${dir}/de-DE.ass`
const stream = fs.createWriteStream(path) const stream = fs.createWriteStream(path)
@ -57,6 +78,41 @@ export async function downloadADNSub(
const subs = await ADNparseSub(rawSubs, secret) const subs = await ADNparseSub(rawSubs, secret)
const parsedSubs: {
vde: Array<{
startTime: number
endTime: number
positionAligh: string
lineAlign: string
text: string
}>,
vostde: Array<{
startTime: number
endTime: number
positionAligh: string
lineAlign: string
text: string
}>,
} = await JSON.parse(subs)
if (parsedSubs.vde) {
for (const s of parsedSubs.vde) {
const convertedStart = convertToTimeFormat(s.startTime)
const convertedEnd = convertToTimeFormat(s.endTime)
templateASS = templateASS + `Dialogue: 0,${convertedStart},${convertedEnd},Default,,0,0,0,,${s.text.replace('\n', '\\N').replace('<i>', '{\\i1}').replace('</i>', '{\\i0}')}\n`
}
}
if (parsedSubs.vostde) {
for (const s of parsedSubs.vostde) {
const convertedStart = convertToTimeFormat(s.startTime)
const convertedEnd = convertToTimeFormat(s.endTime)
templateASS = templateASS + `Dialogue: 0,${convertedStart},${convertedEnd},Default,,0,0,0,,${s.text.replace('\n', '\\N').replace('<i>', '{\\i1}').replace('</i>', '{\\i0}')}\n`
}
}
// Disabling Changing ASS because still broken in vlc // Disabling Changing ASS because still broken in vlc
// parsedASS.info.PlayResX = "1920"; // parsedASS.info.PlayResX = "1920";
@ -68,7 +124,7 @@ export async function downloadADNSub(
// const fixed = stringify(parsedASS) // const fixed = stringify(parsedASS)
const readableStream = Readable.from([subs]) const readableStream = Readable.from([templateASS])
await finished(readableStream.pipe(stream)) await finished(readableStream.pipe(stream))
console.log(`Sub downloaded`) console.log(`Sub downloaded`)
@ -76,6 +132,26 @@ export async function downloadADNSub(
return path return path
} }
function convertToTimeFormat(time: number) {
var seconds: number | string = Math.floor(time);
var milliseconds = Math.round((time - seconds) * 1000);
var hours: number | string = Math.floor(seconds / 3600);
var minutes: number | string = Math.floor((seconds % 3600) / 60);
seconds = seconds % 60;
hours = String(hours).padStart(2, '0');
minutes = String(minutes).padStart(2, '0');
seconds = String(seconds).padStart(2, '0');
milliseconds = Math.round(milliseconds / 10);
var formattedMilliseconds = milliseconds < 10 ? '0' + milliseconds : milliseconds;
var formattedTime = hours + ':' + minutes + ':' + seconds + '.' + formattedMilliseconds;
return formattedTime;
}
export async function ADNparseSub(raw: string, secret: string) { export async function ADNparseSub(raw: string, secret: string) {
var key = secret + '7fac1178830cfe0c' var key = secret + '7fac1178830cfe0c'