mirror of
https://github.com/anidl/multi-downloader-nx.git
synced 2026-04-21 16:31:55 +00:00
50% crunchy-beta.ts
This commit is contained in:
parent
4a83046116
commit
58697e8b0f
16 changed files with 737 additions and 381 deletions
122
@types/crunchyEpisodeList.d.ts
vendored
Normal file
122
@types/crunchyEpisodeList.d.ts
vendored
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
export interface CrunchyEpisodeList {
|
||||||
|
__class__: string;
|
||||||
|
__href__: string;
|
||||||
|
__resource_key__: string;
|
||||||
|
__links__: Actions;
|
||||||
|
__actions__: Actions;
|
||||||
|
total: number;
|
||||||
|
items: Item[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Actions {
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Item {
|
||||||
|
__class__: Class;
|
||||||
|
__href__: string;
|
||||||
|
__resource_key__: string;
|
||||||
|
__links__: Links;
|
||||||
|
__actions__: Actions;
|
||||||
|
id: string;
|
||||||
|
channel_id: ChannelID;
|
||||||
|
series_id: string;
|
||||||
|
series_title: string;
|
||||||
|
series_slug_title: string;
|
||||||
|
season_id: string;
|
||||||
|
season_title: string;
|
||||||
|
season_slug_title: string;
|
||||||
|
season_number: number;
|
||||||
|
episode: string;
|
||||||
|
episode_number: number | null;
|
||||||
|
sequence_number: number;
|
||||||
|
production_episode_id: string;
|
||||||
|
title: string;
|
||||||
|
slug_title: string;
|
||||||
|
description: string;
|
||||||
|
next_episode_id?: string;
|
||||||
|
next_episode_title?: string;
|
||||||
|
hd_flag: boolean;
|
||||||
|
is_mature: boolean;
|
||||||
|
mature_blocked: boolean;
|
||||||
|
episode_air_date: string;
|
||||||
|
is_subbed: boolean;
|
||||||
|
is_dubbed: boolean;
|
||||||
|
is_clip: boolean;
|
||||||
|
seo_title: string;
|
||||||
|
seo_description: string;
|
||||||
|
season_tags: string[];
|
||||||
|
available_offline: boolean;
|
||||||
|
media_type: Class;
|
||||||
|
slug: string;
|
||||||
|
images: Images;
|
||||||
|
duration_ms: number;
|
||||||
|
ad_breaks: AdBreak[];
|
||||||
|
is_premium_only: boolean;
|
||||||
|
listing_id: string;
|
||||||
|
subtitle_locales: SubtitleLocale[];
|
||||||
|
playback?: string;
|
||||||
|
availability_notes: string;
|
||||||
|
available_date?: string;
|
||||||
|
hide_season_title?: boolean;
|
||||||
|
hide_season_number?: boolean;
|
||||||
|
isSelected?: boolean;
|
||||||
|
seq_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Class {
|
||||||
|
Episode = "episode",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Links {
|
||||||
|
ads: Ads;
|
||||||
|
"episode/channel": Ads;
|
||||||
|
"episode/next_episode"?: Ads;
|
||||||
|
"episode/season": Ads;
|
||||||
|
"episode/series": Ads;
|
||||||
|
streams?: Ads;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Ads {
|
||||||
|
href: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AdBreak {
|
||||||
|
type: AdBreakType;
|
||||||
|
offset_ms: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AdBreakType {
|
||||||
|
Midroll = "midroll",
|
||||||
|
Preroll = "preroll",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ChannelID {
|
||||||
|
Crunchyroll = "crunchyroll",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Images {
|
||||||
|
thumbnail: Array<Thumbnail[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Thumbnail {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
type: ThumbnailType;
|
||||||
|
source: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ThumbnailType {
|
||||||
|
Thumbnail = "thumbnail",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SubtitleLocale {
|
||||||
|
ArSA = "ar-SA",
|
||||||
|
DeDE = "de-DE",
|
||||||
|
EnUS = "en-US",
|
||||||
|
Es419 = "es-419",
|
||||||
|
EsES = "es-ES",
|
||||||
|
FrFR = "fr-FR",
|
||||||
|
ItIT = "it-IT",
|
||||||
|
PtBR = "pt-BR",
|
||||||
|
RuRU = "ru-RU",
|
||||||
|
}
|
||||||
175
@types/crunchySearch.d.ts
vendored
Normal file
175
@types/crunchySearch.d.ts
vendored
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
// Generated by https://quicktype.io
|
||||||
|
|
||||||
|
export interface CrunchySearch {
|
||||||
|
__class__: string;
|
||||||
|
__href__: string;
|
||||||
|
__resource_key__: string;
|
||||||
|
__links__: CrunchySearchLinks;
|
||||||
|
__actions__: Actions;
|
||||||
|
total: number;
|
||||||
|
items: CrunchySearchItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Actions {
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CrunchySearchLinks {
|
||||||
|
continuation?: Continuation;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Continuation {
|
||||||
|
href: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CrunchySearchItem {
|
||||||
|
__class__: string;
|
||||||
|
__href__: string;
|
||||||
|
__resource_key__: string;
|
||||||
|
__links__: CrunchySearchLinks;
|
||||||
|
__actions__: Actions;
|
||||||
|
type: string;
|
||||||
|
total: number;
|
||||||
|
items: ItemItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ItemItem {
|
||||||
|
__actions__: Actions;
|
||||||
|
__class__: Class;
|
||||||
|
__href__: string;
|
||||||
|
__links__: PurpleLinks;
|
||||||
|
channel_id: ChannelID;
|
||||||
|
description: string;
|
||||||
|
external_id: string;
|
||||||
|
id: string;
|
||||||
|
images: Images;
|
||||||
|
linked_resource_key: string;
|
||||||
|
new: boolean;
|
||||||
|
new_content: boolean;
|
||||||
|
promo_description: string;
|
||||||
|
promo_title: string;
|
||||||
|
search_metadata: SearchMetadata;
|
||||||
|
series_metadata?: SeriesMetadata;
|
||||||
|
slug: string;
|
||||||
|
slug_title: string;
|
||||||
|
title: string;
|
||||||
|
type: ItemType;
|
||||||
|
episode_metadata?: EpisodeMetadata;
|
||||||
|
playback?: string;
|
||||||
|
isSelected?: boolean;
|
||||||
|
season_number?: string;
|
||||||
|
is_premium_only?: boolean;
|
||||||
|
hide_metadata?: boolean;
|
||||||
|
seq_id?: string;
|
||||||
|
f_num?: string;
|
||||||
|
s_num?: string;
|
||||||
|
ep_num?: string;
|
||||||
|
last_public?: string;
|
||||||
|
subtitle_locales?: string[];
|
||||||
|
availability_notes?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Class {
|
||||||
|
Panel = "panel",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PurpleLinks {
|
||||||
|
resource: Continuation;
|
||||||
|
"resource/channel": Continuation;
|
||||||
|
"episode/season"?: Continuation;
|
||||||
|
"episode/series"?: Continuation;
|
||||||
|
streams?: Continuation;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ChannelID {
|
||||||
|
Crunchyroll = "crunchyroll",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EpisodeMetadata {
|
||||||
|
ad_breaks: AdBreak[];
|
||||||
|
availability_notes: string;
|
||||||
|
available_offline: boolean;
|
||||||
|
duration_ms: number;
|
||||||
|
episode: string;
|
||||||
|
episode_air_date: string;
|
||||||
|
episode_number: number;
|
||||||
|
is_clip: boolean;
|
||||||
|
is_dubbed: boolean;
|
||||||
|
is_mature: boolean;
|
||||||
|
is_premium_only: boolean;
|
||||||
|
is_subbed: boolean;
|
||||||
|
mature_blocked: boolean;
|
||||||
|
maturity_ratings: string[];
|
||||||
|
season_id: string;
|
||||||
|
season_number: number;
|
||||||
|
season_slug_title: string;
|
||||||
|
season_title: string;
|
||||||
|
sequence_number: number;
|
||||||
|
series_id: string;
|
||||||
|
series_slug_title: string;
|
||||||
|
series_title: string;
|
||||||
|
subtitle_locales: string[];
|
||||||
|
tenant_categories?: TenantCategory[];
|
||||||
|
available_date?: string;
|
||||||
|
free_available_date?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AdBreak {
|
||||||
|
offset_ms: number;
|
||||||
|
type: AdBreakType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AdBreakType {
|
||||||
|
Midroll = "midroll",
|
||||||
|
Preroll = "preroll",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TenantCategory {
|
||||||
|
Action = "Action",
|
||||||
|
Drama = "Drama",
|
||||||
|
SciFi = "Sci-Fi",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Images {
|
||||||
|
poster_tall?: Array<PosterTall[]>;
|
||||||
|
poster_wide?: Array<PosterTall[]>;
|
||||||
|
thumbnail?: Array<PosterTall[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PosterTall {
|
||||||
|
height: number;
|
||||||
|
source: string;
|
||||||
|
type: PosterTallType;
|
||||||
|
width: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum PosterTallType {
|
||||||
|
PosterTall = "poster_tall",
|
||||||
|
PosterWide = "poster_wide",
|
||||||
|
Thumbnail = "thumbnail",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchMetadata {
|
||||||
|
score: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SeriesMetadata {
|
||||||
|
availability_notes: string;
|
||||||
|
episode_count: number;
|
||||||
|
extended_description: string;
|
||||||
|
is_dubbed: boolean;
|
||||||
|
is_mature: boolean;
|
||||||
|
is_simulcast: boolean;
|
||||||
|
is_subbed: boolean;
|
||||||
|
mature_blocked: boolean;
|
||||||
|
maturity_ratings: string[];
|
||||||
|
season_count: number;
|
||||||
|
tenant_categories: TenantCategory[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ItemType {
|
||||||
|
Episode = "episode",
|
||||||
|
Series = "series",
|
||||||
|
Season = 'season',
|
||||||
|
MovieListing = 'movie_listing',
|
||||||
|
Movie = 'Movie'
|
||||||
|
}
|
||||||
26
@types/crunchyTypes.ts
Normal file
26
@types/crunchyTypes.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
export type CrunchyEpMeta = {
|
||||||
|
mediaId: string,
|
||||||
|
seasonTitle: string,
|
||||||
|
episodeNumber: string,
|
||||||
|
episodeTitle: string,
|
||||||
|
playback?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ParseItem = {
|
||||||
|
isSelected?: boolean,
|
||||||
|
type?: string,
|
||||||
|
id: string,
|
||||||
|
title: string,
|
||||||
|
playback?: string,
|
||||||
|
season_number?: number|string,
|
||||||
|
is_premium_only?: boolean,
|
||||||
|
hide_metadata?: boolean,
|
||||||
|
seq_id?: string,
|
||||||
|
f_num?: string,
|
||||||
|
s_num?: string
|
||||||
|
external_id?: string,
|
||||||
|
ep_num?: string
|
||||||
|
last_public?: string,
|
||||||
|
subtitle_locales?: string[],
|
||||||
|
availability_notes?: string
|
||||||
|
}
|
||||||
12
@types/sei-helper.d.ts
vendored
12
@types/sei-helper.d.ts
vendored
|
|
@ -1,5 +1,15 @@
|
||||||
declare module 'sei-helper' {
|
declare module 'sei-helper' {
|
||||||
export async function question(qStr: string): string;
|
export async function question(qStr: string): Promise<string>;
|
||||||
export function cleanupFilename(str: string): string;
|
export function cleanupFilename(str: string): string;
|
||||||
export function exec(str: string, str1: string, str2: string);
|
export function exec(str: string, str1: string, str2: string);
|
||||||
|
export const cookie: {
|
||||||
|
parse: (data: Record<string, string>) => Record<string, {
|
||||||
|
value: string;
|
||||||
|
expires: Date;
|
||||||
|
path: string;
|
||||||
|
domain: string;
|
||||||
|
secure: boolean;
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
export function formatTime(time: number): string
|
||||||
}
|
}
|
||||||
|
|
@ -1,40 +1,44 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
// build-in
|
// build-in
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
const fs = require('fs-extra');
|
import fs from 'fs-extra';
|
||||||
|
|
||||||
// package program
|
// package program
|
||||||
const packageJson = require('./package.json');
|
import packageJson from './package.json';
|
||||||
console.log(`\n=== Crunchyroll Beta Downloader NX ${packageJson.version} ===\n`);
|
console.log(`\n=== Crunchyroll Beta Downloader NX ${packageJson.version} ===\n`);
|
||||||
|
|
||||||
// plugins
|
// plugins
|
||||||
const shlp = require('sei-helper');
|
import shlp from 'sei-helper';
|
||||||
const m3u8 = require('m3u8-parsed');
|
import m3u8 from 'm3u8-parsed';
|
||||||
const streamdl = require('hls-download');
|
import streamdl from 'hls-download';
|
||||||
|
|
||||||
// custom modules
|
// custom modules
|
||||||
const fontsData = require('./crunchy/modules/module.fontsData');
|
import * as fontsData from './modules/module.fontsData';
|
||||||
const langsData = require('./crunchy/modules/module.langsData');
|
import * as langsData from './modules/module.langsData';
|
||||||
const yamlCfg = require('./crunchy/modules/module.cfg-loader');
|
import * as yamlCfg from './modules/module.cfg-loader';
|
||||||
const yargs = require('./crunchy/modules/module.app-args');
|
import * as yargs from './modules/module.app-args';
|
||||||
const epsFilter = require('./crunchy/modules/module.eps-filter');
|
import epsFilter from './modules/module.eps-filter';
|
||||||
const appMux = require('./crunchy/modules/module.muxing');
|
import Merger from './modules/module.merger';
|
||||||
|
|
||||||
// new-cfg paths
|
// new-cfg paths
|
||||||
const cfg = yamlCfg.loadCfg();
|
const cfg = yamlCfg.loadCfg();
|
||||||
let token = yamlCfg.loadCRToken();
|
let token = yamlCfg.loadCRToken();
|
||||||
let cmsToken = {};
|
let cmsToken: {
|
||||||
|
cms?: Record<string, string>
|
||||||
|
} = {};
|
||||||
|
|
||||||
// args
|
// args
|
||||||
const appYargs = new yargs(cfg.cli, langsData, true);
|
const argv = yargs.appArgv(cfg.cli)
|
||||||
const argv = appYargs.appArgv();
|
|
||||||
argv.appstore = {};
|
argv.appstore = {};
|
||||||
|
|
||||||
// load req
|
// load req
|
||||||
const { domain, api } = require('./crunchy/modules/module.api-urls');
|
import { domain, api } from './modules/module.api-urls';
|
||||||
const reqModule = require('./crunchy/modules/module.req');
|
import * as reqModule from './modules/module.req';
|
||||||
const req = new reqModule.Req(domain, argv, true);
|
import { CrunchySearch, ItemItem, ItemType } from './@types/crunchySearch';
|
||||||
|
import { CrunchyEpisodeList } from './@types/crunchyEpisodeList';
|
||||||
|
import { CrunchyEpMeta, ParseItem } from './@types/crunchyTypes';
|
||||||
|
const req = new reqModule.Req(domain, argv);
|
||||||
|
|
||||||
// select
|
// select
|
||||||
(async () => {
|
(async () => {
|
||||||
|
|
@ -84,7 +88,7 @@ const req = new reqModule.Req(domain, argv, true);
|
||||||
async function getFonts(){
|
async function getFonts(){
|
||||||
console.log('[INFO] Downloading fonts...');
|
console.log('[INFO] Downloading fonts...');
|
||||||
for(const f of Object.keys(fontsData.fonts)){
|
for(const f of Object.keys(fontsData.fonts)){
|
||||||
const fontFile = fontsData.fonts[f];
|
const fontFile = fontsData.fonts[f as fontsData.AvailableFonts];
|
||||||
const fontLoc = path.join(cfg.dir.fonts, fontFile);
|
const fontLoc = path.join(cfg.dir.fonts, fontFile);
|
||||||
if(fs.existsSync(fontLoc) && fs.statSync(fontLoc).size != 0){
|
if(fs.existsSync(fontLoc) && fs.statSync(fontLoc).size != 0){
|
||||||
console.log(`[INFO] ${f} (${fontFile}) already downloaded!`);
|
console.log(`[INFO] ${f} (${fontFile}) already downloaded!`);
|
||||||
|
|
@ -99,8 +103,8 @@ async function getFonts(){
|
||||||
}
|
}
|
||||||
catch(e){}
|
catch(e){}
|
||||||
const fontUrl = fontsData.root + fontFile;
|
const fontUrl = fontsData.root + fontFile;
|
||||||
const getFont = await req.getData(fontUrl, { useProxy: true, binary: true });
|
const getFont = await req.getData<Buffer>(fontUrl, { binary: true });
|
||||||
if(getFont.ok){
|
if(getFont.ok && getFont.res){
|
||||||
fs.writeFileSync(fontLoc, getFont.res.body);
|
fs.writeFileSync(fontLoc, getFont.res.body);
|
||||||
console.log(`[INFO] Downloaded: ${f} (${fontFile})`);
|
console.log(`[INFO] Downloaded: ${f} (${fontFile})`);
|
||||||
}
|
}
|
||||||
|
|
@ -114,22 +118,21 @@ async function getFonts(){
|
||||||
|
|
||||||
// auth method
|
// auth method
|
||||||
async function doAuth(){
|
async function doAuth(){
|
||||||
const iLogin = argv.user ? argv.user : await shlp.question('[Q] LOGIN/EMAIL');
|
const iLogin = await shlp.question('[Q] LOGIN/EMAIL');
|
||||||
const iPsswd = argv.pass ? argv.pass : await shlp.question('[Q] PASSWORD ');
|
const iPsswd = await shlp.question('[Q] PASSWORD ');
|
||||||
const authData = new URLSearchParams({
|
const authData = new URLSearchParams({
|
||||||
'username': iLogin,
|
'username': iLogin,
|
||||||
'password': iPsswd,
|
'password': iPsswd,
|
||||||
'grant_type': 'password',
|
'grant_type': 'password',
|
||||||
'scope': 'offline_access'
|
'scope': 'offline_access'
|
||||||
}).toString();
|
}).toString();
|
||||||
const authReqOpts = {
|
const authReqOpts: reqModule.Params = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: api.beta_authHeaderMob,
|
headers: api.beta_authHeaderMob,
|
||||||
body: authData,
|
body: authData
|
||||||
useProxy: true
|
|
||||||
};
|
};
|
||||||
const authReq = await req.getData(api.beta_auth, authReqOpts);
|
const authReq = await req.getData(api.beta_auth, authReqOpts);
|
||||||
if(!authReq.ok){
|
if(!authReq.ok || !authReq.res){
|
||||||
console.log('[ERROR] Authentication failed!');
|
console.log('[ERROR] Authentication failed!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +155,7 @@ async function getProfile(){
|
||||||
useProxy: true
|
useProxy: true
|
||||||
};
|
};
|
||||||
const profileReq = await req.getData(api.beta_profile, profileReqOptions);
|
const profileReq = await req.getData(api.beta_profile, profileReqOptions);
|
||||||
if(!profileReq.ok){
|
if(!profileReq.ok || !profileReq.res){
|
||||||
console.log('[ERROR] Get profile failed!');
|
console.log('[ERROR] Get profile failed!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -166,14 +169,13 @@ async function doAnonymousAuth(){
|
||||||
'grant_type': 'client_id',
|
'grant_type': 'client_id',
|
||||||
'scope': 'offline_access',
|
'scope': 'offline_access',
|
||||||
}).toString();
|
}).toString();
|
||||||
const authReqOpts = {
|
const authReqOpts: reqModule.Params = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: api.beta_authHeaderMob,
|
headers: api.beta_authHeaderMob,
|
||||||
body: authData,
|
body: authData
|
||||||
useProxy: true
|
|
||||||
};
|
};
|
||||||
const authReq = await req.getData(api.beta_auth, authReqOpts);
|
const authReq = await req.getData(api.beta_auth, authReqOpts);
|
||||||
if(!authReq.ok){
|
if(!authReq.ok || !authReq.res){
|
||||||
console.log('[ERROR] Authentication failed!');
|
console.log('[ERROR] Authentication failed!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -196,14 +198,13 @@ async function refreshToken(){
|
||||||
'grant_type': 'refresh_token',
|
'grant_type': 'refresh_token',
|
||||||
'scope': 'offline_access'
|
'scope': 'offline_access'
|
||||||
}).toString();
|
}).toString();
|
||||||
const authReqOpts = {
|
const authReqOpts: reqModule.Params = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: api.beta_authHeaderMob,
|
headers: api.beta_authHeaderMob,
|
||||||
body: authData,
|
body: authData
|
||||||
useProxy: true
|
|
||||||
};
|
};
|
||||||
const authReq = await req.getData(api.beta_auth, authReqOpts);
|
const authReq = await req.getData(api.beta_auth, authReqOpts);
|
||||||
if(!authReq.ok){
|
if(!authReq.ok || !authReq.res){
|
||||||
console.log('[ERROR] Authentication failed!');
|
console.log('[ERROR] Authentication failed!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -232,12 +233,12 @@ async function getCMStoken(){
|
||||||
useProxy: true
|
useProxy: true
|
||||||
};
|
};
|
||||||
const cmsTokenReq = await req.getData(api.beta_cmsToken, cmsTokenReqOpts);
|
const cmsTokenReq = await req.getData(api.beta_cmsToken, cmsTokenReqOpts);
|
||||||
if(!cmsTokenReq.ok){
|
if(!cmsTokenReq.ok || !cmsTokenReq.res){
|
||||||
console.log('[ERROR] Authentication CMS token failed!');
|
console.log('[ERROR] Authentication CMS token failed!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cmsToken = JSON.parse(cmsTokenReq.res.body);
|
cmsToken = JSON.parse(cmsTokenReq.res.body);
|
||||||
console.log('[INFO] Your Country: %s\n', cmsToken.cms.bucket.split('/')[1]);
|
console.log('[INFO] Your Country: %s\n', cmsToken.cms?.bucket.split('/')[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getCmsData(){
|
async function getCmsData(){
|
||||||
|
|
@ -257,8 +258,8 @@ async function getCmsData(){
|
||||||
'Key-Pair-Id': cmsToken.cms.key_pair_id,
|
'Key-Pair-Id': cmsToken.cms.key_pair_id,
|
||||||
}),
|
}),
|
||||||
].join('');
|
].join('');
|
||||||
const indexReq = await req.getData(indexReqOpts, { useProxy: true });
|
const indexReq = await req.getData(indexReqOpts);
|
||||||
if(!indexReq.ok){
|
if(!indexReq.ok || ! indexReq.res){
|
||||||
console.log('[ERROR] Get CMS index FAILED!');
|
console.log('[ERROR] Get CMS index FAILED!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -277,18 +278,18 @@ async function doSearch(){
|
||||||
useProxy: true
|
useProxy: true
|
||||||
};
|
};
|
||||||
const searchParams = new URLSearchParams({
|
const searchParams = new URLSearchParams({
|
||||||
q: argv.search,
|
q: argv.search as string,
|
||||||
n: 5,
|
n: "5",
|
||||||
start: argv.page ? (parseInt(argv.page)-1)*5 : 0,
|
start: argv.page ? `${(argv.page-1)*5}` : "0",
|
||||||
type: argv['search-type'],
|
type: argv['search-type'],
|
||||||
locale: argv['search-locale'],
|
locale: argv['search-locale'],
|
||||||
}).toString();
|
}).toString();
|
||||||
let searchReq = await req.getData(`${api.beta_search}?${searchParams}`, searchReqOpts);
|
let searchReq = await req.getData(`${api.beta_search}?${searchParams}`, searchReqOpts);
|
||||||
if(!searchReq.ok){
|
if(!searchReq.ok || ! searchReq.res){
|
||||||
console.log('[ERROR] Search FAILED!');
|
console.log('[ERROR] Search FAILED!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let searchResults = JSON.parse(searchReq.res.body);
|
let searchResults = JSON.parse(searchReq.res.body) as CrunchySearch;
|
||||||
if(searchResults.total < 1){
|
if(searchResults.total < 1){
|
||||||
console.log('[INFO] Nothing Found!');
|
console.log('[INFO] Nothing Found!');
|
||||||
return;
|
return;
|
||||||
|
|
@ -300,9 +301,9 @@ async function doSearch(){
|
||||||
'episode': 'Found episodes'
|
'episode': 'Found episodes'
|
||||||
};
|
};
|
||||||
for(let search_item of searchResults.items){
|
for(let search_item of searchResults.items){
|
||||||
console.log('[INFO] %s:', searchTypesInfo[search_item.type]);
|
console.log('[INFO] %s:', searchTypesInfo[search_item.type as keyof typeof searchTypesInfo]);
|
||||||
// calculate pages
|
// calculate pages
|
||||||
let itemPad = parseInt(new URL(search_item.__href__, domain.api_beta).searchParams.get('start'));
|
let itemPad = parseInt(new URL(search_item.__href__, domain.api_beta).searchParams.get('start') || '');
|
||||||
let pageCur = itemPad > 0 ? Math.ceil(itemPad/5) + 1 : 1;
|
let pageCur = itemPad > 0 ? Math.ceil(itemPad/5) + 1 : 1;
|
||||||
let pageMax = Math.ceil(search_item.total/5);
|
let pageMax = Math.ceil(search_item.total/5);
|
||||||
// pages per category
|
// pages per category
|
||||||
|
|
@ -322,16 +323,17 @@ async function doSearch(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function parseObject(item, pad, getSeries, getMovieListing){
|
async function parseObject(item: ParseItem, pad?: number, getSeries?: boolean, getMovieListing?: boolean){
|
||||||
if(argv.debug){
|
if(argv.debug){
|
||||||
console.log(item);
|
console.log(item);
|
||||||
}
|
}
|
||||||
pad = typeof pad == 'number' ? pad : 2;
|
pad = pad || 2;
|
||||||
getSeries = typeof getSeries == 'boolean' ? getSeries : true;
|
getSeries = getSeries === undefined ? true : getSeries;
|
||||||
getMovieListing = typeof getMovieListing == 'boolean' ? getMovieListing : true;
|
getMovieListing = getMovieListing === undefined ? true : getMovieListing;
|
||||||
item.isSelected = typeof item.isSelected == 'boolean' ? item.isSelected : false;
|
item.isSelected = item.isSelected === undefined ? false : item.isSelected;
|
||||||
if(!item.type){
|
if(!item.type){
|
||||||
item.type = item.__class__;
|
console.log('[INFO] Unable to parse type for %s. Defaulted to %s', item.id, ItemType.Episode)
|
||||||
|
item.type = ItemType.Episode;
|
||||||
}
|
}
|
||||||
const oTypes = {
|
const oTypes = {
|
||||||
'series': 'Z', // SRZ
|
'series': 'Z', // SRZ
|
||||||
|
|
@ -396,7 +398,7 @@ async function parseObject(item, pad, getSeries, getMovieListing){
|
||||||
const showObjectBooleans = oBooleans.length > 0 && !iMetadata.hide_metadata ? true : false;
|
const showObjectBooleans = oBooleans.length > 0 && !iMetadata.hide_metadata ? true : false;
|
||||||
// make obj ids
|
// make obj ids
|
||||||
let objects_ids = [];
|
let objects_ids = [];
|
||||||
objects_ids.push(oTypes[item.type] + ':' + item.id);
|
objects_ids.push(oTypes[item.type as keyof typeof oTypes] + ':' + item.id);
|
||||||
if(item.seq_id){
|
if(item.seq_id){
|
||||||
objects_ids.unshift(item.seq_id);
|
objects_ids.unshift(item.seq_id);
|
||||||
}
|
}
|
||||||
|
|
@ -453,10 +455,10 @@ async function parseObject(item, pad, getSeries, getMovieListing){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getSeriesById(pad, hideSeriesTitle){
|
async function getSeriesById(pad?: number, hideSeriesTitle?: boolean){
|
||||||
// parse
|
// parse
|
||||||
pad = typeof pad == 'number' ? pad : 0;
|
pad = pad || 0;
|
||||||
hideSeriesTitle = typeof hideSeriesTitle == 'boolean' ? hideSeriesTitle : false;
|
hideSeriesTitle = hideSeriesTitle !== undefined ? hideSeriesTitle : false;
|
||||||
// check token
|
// check token
|
||||||
if(!cmsToken.cms){
|
if(!cmsToken.cms){
|
||||||
console.log('[ERROR] Authentication required!');
|
console.log('[ERROR] Authentication required!');
|
||||||
|
|
@ -480,7 +482,7 @@ async function getSeriesById(pad, hideSeriesTitle){
|
||||||
cmsToken.cms.bucket,
|
cmsToken.cms.bucket,
|
||||||
'/seasons?',
|
'/seasons?',
|
||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
'series_id': argv.series,
|
'series_id': argv.series as string,
|
||||||
'Policy': cmsToken.cms.policy,
|
'Policy': cmsToken.cms.policy,
|
||||||
'Signature': cmsToken.cms.signature,
|
'Signature': cmsToken.cms.signature,
|
||||||
'Key-Pair-Id': cmsToken.cms.key_pair_id,
|
'Key-Pair-Id': cmsToken.cms.key_pair_id,
|
||||||
|
|
@ -488,8 +490,8 @@ async function getSeriesById(pad, hideSeriesTitle){
|
||||||
].join('');
|
].join('');
|
||||||
// reqs
|
// reqs
|
||||||
if(!hideSeriesTitle){
|
if(!hideSeriesTitle){
|
||||||
const seriesReq = await req.getData(seriesReqOpts, {useProxy: true});
|
const seriesReq = await req.getData(seriesReqOpts);
|
||||||
if(!seriesReq.ok){
|
if(!seriesReq.ok || !seriesReq.res){
|
||||||
console.log('[ERROR] Series Request FAILED!');
|
console.log('[ERROR] Series Request FAILED!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -497,8 +499,8 @@ async function getSeriesById(pad, hideSeriesTitle){
|
||||||
await parseObject(seriesData, pad, false);
|
await parseObject(seriesData, pad, false);
|
||||||
}
|
}
|
||||||
// seasons list
|
// seasons list
|
||||||
const seriesSeasonListReq = await req.getData(seriesSeasonListReqOpts, {useProxy: true});
|
const seriesSeasonListReq = await req.getData(seriesSeasonListReqOpts);
|
||||||
if(!seriesSeasonListReq.ok){
|
if(!seriesSeasonListReq.ok || !seriesSeasonListReq.res){
|
||||||
console.log('[ERROR] Series Request FAILED!');
|
console.log('[ERROR] Series Request FAILED!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -513,8 +515,8 @@ async function getSeriesById(pad, hideSeriesTitle){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getMovieListingById(pad){
|
async function getMovieListingById(pad?: number){
|
||||||
pad = typeof pad == 'number' ? pad : 2;
|
pad = pad || 2;
|
||||||
if(!cmsToken.cms){
|
if(!cmsToken.cms){
|
||||||
console.log('[ERROR] Authentication required!');
|
console.log('[ERROR] Authentication required!');
|
||||||
return;
|
return;
|
||||||
|
|
@ -524,14 +526,14 @@ async function getMovieListingById(pad){
|
||||||
cmsToken.cms.bucket,
|
cmsToken.cms.bucket,
|
||||||
'/movies?',
|
'/movies?',
|
||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
'movie_listing_id': argv['movie-listing'],
|
'movie_listing_id': argv['movie-listing'] as string,
|
||||||
'Policy': cmsToken.cms.policy,
|
'Policy': cmsToken.cms.policy,
|
||||||
'Signature': cmsToken.cms.signature,
|
'Signature': cmsToken.cms.signature,
|
||||||
'Key-Pair-Id': cmsToken.cms.key_pair_id,
|
'Key-Pair-Id': cmsToken.cms.key_pair_id,
|
||||||
}),
|
}),
|
||||||
].join('');
|
].join('');
|
||||||
const movieListingReq = await req.getData(movieListingReqOpts, {useProxy: true});
|
const movieListingReq = await req.getData(movieListingReqOpts);
|
||||||
if(!movieListingReq.ok){
|
if(!movieListingReq.ok || !movieListingReq.res){
|
||||||
console.log('[ERROR] Movie Listing Request FAILED!');
|
console.log('[ERROR] Movie Listing Request FAILED!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -558,11 +560,11 @@ async function getNewlyAdded(){
|
||||||
};
|
};
|
||||||
const newlyAddedParams = new URLSearchParams({
|
const newlyAddedParams = new URLSearchParams({
|
||||||
sort_by: 'newly_added',
|
sort_by: 'newly_added',
|
||||||
n: 25,
|
n: "25",
|
||||||
start: argv.page ? (parseInt(argv.page)-1)*25 : 0,
|
start: (argv.page ? (argv.page-1)*25 : 0).toString(),
|
||||||
}).toString();
|
}).toString();
|
||||||
let newlyAddedReq = await req.getData(`${api.beta_browse}?${newlyAddedParams}`, newlyAddedReqOpts);
|
let newlyAddedReq = await req.getData(`${api.beta_browse}?${newlyAddedParams}`, newlyAddedReqOpts);
|
||||||
if(!newlyAddedReq.ok){
|
if(!newlyAddedReq.ok || !newlyAddedReq.res){
|
||||||
console.log('[ERROR] Get newly added FAILED!');
|
console.log('[ERROR] Get newly added FAILED!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -572,7 +574,7 @@ async function getNewlyAdded(){
|
||||||
await parseObject(i, 2);
|
await parseObject(i, 2);
|
||||||
}
|
}
|
||||||
// calculate pages
|
// calculate pages
|
||||||
let itemPad = parseInt(new URL(newlyAddedResults.__href__, domain.api_beta).searchParams.get('start'));
|
let itemPad = parseInt(new URL(newlyAddedResults.__href__, domain.api_beta).searchParams.get('start') as string);
|
||||||
let pageCur = itemPad > 0 ? Math.ceil(itemPad/5) + 1 : 1;
|
let pageCur = itemPad > 0 ? Math.ceil(itemPad/5) + 1 : 1;
|
||||||
let pageMax = Math.ceil(newlyAddedResults.total/5);
|
let pageMax = Math.ceil(newlyAddedResults.total/5);
|
||||||
console.log(` [INFO] Total results: ${newlyAddedResults.total} (Page: ${pageCur}/${pageMax})`);
|
console.log(` [INFO] Total results: ${newlyAddedResults.total} (Page: ${pageCur}/${pageMax})`);
|
||||||
|
|
@ -596,8 +598,8 @@ async function getSeasonById(){
|
||||||
'Key-Pair-Id': cmsToken.cms.key_pair_id,
|
'Key-Pair-Id': cmsToken.cms.key_pair_id,
|
||||||
}),
|
}),
|
||||||
].join('');
|
].join('');
|
||||||
const showInfoReq = await req.getData(showInfoReqOpts, {useProxy: true});
|
const showInfoReq = await req.getData(showInfoReqOpts);
|
||||||
if(!showInfoReq.ok){
|
if(!showInfoReq.ok || !showInfoReq.res){
|
||||||
console.log('[ERROR] Show Request FAILED!');
|
console.log('[ERROR] Show Request FAILED!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -608,20 +610,23 @@ async function getSeasonById(){
|
||||||
cmsToken.cms.bucket,
|
cmsToken.cms.bucket,
|
||||||
'/episodes?',
|
'/episodes?',
|
||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
'season_id': argv.season,
|
'season_id': argv.season as string,
|
||||||
'Policy': cmsToken.cms.policy,
|
'Policy': cmsToken.cms.policy,
|
||||||
'Signature': cmsToken.cms.signature,
|
'Signature': cmsToken.cms.signature,
|
||||||
'Key-Pair-Id': cmsToken.cms.key_pair_id,
|
'Key-Pair-Id': cmsToken.cms.key_pair_id,
|
||||||
}),
|
}),
|
||||||
].join('');
|
].join('');
|
||||||
const reqEpsList = await req.getData(reqEpsListOpts, {useProxy: true});
|
const reqEpsList = await req.getData(reqEpsListOpts);
|
||||||
if(!reqEpsList.ok){
|
if(!reqEpsList.ok || !reqEpsList.res){
|
||||||
console.log('[ERROR] Episode List Request FAILED!');
|
console.log('[ERROR] Episode List Request FAILED!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let episodeList = JSON.parse(reqEpsList.res.body);
|
let episodeList = JSON.parse(reqEpsList.res.body) as CrunchyEpisodeList;
|
||||||
|
|
||||||
const epNumList = { ep: [], sp: 0 };
|
const epNumList: {
|
||||||
|
ep: number[],
|
||||||
|
sp: number
|
||||||
|
} = { ep: [], sp: 0 };
|
||||||
const epNumLen = epsFilter.epNumLen;
|
const epNumLen = epsFilter.epNumLen;
|
||||||
|
|
||||||
if(episodeList.total < 1){
|
if(episodeList.total < 1){
|
||||||
|
|
@ -631,7 +636,7 @@ async function getSeasonById(){
|
||||||
|
|
||||||
const doEpsFilter = new epsFilter.doFilter();
|
const doEpsFilter = new epsFilter.doFilter();
|
||||||
const selEps = doEpsFilter.checkFilter(argv.episode);
|
const selEps = doEpsFilter.checkFilter(argv.episode);
|
||||||
const selectedMedia = [];
|
const selectedMedia: CrunchyEpMeta[] = [];
|
||||||
|
|
||||||
episodeList.items.forEach((item) => {
|
episodeList.items.forEach((item) => {
|
||||||
item.hide_season_title = true;
|
item.hide_season_title = true;
|
||||||
|
|
@ -644,7 +649,7 @@ async function getSeasonById(){
|
||||||
item.season_title = 'NO_TITLE';
|
item.season_title = 'NO_TITLE';
|
||||||
}
|
}
|
||||||
// set data
|
// set data
|
||||||
const epMeta = {
|
const epMeta: CrunchyEpMeta = {
|
||||||
mediaId: item.id,
|
mediaId: item.id,
|
||||||
seasonTitle: item.season_title,
|
seasonTitle: item.season_title,
|
||||||
episodeNumber: item.episode,
|
episodeNumber: item.episode,
|
||||||
|
|
@ -685,7 +690,7 @@ async function getSeasonById(){
|
||||||
}
|
}
|
||||||
|
|
||||||
if(selectedMedia.length > 1){
|
if(selectedMedia.length > 1){
|
||||||
argv.appstore.isBatch = true;
|
(argv.appstore as Record<string, unknown>).isBatch = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log();
|
console.log();
|
||||||
|
|
@ -1,285 +0,0 @@
|
||||||
const path = require('path');
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
|
|
||||||
const shlp = require('sei-helper');
|
|
||||||
const got = require('got');
|
|
||||||
const cookieFile = require('./module.cookieFile');
|
|
||||||
const yamlCfg = require('./module.cfg-loader');
|
|
||||||
const curlReq = require('./module.curl-req');
|
|
||||||
|
|
||||||
// set usable cookies
|
|
||||||
const usefulCookies = {
|
|
||||||
auth: [
|
|
||||||
'etp_rt',
|
|
||||||
'c_visitor',
|
|
||||||
],
|
|
||||||
sess: [
|
|
||||||
'session_id',
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
// req
|
|
||||||
const Req = class {
|
|
||||||
constructor(domain, argv, is_beta){
|
|
||||||
// settings and cookies
|
|
||||||
this.is_beta = Boolean(is_beta);
|
|
||||||
this.loadSessTxt = this.is_beta ? false : true;
|
|
||||||
// main cfg
|
|
||||||
this.domain = domain;
|
|
||||||
this.argv = argv;
|
|
||||||
// session cfg
|
|
||||||
this.sessCfg = yamlCfg.sessCfgFile,
|
|
||||||
this.session = this.is_beta ? {} : yamlCfg.loadCRSession();
|
|
||||||
this.cfgDir = yamlCfg.cfgFolder;
|
|
||||||
this.curl = false;
|
|
||||||
}
|
|
||||||
async getData (durl, params) {
|
|
||||||
params = params || {};
|
|
||||||
// options
|
|
||||||
let options = {
|
|
||||||
method: params.method ? params.method : 'GET',
|
|
||||||
headers: {
|
|
||||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:90.0) Gecko/20100101 Firefox/90.0',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
// additional params
|
|
||||||
if(params.headers){
|
|
||||||
options.headers = {...options.headers, ...params.headers};
|
|
||||||
}
|
|
||||||
if(options.method == 'POST'){
|
|
||||||
options.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
||||||
}
|
|
||||||
if(params.body){
|
|
||||||
options.body = params.body;
|
|
||||||
}
|
|
||||||
if(params.binary == true){
|
|
||||||
options.responseType = 'buffer';
|
|
||||||
}
|
|
||||||
if(typeof params.followRedirect == 'boolean'){
|
|
||||||
options.followRedirect = params.followRedirect;
|
|
||||||
}
|
|
||||||
// check if cookies.txt exists
|
|
||||||
const sessTxtFile = path.join(this.cfgDir, 'cookies.txt');
|
|
||||||
if(!this.is_beta && this.loadSessTxt && fs.existsSync(sessTxtFile)){
|
|
||||||
const cookiesTxtName = path.basename(sessTxtFile);
|
|
||||||
try{
|
|
||||||
// console.log(`[INFO] Loading custom ${cookiesTxtName} file...`);
|
|
||||||
const netcookie = fs.readFileSync(sessTxtFile, 'utf8');
|
|
||||||
fs.unlinkSync(sessTxtFile);
|
|
||||||
this.setNewCookie('', true, netcookie);
|
|
||||||
}
|
|
||||||
catch(e){
|
|
||||||
console.log(`[ERROR] Cannot load ${cookiesTxtName} file!`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.loadSessTxt = false;
|
|
||||||
// proxy
|
|
||||||
if(params.useProxy && this.argv.proxy && this.argv.curl){
|
|
||||||
try{
|
|
||||||
options.curlProxy = buildProxy(this.argv.proxy);
|
|
||||||
options.curlProxyAuth = this.argv['proxy-auth'];
|
|
||||||
}
|
|
||||||
catch(e){
|
|
||||||
console.log(`[WARN] Not valid proxy URL${e.input?' ('+e.input+')':''}!`);
|
|
||||||
console.log('[WARN] Skipping...\n');
|
|
||||||
this.argv.proxy = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if auth
|
|
||||||
let cookie = [];
|
|
||||||
const loc = new URL(durl);
|
|
||||||
if(!this.is_beta && Object.values(this.domain).includes(loc.origin)){
|
|
||||||
for(let uCookie of usefulCookies.auth){
|
|
||||||
const checkedCookie = this.checkCookieVal(this.session[uCookie]);
|
|
||||||
if(checkedCookie){
|
|
||||||
cookie.push(uCookie);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(let uCookie of usefulCookies.sess){
|
|
||||||
if(this.checkSessId(this.session[uCookie]) && !this.argv.nosess){
|
|
||||||
cookie.push(uCookie);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!params.skipCookies){
|
|
||||||
cookie.push('c_locale');
|
|
||||||
options.headers.Cookie = shlp.cookie.make({
|
|
||||||
...{ c_locale : { value: 'enUS' } },
|
|
||||||
...this.session,
|
|
||||||
}, cookie);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// avoid cloudflare protection
|
|
||||||
if(loc.origin == this.domain.www){
|
|
||||||
options.minVersion = 'TLSv1.3';
|
|
||||||
options.maxVersion = 'TLSv1.3';
|
|
||||||
options.http2 = true;
|
|
||||||
}
|
|
||||||
// debug
|
|
||||||
options.hooks = {
|
|
||||||
beforeRequest: [
|
|
||||||
(options) => {
|
|
||||||
if(this.argv.debug){
|
|
||||||
console.log('[DEBUG] GOT OPTIONS:');
|
|
||||||
console.log(options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
if(this.argv.debug){
|
|
||||||
options.curlDebug = true;
|
|
||||||
}
|
|
||||||
// try do request
|
|
||||||
try {
|
|
||||||
let res;
|
|
||||||
if(this.curl && this.argv.curl && Object.values(this.domain).includes(loc.origin)){
|
|
||||||
res = await curlReq(this.curl, durl.toString(), options, this.cfgDir);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
res = await got(durl.toString(), options);
|
|
||||||
}
|
|
||||||
if(!this.is_beta && !params.skipCookies && res && res.headers && res.headers['set-cookie']){
|
|
||||||
this.setNewCookie(res.headers['set-cookie'], false);
|
|
||||||
for(let uCookie of usefulCookies.sess){
|
|
||||||
if(this.session[uCookie] && this.argv.nosess){
|
|
||||||
this.argv.nosess = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
ok: true,
|
|
||||||
res,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
catch(error){
|
|
||||||
if(error.response && error.response.statusCode && error.response.statusMessage){
|
|
||||||
console.log(`[ERROR] ${error.name} ${error.response.statusCode}: ${error.response.statusMessage}`);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
console.log(`[ERROR] ${error.name}: ${error.code || error.message}`);
|
|
||||||
}
|
|
||||||
if(error.response && !error.res){
|
|
||||||
error.res = error.response;
|
|
||||||
const docTitle = error.res.body.match(/<title>(.*)<\/title>/);
|
|
||||||
if(error.res.body && docTitle){
|
|
||||||
console.log('[ERROR]', docTitle[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(error.res && error.res.body && error.response.statusCode
|
|
||||||
&& error.response.statusCode != 404 && error.response.statusCode != 403){
|
|
||||||
console.log('[ERROR] Body:', error.res.body);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
ok: false,
|
|
||||||
error,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setNewCookie(setCookie, isAuth, fileData){
|
|
||||||
let cookieUpdated = [], lastExp = 0;
|
|
||||||
setCookie = fileData ? cookieFile(fileData) : shlp.cookie.parse(setCookie);
|
|
||||||
for(let cookieName of Object.keys(setCookie)){
|
|
||||||
if(setCookie[cookieName] && setCookie[cookieName].value && setCookie[cookieName].value == 'deleted'){
|
|
||||||
delete setCookie[cookieName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(let uCookie of usefulCookies.auth){
|
|
||||||
const cookieForceExp = 60*60*24*7;
|
|
||||||
const cookieExpCur = this.session[uCookie] ? this.session[uCookie] : { expires: 0 };
|
|
||||||
const cookieExp = new Date(cookieExpCur.expires).getTime() - cookieForceExp;
|
|
||||||
if(cookieExp > lastExp){
|
|
||||||
lastExp = cookieExp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(let uCookie of usefulCookies.auth){
|
|
||||||
if(!setCookie[uCookie]){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(isAuth || setCookie[uCookie] && Date.now() > lastExp){
|
|
||||||
this.session[uCookie] = setCookie[uCookie];
|
|
||||||
cookieUpdated.push(uCookie);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for(let uCookie of usefulCookies.sess){
|
|
||||||
if(!setCookie[uCookie]){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(
|
|
||||||
isAuth
|
|
||||||
|| this.argv.nosess && setCookie[uCookie]
|
|
||||||
|| setCookie[uCookie] && !this.checkSessId(this.session[uCookie])
|
|
||||||
){
|
|
||||||
const sessionExp = 60*60;
|
|
||||||
this.session[uCookie] = setCookie[uCookie];
|
|
||||||
this.session[uCookie].expires = new Date(Date.now() + sessionExp*1000);
|
|
||||||
this.session[uCookie]['Max-Age'] = sessionExp.toString();
|
|
||||||
cookieUpdated.push(uCookie);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(cookieUpdated.length > 0){
|
|
||||||
if(this.argv.debug){
|
|
||||||
console.log('[SAVING FILE]',`${this.sessCfg}.yml`);
|
|
||||||
}
|
|
||||||
yamlCfg.saveCRSession(this.session);
|
|
||||||
console.log(`[INFO] Cookies were updated! (${cookieUpdated.join(', ')})\n`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
checkCookieVal(chcookie){
|
|
||||||
return chcookie
|
|
||||||
&& chcookie.toString() == '[object Object]'
|
|
||||||
&& typeof chcookie.value == 'string'
|
|
||||||
? true : false;
|
|
||||||
}
|
|
||||||
checkSessId(session_id){
|
|
||||||
if(session_id && typeof session_id.expires == 'string'){
|
|
||||||
session_id.expires = new Date(session_id.expires);
|
|
||||||
}
|
|
||||||
return session_id
|
|
||||||
&& session_id.toString() == '[object Object]'
|
|
||||||
&& typeof session_id.expires == 'object'
|
|
||||||
&& Date.now() < new Date(session_id.expires).getTime()
|
|
||||||
&& typeof session_id.value == 'string'
|
|
||||||
? true : false;
|
|
||||||
}
|
|
||||||
uuidv4(){
|
|
||||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
||||||
let r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
|
||||||
return v.toString(16);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function buildProxy(proxyBaseUrl, proxyAuth){
|
|
||||||
if(!proxyBaseUrl.match(/^(https?|socks4|socks5):/)){
|
|
||||||
proxyBaseUrl = 'http://' + proxyBaseUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
let proxyCfg = new URL(proxyBaseUrl);
|
|
||||||
let proxyStr = `${proxyCfg.protocol}//`;
|
|
||||||
|
|
||||||
if(typeof proxyCfg.hostname != 'string' || proxyCfg.hostname == ''){
|
|
||||||
throw new Error('[ERROR] Hostname and port required for proxy!');
|
|
||||||
}
|
|
||||||
|
|
||||||
if(proxyAuth && typeof proxyAuth == 'string' && proxyAuth.match(':')){
|
|
||||||
proxyCfg.username = proxyAuth.split(':')[0];
|
|
||||||
proxyCfg.password = proxyAuth.split(':')[1];
|
|
||||||
proxyStr += `${proxyCfg.username}:${proxyCfg.password}@`;
|
|
||||||
}
|
|
||||||
|
|
||||||
proxyStr += proxyCfg.hostname;
|
|
||||||
|
|
||||||
if(!proxyCfg.port && proxyCfg.protocol == 'http:'){
|
|
||||||
proxyStr += ':80';
|
|
||||||
}
|
|
||||||
else if(!proxyCfg.port && proxyCfg.protocol == 'https:'){
|
|
||||||
proxyStr += ':443';
|
|
||||||
}
|
|
||||||
|
|
||||||
return proxyStr;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
buildProxy,
|
|
||||||
usefulCookies,
|
|
||||||
Req,
|
|
||||||
};
|
|
||||||
8
funi.ts
8
funi.ts
|
|
@ -55,7 +55,7 @@ let title = '',
|
||||||
stDlPath: Subtitle[] = [];
|
stDlPath: Subtitle[] = [];
|
||||||
|
|
||||||
// main
|
// main
|
||||||
(async () => {
|
export default (async () => {
|
||||||
// load binaries
|
// load binaries
|
||||||
cfg.bin = await yamlCfg.loadBinCfg();
|
cfg.bin = await yamlCfg.loadBinCfg();
|
||||||
// select mode
|
// select mode
|
||||||
|
|
@ -65,13 +65,13 @@ let title = '',
|
||||||
else if(argv.search){
|
else if(argv.search){
|
||||||
searchShow();
|
searchShow();
|
||||||
}
|
}
|
||||||
else if(argv.s && argv.s > 0){
|
else if(argv.s && !isNaN(parseInt(argv.s)) && parseInt(argv.s) > 0){
|
||||||
getShow();
|
getShow();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
appYargs.showHelp();
|
appYargs.showHelp();
|
||||||
}
|
}
|
||||||
})();
|
});
|
||||||
|
|
||||||
// auth
|
// auth
|
||||||
async function auth(){
|
async function auth(){
|
||||||
|
|
@ -157,7 +157,7 @@ async function getShow(){
|
||||||
sort_direction: string,
|
sort_direction: string,
|
||||||
title_id: number,
|
title_id: number,
|
||||||
language?: string
|
language?: string
|
||||||
} = { limit: -1, sort: 'order', sort_direction: 'ASC', title_id: argv.s as number };
|
} = { limit: -1, sort: 'order', sort_direction: 'ASC', title_id: parseInt(argv.s as string) };
|
||||||
if(argv.alt){ qs.language = 'English'; }
|
if(argv.alt){ qs.language = 'English'; }
|
||||||
const episodesData = await getData({
|
const episodesData = await getData({
|
||||||
baseUrl: api_host,
|
baseUrl: api_host,
|
||||||
|
|
|
||||||
18
index.ts
18
index.ts
|
|
@ -1 +1,17 @@
|
||||||
import { appArgv } from "./modules/module.app-args";
|
import { appArgv } from "./modules/module.app-args";
|
||||||
|
import * as yamlCfg from './modules/module.cfg-loader';
|
||||||
|
|
||||||
|
import funimation from './funi'
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const cfg = yamlCfg.loadCfg();
|
||||||
|
|
||||||
|
const argv = appArgv(cfg.cli);
|
||||||
|
|
||||||
|
if (argv.service === 'funi') {
|
||||||
|
await funimation()
|
||||||
|
} else if (argv.service === 'crunchy') {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
})()
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { Headers } from "got/dist/source";
|
||||||
|
|
||||||
// api domains
|
// api domains
|
||||||
const domain = {
|
const domain = {
|
||||||
www: 'https://www.crunchyroll.com',
|
www: 'https://www.crunchyroll.com',
|
||||||
|
|
@ -28,8 +30,8 @@ export type APIType = {
|
||||||
beta_search: string
|
beta_search: string
|
||||||
beta_browse: string
|
beta_browse: string
|
||||||
beta_cms: string,
|
beta_cms: string,
|
||||||
beta_authHeader: HeadersInit,
|
beta_authHeader: Headers,
|
||||||
beta_authHeaderMob: HeadersInit
|
beta_authHeaderMob: Headers
|
||||||
}
|
}
|
||||||
|
|
||||||
// api urls
|
// api urls
|
||||||
|
|
|
||||||
|
|
@ -119,9 +119,9 @@ const appArgv = (cfg: {
|
||||||
default: parseDefault<number>('videoLayer', 7),
|
default: parseDefault<number>('videoLayer', 7),
|
||||||
type: 'number'
|
type: 'number'
|
||||||
})
|
})
|
||||||
.option('server', {
|
.option('x', {
|
||||||
group: groups.dl,
|
group: groups.dl,
|
||||||
alias: 'x',
|
alias: 'server',
|
||||||
describe: 'Select server',
|
describe: 'Select server',
|
||||||
choices: [1, 2, 3, 4],
|
choices: [1, 2, 3, 4],
|
||||||
default: parseDefault<number>('nServer', 1),
|
default: parseDefault<number>('nServer', 1),
|
||||||
|
|
@ -260,7 +260,14 @@ const appArgv = (cfg: {
|
||||||
describe: 'Show this help',
|
describe: 'Show this help',
|
||||||
type: 'boolean'
|
type: 'boolean'
|
||||||
})
|
})
|
||||||
|
.option('service', {
|
||||||
|
group: groups.util,
|
||||||
|
describe: 'Set the service to use',
|
||||||
|
choices: ['funi', 'chrunchy'],
|
||||||
|
demandOption: true
|
||||||
|
})
|
||||||
.parseSync();
|
.parseSync();
|
||||||
|
return argv;
|
||||||
}
|
}
|
||||||
|
|
||||||
const showHelp = yargs.showHelp;
|
const showHelp = yargs.showHelp;
|
||||||
|
|
|
||||||
|
|
@ -192,4 +192,17 @@ const saveFuniToken = (data: {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export { loadBinCfg, loadCfg, loadFuniToken, saveFuniToken, saveCRSession, saveCRToken, loadCRToken, loadCRSession };
|
const cfgDir = path.join(workingDir, 'config');
|
||||||
|
|
||||||
|
export {
|
||||||
|
loadBinCfg,
|
||||||
|
loadCfg,
|
||||||
|
loadFuniToken,
|
||||||
|
saveFuniToken,
|
||||||
|
saveCRSession,
|
||||||
|
saveCRToken,
|
||||||
|
loadCRToken,
|
||||||
|
loadCRSession,
|
||||||
|
sessCfgFile,
|
||||||
|
cfgDir
|
||||||
|
};
|
||||||
|
|
@ -23,4 +23,4 @@ const parse = (data: string) => {
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = parse;
|
export default parse;
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
// build-in
|
// build-in
|
||||||
import child_process from 'child_process';
|
import child_process from 'child_process';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
|
import { Headers } from 'got';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
export type CurlOptions = {
|
export type CurlOptions = {
|
||||||
headers?: Record<string, string>,
|
headers?: Headers,
|
||||||
curlProxy?: boolean,
|
curlProxy?: boolean,
|
||||||
curlProxyAuth?: string,
|
curlProxyAuth?: string,
|
||||||
minVersion?: string,
|
minVersion?: string,
|
||||||
http2?: string,
|
http2?: boolean,
|
||||||
body?: unknown,
|
body?: unknown,
|
||||||
curlDebug?: boolean
|
curlDebug?: boolean
|
||||||
} | undefined;
|
} | undefined;
|
||||||
|
|
@ -158,4 +159,4 @@ function uuidv4() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = curlReq;
|
export default curlReq;
|
||||||
|
|
|
||||||
|
|
@ -89,5 +89,7 @@ function fontMime(fontFile: string){
|
||||||
return 'application/octet-stream';
|
return 'application/octet-stream';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type AvailableFonts = keyof typeof fonts;
|
||||||
|
|
||||||
// output
|
// output
|
||||||
export { root, fonts, assFonts, fontMime };
|
export { root, fonts, assFonts, fontMime };
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ export type Options = {
|
||||||
dinstid?: boolean|string,
|
dinstid?: boolean|string,
|
||||||
debug?: boolean
|
debug?: boolean
|
||||||
}
|
}
|
||||||
|
// TODO convert to class
|
||||||
const getData = async <T = string>(options: Options) => {
|
const getData = async <T = string>(options: Options) => {
|
||||||
const regionHeaders = {};
|
const regionHeaders = {};
|
||||||
|
|
||||||
|
|
|
||||||
261
modules/module.req.ts
Normal file
261
modules/module.req.ts
Normal file
|
|
@ -0,0 +1,261 @@
|
||||||
|
import path from 'path';
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
|
||||||
|
import shlp from 'sei-helper';
|
||||||
|
import got, { Headers, Method, Options, ReadError, Response } from 'got';
|
||||||
|
import cookieFile from './module.cookieFile';
|
||||||
|
import * as yamlCfg from './module.cfg-loader';
|
||||||
|
import curlReq from './module.curl-req';
|
||||||
|
|
||||||
|
export type Params = {
|
||||||
|
method?: Method,
|
||||||
|
headers?: Headers,
|
||||||
|
body?: string | Buffer,
|
||||||
|
binary?: boolean,
|
||||||
|
followRedirect?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
// set usable cookies
|
||||||
|
const usefulCookies = {
|
||||||
|
auth: [
|
||||||
|
'etp_rt',
|
||||||
|
'c_visitor',
|
||||||
|
],
|
||||||
|
sess: [
|
||||||
|
'session_id',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
// req
|
||||||
|
class Req {
|
||||||
|
private sessCfg = yamlCfg.sessCfgFile;
|
||||||
|
private session: Record<string, {
|
||||||
|
value: string;
|
||||||
|
expires: Date;
|
||||||
|
path: string;
|
||||||
|
domain: string;
|
||||||
|
secure: boolean;
|
||||||
|
'Max-Age'?: string
|
||||||
|
}> = {};
|
||||||
|
private cfgDir = yamlCfg.cfgDir
|
||||||
|
private curl: boolean|string = false;
|
||||||
|
|
||||||
|
constructor(private domain: Record<string, unknown>, private argv: Record<string, unknown>) {}
|
||||||
|
async getData<T = string> (durl: string, params?: Params) {
|
||||||
|
params = params || {};
|
||||||
|
// options
|
||||||
|
let options: Options & {
|
||||||
|
minVersion?: string,
|
||||||
|
maxVersion?: string
|
||||||
|
curlDebug?: boolean
|
||||||
|
} = {
|
||||||
|
method: params.method ? params.method : 'GET',
|
||||||
|
headers: {
|
||||||
|
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:90.0) Gecko/20100101 Firefox/90.0',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// additional params
|
||||||
|
if(params.headers){
|
||||||
|
options.headers = {...options.headers, ...params.headers};
|
||||||
|
}
|
||||||
|
if(options.method == 'POST'){
|
||||||
|
(options.headers as Headers)['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||||
|
}
|
||||||
|
if(params.body){
|
||||||
|
options.body = params.body;
|
||||||
|
}
|
||||||
|
if(params.binary == true){
|
||||||
|
options.responseType = 'buffer';
|
||||||
|
}
|
||||||
|
if(typeof params.followRedirect == 'boolean'){
|
||||||
|
options.followRedirect = params.followRedirect;
|
||||||
|
}
|
||||||
|
// Removed Proxy support since it was only partialy supported
|
||||||
|
/*if(params.useProxy && this.argv.proxy && this.argv.curl){
|
||||||
|
try{
|
||||||
|
options.curlProxy = buildProxy(this.argv.proxy);
|
||||||
|
options.curlProxyAuth = this.argv['proxy-auth'];
|
||||||
|
}
|
||||||
|
catch(e){
|
||||||
|
console.log(`[WARN] Not valid proxy URL${e.input?' ('+e.input+')':''}!`);
|
||||||
|
console.log('[WARN] Skipping...\n');
|
||||||
|
this.argv.proxy = false;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
// if auth
|
||||||
|
let cookie = [];
|
||||||
|
const loc = new URL(durl);
|
||||||
|
// avoid cloudflare protection
|
||||||
|
if(loc.origin == this.domain.www){
|
||||||
|
options.minVersion = 'TLSv1.3';
|
||||||
|
options.maxVersion = 'TLSv1.3';
|
||||||
|
options.http2 = true;
|
||||||
|
}
|
||||||
|
// debug
|
||||||
|
options.hooks = {
|
||||||
|
beforeRequest: [
|
||||||
|
(options) => {
|
||||||
|
if(this.argv.debug){
|
||||||
|
console.log('[DEBUG] GOT OPTIONS:');
|
||||||
|
console.log(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
if(this.argv.debug){
|
||||||
|
options.curlDebug = true;
|
||||||
|
}
|
||||||
|
// try do request
|
||||||
|
try {
|
||||||
|
let res: Response<T>;
|
||||||
|
if(this.curl && this.argv.curl && Object.values(this.domain).includes(loc.origin)){
|
||||||
|
res = await curlReq(typeof this.curl === 'boolean' ? '' : this.curl, durl.toString(), options, this.cfgDir) as unknown as Response<T>;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
res = await got(durl.toString(), options) as unknown as Response<T>;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
res
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch(_error){
|
||||||
|
const error = _error as {
|
||||||
|
name: string
|
||||||
|
} & ReadError & {
|
||||||
|
res: Response<unknown>
|
||||||
|
}
|
||||||
|
if(error.response && error.response.statusCode && error.response.statusMessage){
|
||||||
|
console.log(`[ERROR] ${error.name} ${error.response.statusCode}: ${error.response.statusMessage}`);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
console.log(`[ERROR] ${error.name}: ${error.code || error.message}`);
|
||||||
|
}
|
||||||
|
if(error.response && !error.res){
|
||||||
|
error.res = error.response;
|
||||||
|
const docTitle = (error.res.body as string).match(/<title>(.*)<\/title>/);
|
||||||
|
if(error.res.body && docTitle){
|
||||||
|
console.log('[ERROR]', docTitle[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(error.res && error.res.body && error.response.statusCode
|
||||||
|
&& error.response.statusCode != 404 && error.response.statusCode != 403){
|
||||||
|
console.log('[ERROR] Body:', error.res.body);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
ok: false,
|
||||||
|
error,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setNewCookie(setCookie: Record<string, string>, isAuth: boolean, fileData?: string){
|
||||||
|
let cookieUpdated = [], lastExp = 0;
|
||||||
|
console.trace('Type of setCookie:', typeof setCookie, setCookie)
|
||||||
|
const parsedCookie = fileData ? cookieFile(fileData) : shlp.cookie.parse(setCookie);
|
||||||
|
for(let cookieName of Object.keys(parsedCookie)){
|
||||||
|
if(parsedCookie[cookieName] && parsedCookie[cookieName].value && parsedCookie[cookieName].value == 'deleted'){
|
||||||
|
delete parsedCookie[cookieName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(let uCookie of usefulCookies.auth){
|
||||||
|
const cookieForceExp = 60*60*24*7;
|
||||||
|
const cookieExpCur = this.session[uCookie] ? this.session[uCookie] : { expires: 0 };
|
||||||
|
const cookieExp = new Date(cookieExpCur.expires).getTime() - cookieForceExp;
|
||||||
|
if(cookieExp > lastExp){
|
||||||
|
lastExp = cookieExp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(let uCookie of usefulCookies.auth){
|
||||||
|
if(!parsedCookie[uCookie]){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(isAuth || parsedCookie[uCookie] && Date.now() > lastExp){
|
||||||
|
this.session[uCookie] = parsedCookie[uCookie];
|
||||||
|
cookieUpdated.push(uCookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(let uCookie of usefulCookies.sess){
|
||||||
|
if(!parsedCookie[uCookie]){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(
|
||||||
|
isAuth
|
||||||
|
|| this.argv.nosess && parsedCookie[uCookie]
|
||||||
|
|| parsedCookie[uCookie] && !this.checkSessId(this.session[uCookie])
|
||||||
|
){
|
||||||
|
const sessionExp = 60*60;
|
||||||
|
this.session[uCookie] = parsedCookie[uCookie];
|
||||||
|
this.session[uCookie].expires = new Date(Date.now() + sessionExp*1000);
|
||||||
|
this.session[uCookie]['Max-Age'] = sessionExp.toString();
|
||||||
|
cookieUpdated.push(uCookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(cookieUpdated.length > 0){
|
||||||
|
if(this.argv.debug){
|
||||||
|
console.log('[SAVING FILE]',`${this.sessCfg}.yml`);
|
||||||
|
}
|
||||||
|
yamlCfg.saveCRSession(this.session);
|
||||||
|
console.log(`[INFO] Cookies were updated! (${cookieUpdated.join(', ')})\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkCookieVal(chcookie: Record<string, string>){
|
||||||
|
return chcookie
|
||||||
|
&& chcookie.toString() == '[object Object]'
|
||||||
|
&& typeof chcookie.value == 'string'
|
||||||
|
? true : false;
|
||||||
|
}
|
||||||
|
checkSessId(session_id: Record<string, unknown>){
|
||||||
|
if(session_id && typeof session_id.expires == 'string'){
|
||||||
|
session_id.expires = new Date(session_id.expires);
|
||||||
|
}
|
||||||
|
return session_id
|
||||||
|
&& session_id.toString() == '[object Object]'
|
||||||
|
&& typeof session_id.expires == 'object'
|
||||||
|
&& Date.now() < new Date(session_id.expires as any).getTime()
|
||||||
|
&& typeof session_id.value == 'string'
|
||||||
|
? true : false;
|
||||||
|
}
|
||||||
|
uuidv4(){
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||||
|
let r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function buildProxy(proxyBaseUrl: string, proxyAuth: string){
|
||||||
|
if(!proxyBaseUrl.match(/^(https?|socks4|socks5):/)){
|
||||||
|
proxyBaseUrl = 'http://' + proxyBaseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
let proxyCfg = new URL(proxyBaseUrl);
|
||||||
|
let proxyStr = `${proxyCfg.protocol}//`;
|
||||||
|
|
||||||
|
if(typeof proxyCfg.hostname != 'string' || proxyCfg.hostname == ''){
|
||||||
|
throw new Error('[ERROR] Hostname and port required for proxy!');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(proxyAuth && typeof proxyAuth == 'string' && proxyAuth.match(':')){
|
||||||
|
proxyCfg.username = proxyAuth.split(':')[0];
|
||||||
|
proxyCfg.password = proxyAuth.split(':')[1];
|
||||||
|
proxyStr += `${proxyCfg.username}:${proxyCfg.password}@`;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxyStr += proxyCfg.hostname;
|
||||||
|
|
||||||
|
if(!proxyCfg.port && proxyCfg.protocol == 'http:'){
|
||||||
|
proxyStr += ':80';
|
||||||
|
}
|
||||||
|
else if(!proxyCfg.port && proxyCfg.protocol == 'https:'){
|
||||||
|
proxyStr += ':443';
|
||||||
|
}
|
||||||
|
|
||||||
|
return proxyStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
buildProxy,
|
||||||
|
usefulCookies,
|
||||||
|
Req,
|
||||||
|
};
|
||||||
|
|
||||||
Loading…
Reference in a new issue