Download Archve
This commit is contained in:
parent
9586b412a1
commit
8f6dcbed0f
7 changed files with 266 additions and 51 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -19,4 +19,5 @@ token.yml
|
|||
lib
|
||||
test.*
|
||||
updates.json
|
||||
cr_token.yml
|
||||
cr_token.yml
|
||||
archive.json
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
videoLayer: 7
|
||||
videoLayer: 1
|
||||
nServer: 1
|
||||
mp4mux: false
|
||||
noCleanUp: false
|
||||
noCleanUp: false
|
||||
partsize: 50
|
||||
timeout: 30000
|
||||
25
crunchy.ts
25
crunchy.ts
|
|
@ -55,6 +55,7 @@ import { CrunchyEpMeta, CrunchyEpMetaMultiDub, ParseItem, SeriesSearch, SeriesSe
|
|||
import { ObjectInfo } from './@types/objectInfo';
|
||||
import parseFileName, { Variable } from './modules/module.filename';
|
||||
import { PlaybackData } from './@types/playbackData';
|
||||
import { downloaded } from './modules/module.downloadArchive';
|
||||
const req = new reqModule.Req(domain, argv);
|
||||
|
||||
// select
|
||||
|
|
@ -84,7 +85,7 @@ export default (async () => {
|
|||
else if(argv.series && argv.series.match(/^[0-9A-Z]{9}$/)){
|
||||
await refreshToken();
|
||||
await getSeriesById();
|
||||
await downloadFromSeriesID();
|
||||
return await downloadFromSeriesID();
|
||||
}
|
||||
else if(argv['movie-listing'] && argv['movie-listing'].match(/^[0-9A-Z]{9}$/)){
|
||||
await refreshToken();
|
||||
|
|
@ -96,7 +97,7 @@ export default (async () => {
|
|||
console.log('[INFO] One show can only be downloaded with one dub. Use --srz instead.');
|
||||
}
|
||||
argv.dubLang = argv.dubLang[0];
|
||||
await getSeasonById();
|
||||
return await getSeasonById();
|
||||
}
|
||||
else if(argv.e){
|
||||
await refreshToken();
|
||||
|
|
@ -719,10 +720,18 @@ async function getSeasonById(){
|
|||
}
|
||||
|
||||
console.log();
|
||||
let ok = true;
|
||||
for(const media of selectedMedia){
|
||||
await getMedia(media);
|
||||
if (await getMedia(media) !== true) {
|
||||
ok = false;
|
||||
} else {
|
||||
downloaded({
|
||||
service: 'crunchy',
|
||||
type: 's'
|
||||
}, argv.s as string, [media.episodeNumber]);
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
async function getObjectById(returnData?: boolean){
|
||||
|
|
@ -1164,7 +1173,8 @@ async function getMedia(mMeta: CrunchyEpMeta){
|
|||
else{
|
||||
console.log();
|
||||
}
|
||||
|
||||
|
||||
return !dlFailed;
|
||||
}
|
||||
|
||||
async function muxStreams(options: MergerOptions){
|
||||
|
|
@ -1247,6 +1257,10 @@ const downloadFromSeriesID = async () => {
|
|||
const res = await getMediaList(item);
|
||||
if (!res)
|
||||
return;
|
||||
downloaded({
|
||||
service: 'crunchy',
|
||||
type: 'srz'
|
||||
}, argv.series as string, [item.episodeNumber]);
|
||||
muxStreams({
|
||||
onlyAudio: [],
|
||||
onlyVid: [],
|
||||
|
|
@ -1262,6 +1276,7 @@ const downloadFromSeriesID = async () => {
|
|||
fonts: Merger.makeFontsList(cfg.dir.fonts, appstore.sxList)
|
||||
});
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const itemSelectMultiDub = (eps: Record<string, {
|
||||
|
|
|
|||
60
funi.ts
60
funi.ts
|
|
@ -26,7 +26,6 @@ const cfg = yamlCfg.loadCfg();
|
|||
const token = yamlCfg.loadFuniToken();
|
||||
// cli
|
||||
const argv = appYargs.appArgv(cfg.cli);
|
||||
|
||||
// Import modules after argv has been exported
|
||||
import getData from './modules/module.getdata.js';
|
||||
import merger, { SubtitleInput } from './modules/module.merger';
|
||||
|
|
@ -36,6 +35,7 @@ import { Subtitle } from './@types/subtitleObject';
|
|||
import { StreamData } from './@types/streamData';
|
||||
import { DownloadedFile } from './@types/downloadedFile';
|
||||
import parseFileName, { Variable } from './modules/module.filename';
|
||||
import { downloaded } from './modules/module.downloadArchive';
|
||||
|
||||
// check page
|
||||
argv.p = 1;
|
||||
|
|
@ -47,9 +47,9 @@ let title = '',
|
|||
fnOutput: string[] = [],
|
||||
season = 0,
|
||||
tsDlPath: {
|
||||
path: string,
|
||||
lang: string,
|
||||
}[] = [],
|
||||
path: string,
|
||||
lang: string
|
||||
}[] = [],
|
||||
stDlPath: Subtitle[] = [];
|
||||
|
||||
// main
|
||||
|
|
@ -71,7 +71,7 @@ export default (async () => {
|
|||
searchShow();
|
||||
}
|
||||
else if(argv.s && !isNaN(parseInt(argv.s)) && parseInt(argv.s) > 0){
|
||||
getShow();
|
||||
return getShow();
|
||||
}
|
||||
else{
|
||||
appYargs.showHelp();
|
||||
|
|
@ -134,7 +134,7 @@ async function searchShow(){
|
|||
|
||||
// get show
|
||||
async function getShow(){
|
||||
// show main data
|
||||
let ok = true;
|
||||
const showData = await getData({
|
||||
baseUrl: api_host,
|
||||
url: `/source/catalog/title/${argv.s}`,
|
||||
|
|
@ -213,7 +213,8 @@ async function getShow(){
|
|||
|
||||
const fnSlug: {
|
||||
title: string,
|
||||
episode: string
|
||||
episode: string,
|
||||
episodeID: string
|
||||
}[] = []; let is_selected = false;
|
||||
|
||||
const eps = epsDataArr;
|
||||
|
|
@ -233,7 +234,7 @@ async function getShow(){
|
|||
// select
|
||||
is_selected = false;
|
||||
if (argv.all || epSelList.isSelected(epStrId)) {
|
||||
fnSlug.push({title:eps[e].item.titleSlug,episode:eps[e].item.episodeSlug});
|
||||
fnSlug.push({title:eps[e].item.titleSlug,episode:eps[e].item.episodeSlug, episodeID:epStrId});
|
||||
epSelEpsTxt.push(epStrId);
|
||||
is_selected = true;
|
||||
}
|
||||
|
|
@ -265,15 +266,17 @@ async function getShow(){
|
|||
else{
|
||||
console.log('[INFO] Selected Episodes: %s\n',epSelEpsTxt.join(', '));
|
||||
for(let fnEp=0;fnEp<fnSlug.length;fnEp++){
|
||||
await getEpisode(fnSlug[fnEp]);
|
||||
if (await getEpisode(fnSlug[fnEp]) !== true)
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
async function getEpisode(fnSlug: {
|
||||
title: string,
|
||||
episode: string
|
||||
title: string,
|
||||
episode: string,
|
||||
episodeID: string
|
||||
}) {
|
||||
const episodeData = await getData({
|
||||
baseUrl: api_host,
|
||||
|
|
@ -321,7 +324,7 @@ async function getEpisode(fnSlug: {
|
|||
language: m.language,
|
||||
version: m.version,
|
||||
type: m.experienceType,
|
||||
subtitles: getSubsUrl(m.mediaChildren),
|
||||
subtitles: getSubsUrl(m.mediaChildren)
|
||||
};
|
||||
}
|
||||
else{
|
||||
|
|
@ -352,6 +355,7 @@ async function getEpisode(fnSlug: {
|
|||
for (const curDub of (argv.dub as appYargs.possibleDubs)) {
|
||||
if(dub_type == dubType[curDub] && selUncut){
|
||||
streamIds.push({
|
||||
|
||||
id: m.id,
|
||||
lang: merger.getLanguageCode(curDub, curDub.slice(0, -2))
|
||||
});
|
||||
|
|
@ -413,7 +417,7 @@ async function getEpisode(fnSlug: {
|
|||
return;
|
||||
}
|
||||
else{
|
||||
await downloadStreams();
|
||||
return await downloadStreams(fnSlug.episodeID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -457,7 +461,7 @@ function getSubsUrl(m: MediaChild[]) : Subtitle[] {
|
|||
return found;
|
||||
}
|
||||
|
||||
async function downloadStreams(){
|
||||
async function downloadStreams(epsiodeID: string){
|
||||
|
||||
// req playlist
|
||||
|
||||
|
|
@ -713,7 +717,7 @@ async function downloadStreams(){
|
|||
if (addSubs)
|
||||
console.log('[INFO] Subtitles downloaded!');
|
||||
}
|
||||
|
||||
|
||||
if((puraudio.length < 1 && audioAndVideo.length < 1) || (purvideo.length < 1 && audioAndVideo.length < 1)){
|
||||
console.log('\n[INFO] Unable to locate a video AND audio file\n');
|
||||
return;
|
||||
|
|
@ -721,6 +725,10 @@ async function downloadStreams(){
|
|||
|
||||
if(argv.skipmux){
|
||||
console.log('[INFO] Skipping muxing...');
|
||||
downloaded({
|
||||
service: 'funi',
|
||||
type: 's'
|
||||
}, argv.s as string, [epsiodeID]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -759,14 +767,28 @@ async function downloadStreams(){
|
|||
}
|
||||
else{
|
||||
console.log('\n[INFO] Done!\n');
|
||||
return;
|
||||
downloaded({
|
||||
service: 'funi',
|
||||
type: 's'
|
||||
}, argv.s as string, [epsiodeID]);
|
||||
return true;
|
||||
}
|
||||
if (argv.nocleanup) {
|
||||
downloaded({
|
||||
service: 'funi',
|
||||
type: 's'
|
||||
}, argv.s as string, [epsiodeID]);
|
||||
return true;
|
||||
}
|
||||
if (argv.nocleanup)
|
||||
return;
|
||||
|
||||
audioAndVideo.concat(puraudio).concat(purvideo).forEach(a => fs.unlinkSync(a.path));
|
||||
stDlPath.forEach(subObject => subObject.file && fs.unlinkSync(subObject.file));
|
||||
console.log('\n[INFO] Done!\n');
|
||||
downloaded({
|
||||
service: 'funi',
|
||||
type: 's'
|
||||
}, argv.s as string, [epsiodeID]);
|
||||
return true;
|
||||
}
|
||||
|
||||
async function downloadFile(filename: string, chunkList: {
|
||||
|
|
|
|||
47
index.ts
47
index.ts
|
|
@ -1,5 +1,6 @@
|
|||
import { appArgv } from './modules/module.app-args';
|
||||
import { appArgv, overrideArguments } from './modules/module.app-args';
|
||||
import * as yamlCfg from './modules/module.cfg-loader';
|
||||
import { makeCommand, downloaded, addToArchive } from './modules/module.downloadArchive';
|
||||
|
||||
import update from './modules/module.updater';
|
||||
|
||||
|
|
@ -14,11 +15,43 @@ import update from './modules/module.updater';
|
|||
console.log('[ERROR] --all and --but exclude each other!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (argv.service === 'funi') {
|
||||
(await import('./funi')).default();
|
||||
} else if (argv.service === 'crunchy') {
|
||||
(await import('./crunchy')).default();
|
||||
}
|
||||
|
||||
if (argv.addArchive) {
|
||||
if (argv.service === 'funi') {
|
||||
if (argv.s === undefined)
|
||||
return console.log('[ERROR] `-s` not found');
|
||||
addToArchive({
|
||||
service: 'funi',
|
||||
type: 's'
|
||||
}, argv.s);
|
||||
console.log('[INFO] Added %s to the downloadArchive list', argv.s);
|
||||
} else if (argv.service === 'crunchy') {
|
||||
if (argv.s === undefined && argv.series === undefined)
|
||||
return console.log('[ERROR] `-s` or `--srz` not found');
|
||||
if (argv.s && argv.series)
|
||||
return console.log('[ERROR] Both `-s` and `--srz` found');
|
||||
addToArchive({
|
||||
service: 'crunchy',
|
||||
type: argv.s === undefined ? 'srz' : 's'
|
||||
}, (argv.s === undefined ? argv.series : argv.s) as string);
|
||||
console.log('[INFO] Added %s to the downloadArchive list', (argv.s === undefined ? argv.series : argv.s));
|
||||
}
|
||||
} else if (argv.downloadArchive) {
|
||||
const ids = makeCommand(argv.service);
|
||||
for (const id of ids) {
|
||||
overrideArguments(cfg.cli, id);
|
||||
/* Reimport module to override appArgv */
|
||||
Object.keys(require.cache).forEach(key => {
|
||||
if (key.endsWith('crunchy.js') || key.endsWith('funi.js'))
|
||||
delete require.cache[key]
|
||||
})
|
||||
await (argv.service === 'funi' ? await import('./funi') : await import('./crunchy')).default();
|
||||
}
|
||||
} else {
|
||||
if (argv.service === 'funi') {
|
||||
(await import('./funi')).default();
|
||||
} else if (argv.service === 'crunchy') {
|
||||
(await import('./crunchy')).default();
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
|
@ -27,27 +27,58 @@ const availableFilenameVars: AvailableFilenameVars[] = [
|
|||
];
|
||||
|
||||
export type possibleDubs = (
|
||||
'enUS' | 'esLA' | 'ptBR' | 'zhMN' | 'jaJP'
|
||||
)[];
|
||||
'enUS' | 'esLA' | 'ptBR' | 'zhMN' | 'jaJP'
|
||||
)[];
|
||||
export type possibleSubs = (
|
||||
'enUS' | 'esLA' | 'ptBR'
|
||||
)[];
|
||||
)[];
|
||||
const subLang: possibleSubs = ['enUS', 'esLA', 'ptBR'];
|
||||
const dubLang: possibleDubs = ['enUS', 'esLA', 'ptBR', 'zhMN', 'jaJP'];
|
||||
let argvC: { [x: string]: unknown; but: boolean, auth: boolean | undefined; dlFonts: boolean | undefined; search: string | undefined; 'search-type': string; page: number | undefined; 'search-locale': string; new: boolean | undefined; 'movie-listing': string | undefined; series: string | undefined; s: string | undefined; e: string | undefined; q: number; x: number; kstream: number; partsize: number; hslang: string; subLang: string[]; dlsubs: string | string[]; novids: boolean | undefined; noaudio: boolean | undefined; nosubs: boolean | undefined; dub: possibleDubs; dubLang: string; all: boolean; fontSize: number; allSubs: boolean; allDubs: boolean; timeout: number; simul: boolean; mp4: boolean; skipmux: boolean | undefined; fileName: string; numbers: number; nosess: string; debug: boolean | undefined; nocleanup: boolean; help: boolean | undefined; service: 'funi' | 'crunchy'; update: boolean; fontName: string | undefined; _: (string | number)[]; $0: string; };
|
||||
let argvC: { [x: string]: unknown; downloadArchive: boolean, addArchive: boolean, but: boolean, auth: boolean | undefined; dlFonts: boolean | undefined; search: string | undefined; 'search-type': string; page: number | undefined; 'search-locale': string; new: boolean | undefined; 'movie-listing': string | undefined; series: string | undefined; s: string | undefined; e: string | undefined; q: number; x: number; kstream: number; partsize: number; hslang: string; subLang: string[]; dlsubs: string | string[]; novids: boolean | undefined; noaudio: boolean | undefined; nosubs: boolean | undefined; dub: possibleDubs; dubLang: string; all: boolean; fontSize: number; allSubs: boolean; allDubs: boolean; timeout: number; simul: boolean; mp4: boolean; skipmux: boolean | undefined; fileName: string; numbers: number; nosess: string; debug: boolean | undefined; nocleanup: boolean; help: boolean | undefined; service: 'funi' | 'crunchy'; update: boolean; fontName: string | undefined; _: (string | number)[]; $0: string; };
|
||||
|
||||
export type ArgvType = typeof argvC;
|
||||
|
||||
const appArgv = (cfg: {
|
||||
[key: string]: unknown
|
||||
}) => {
|
||||
if (argvC)
|
||||
return argvC;
|
||||
const argv = getArgv(cfg)
|
||||
.parseSync();
|
||||
argvC = argv;
|
||||
return argv;
|
||||
};
|
||||
|
||||
|
||||
const overrideArguments = (cfg: { [key:string]: unknown }, override: Partial<typeof argvC>) => {
|
||||
const argv = getArgv(cfg).middleware((ar) => {
|
||||
for (const key of Object.keys(override)) {
|
||||
ar[key] = override[key];
|
||||
}
|
||||
return ar;
|
||||
}).parseSync();
|
||||
argvC = argv;
|
||||
};
|
||||
|
||||
const showHelp = yargs.showHelp;
|
||||
|
||||
export {
|
||||
appArgv,
|
||||
showHelp,
|
||||
availableFilenameVars,
|
||||
subLang,
|
||||
dubLang,
|
||||
overrideArguments
|
||||
};
|
||||
|
||||
const getArgv = (cfg: { [key:string]: unknown }) => {
|
||||
const parseDefault = <T = unknown>(key: string, _default: T) : T=> {
|
||||
if (Object.prototype.hasOwnProperty.call(cfg, key)) {
|
||||
return cfg[key] as T;
|
||||
} else
|
||||
return _default;
|
||||
};
|
||||
|
||||
|
||||
const argv = yargs.parserConfiguration({
|
||||
'duplicate-arguments-array': false,
|
||||
'camel-case-expansion': false,
|
||||
|
|
@ -305,17 +336,19 @@ const appArgv = (cfg: {
|
|||
type: 'boolean',
|
||||
default: false
|
||||
})
|
||||
.parseSync();
|
||||
argvC = argv;
|
||||
.option('downloadArchive', {
|
||||
group: groups.dl,
|
||||
desc: 'Used to download all archived shows',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
})
|
||||
.option('addArchive', {
|
||||
group: groups.dl,
|
||||
desc: 'Used to add the `-s` and `--srz` to downloadArchive',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
});
|
||||
return argv;
|
||||
};
|
||||
|
||||
const showHelp = yargs.showHelp;
|
||||
|
||||
export {
|
||||
appArgv,
|
||||
showHelp,
|
||||
availableFilenameVars,
|
||||
subLang,
|
||||
dubLang
|
||||
};
|
||||
|
||||
|
||||
109
modules/module.downloadArchive.ts
Normal file
109
modules/module.downloadArchive.ts
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { ArgvType } from './module.app-args';
|
||||
|
||||
const workingDir = (process as NodeJS.Process & {
|
||||
pkg?: unknown
|
||||
}).pkg ? path.dirname(process.execPath) : path.join(__dirname, '/..');
|
||||
export const archiveFile = path.join(workingDir, 'config', 'archive.json');
|
||||
|
||||
export type ItemType = {
|
||||
id: string,
|
||||
already: string[]
|
||||
}[]
|
||||
|
||||
export type DataType = {
|
||||
funi: {
|
||||
s: ItemType
|
||||
},
|
||||
crunchy: {
|
||||
srz: ItemType,
|
||||
s: ItemType
|
||||
}
|
||||
}
|
||||
|
||||
const addToArchive = (kind: {
|
||||
service: 'funi',
|
||||
type: 's'
|
||||
} | {
|
||||
service: 'crunchy',
|
||||
type: 's'|'srz'
|
||||
}, ID: string) => {
|
||||
const data = loadData();
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(data, kind.service)) {
|
||||
const items = kind.service === 'crunchy' ? data[kind.service][kind.type] : data[kind.service][kind.type];
|
||||
items.push({
|
||||
id: ID,
|
||||
already: []
|
||||
});
|
||||
} else {
|
||||
if (kind.service === 'funi') {
|
||||
data['funi'] = {
|
||||
s: [
|
||||
{
|
||||
id: ID,
|
||||
already: []
|
||||
}
|
||||
]
|
||||
};
|
||||
} else {
|
||||
data['crunchy'] = {
|
||||
s: ([] as ItemType).concat(kind.type === 's' ? {
|
||||
id: ID,
|
||||
already: [] as string[]
|
||||
} : []),
|
||||
srz: ([] as ItemType).concat(kind.type === 'srz' ? {
|
||||
id: ID,
|
||||
already: [] as string[]
|
||||
} : []),
|
||||
};
|
||||
}
|
||||
}
|
||||
fs.writeFileSync(archiveFile, JSON.stringify(data, null, 4));
|
||||
};
|
||||
|
||||
const downloaded = (kind: {
|
||||
service: 'funi',
|
||||
type: 's'
|
||||
} | {
|
||||
service: 'crunchy',
|
||||
type: 's'|'srz'
|
||||
}, ID: string, episode: string[]) => {
|
||||
const data = loadData();
|
||||
if (!Object.prototype.hasOwnProperty.call(data, kind.service))
|
||||
addToArchive(kind, ID);
|
||||
(kind.service == 'crunchy' ? data[kind.service][kind.type] : data[kind.service][kind.type]).find(a => a.id === ID)?.already.push(...episode);
|
||||
fs.writeFileSync(archiveFile, JSON.stringify(data, null, 4));
|
||||
};
|
||||
|
||||
const makeCommand = (service: 'funi'|'crunchy') : Partial<ArgvType>[] => {
|
||||
const data = loadData();
|
||||
const ret: Partial<ArgvType>[] = [];
|
||||
const kind = data[service];
|
||||
for (const type of Object.keys(kind)) {
|
||||
const item = kind[type as 's']; // 'srz' is also possible but will be ignored for the compiler
|
||||
item.forEach(i => ret.push({
|
||||
but: true,
|
||||
all: false,
|
||||
service,
|
||||
e: i.already.join(','),
|
||||
...(type === 's' ? {
|
||||
s: i.id,
|
||||
series: undefined
|
||||
} : {
|
||||
series: i.id,
|
||||
s: undefined
|
||||
})
|
||||
}));
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
const loadData = () : DataType => {
|
||||
if (fs.existsSync(archiveFile))
|
||||
return JSON.parse(fs.readFileSync(archiveFile).toString()) as DataType;
|
||||
return {} as DataType;
|
||||
};
|
||||
|
||||
export { addToArchive, downloaded, makeCommand };
|
||||
Loading…
Reference in a new issue