Working typescript

This commit is contained in:
Izuco 2021-10-28 16:57:25 +02:00
parent 5abc0115df
commit 7f325e2835
No known key found for this signature in database
GPG key ID: 41DFCB1835A5695E
20 changed files with 946 additions and 196 deletions

1
.gitignore vendored
View file

@ -16,3 +16,4 @@ token.yml
*.srt
*.resume
*.user.yml
lib

4
@types/downloadedFile.d.ts vendored Normal file
View file

@ -0,0 +1,4 @@
export type DownloadedFile = {
path: string,
lang: string
}

392
@types/episode.d.ts vendored Normal file
View file

@ -0,0 +1,392 @@
// Generated by https://quicktype.io
export interface EpisodeData {
id: number;
title: string;
mediaDict: { [key: string]: string };
episodeSlug: string;
starRating: number;
parent: EpisodeDataParent;
number: string;
description: string;
filename: string;
seriesBanner: string;
media: Media[];
externalItemId: string;
contentId: string;
metaItems: MetaItems;
thumb: string;
type: Type;
default: { [key: string]: Default };
published: boolean;
versions: VersionClass[];
mediaCategory: string;
order: number;
seriesVersions: any[];
source: Source;
ids: EpisodeDataIDS;
runtime: string;
siblings: PreviousSeasonEpisode[];
seriesTitle: string;
seriesSlug: string;
next: Next;
previousSeasonEpisode: PreviousSeasonEpisode;
seasonTitle: string;
quality: Quality;
ratings: Array<string[]>;
languages: TitleElement[];
releaseDate: string;
historicalSelections: HistoricalSelections;
userRating: UserRating;
}
export interface Default {
items: DefaultItem[];
}
export interface DefaultItem {
languages: string[];
territories: string[];
version: null;
value: Value[];
devices: any[];
}
export interface Value {
name: MetaType;
value: string;
label: Label;
}
export enum Label {
Rating = "Rating",
RatingSystem = "Rating System",
ReleaseDate = "Release Date",
Synopsis = "Synopsis",
SynopsisType = "Synopsis Type",
}
export enum MetaType {
Rating = "rating",
RatingSystemType = "RatingSystemType",
ReleaseDate = "release-date",
Synopsis = "synopsis",
Synopsistype = "synopsistype",
VideoRatingType = "VideoRatingType",
}
export interface HistoricalSelections {
version: string;
language: string;
}
export interface EpisodeDataIDS {
externalShowId: string;
externalSeasonId: string;
externalEpisodeId: string;
}
export enum TitleElement {
Empty = "",
English = "English",
}
export interface Media {
id: number;
title: string;
experienceType: string;
created: string;
createdBy: string;
itemFieldData: Next;
keyPath: string;
filename: string;
complianceStatus: null;
events: any[];
clients: string[];
qcStatus: null;
qcStatusDate: null;
image: string;
thumb: string;
ext: string;
avails: Avail[];
version: string;
startTimecode: null;
endTimecode: null;
versionId: string;
mediaType: string;
status: string;
languages: LanguageClass[];
territories: any[];
devices: any[];
keyType: string;
purpose: null;
externalItemId: null | string;
proxyId: null;
externalDbId: null;
mediaChildren: MediaChild[];
isDefault: boolean;
parent: MediaChildParent;
filePath: null | string;
mediaInfo: Next;
type: string;
approved: boolean;
mediaKey: string;
itemFields: any[];
source: Source;
fieldData: Next;
sourceId: null | string;
timecodeOverride: null;
seriesTitle: string;
episodeTitle: string;
genre: any[];
txDate: string;
description: string;
synopsis: string;
resolution: null;
restrictedAccess: boolean;
createdById: string;
userIdsWithAccess: any[];
runtime?: number;
language?: TitleElement;
purchased: boolean;
}
export interface Avail {
id: number;
description: string;
endDate: string;
startDate: string;
ids: AvailIDS;
originalAirDate: null;
physicalReleaseDate: null;
preorderDate: null;
language: TitleElement;
territory: string;
territoryCode: string;
license: string;
parentAvail: null;
item: number;
version: string;
applyToLevel: null;
availLevel: string;
availDisplayCode: string;
availStatus: string;
bundleOnly: boolean;
contentOwnerOrganization: string;
currency: null;
price: null;
purchase: string;
priceValue: string;
resolutionFormat: null;
runtimeMilliseconds: null;
seasonOrEpisodeNumber: null;
tmsid: null;
deviceList: string;
tvodSku: null;
}
export interface AvailIDS {
externalSeasonId: string;
externalAsianId: null;
externalShowId: string;
externalEpisodeId: string;
externalEnglishId: string;
externalAlphaId: string;
}
export interface Next {
}
export interface LanguageClass {
code: string;
id: number;
title: TitleElement;
}
export interface MediaChild {
id: number;
title: string;
experienceType: string;
created: string;
createdBy: string;
itemFieldData: Next;
keyPath: null;
filename: string;
complianceStatus: null;
events: any[];
clients: string[];
qcStatus: null;
qcStatusDate: null;
image: string;
ext: string;
avails: any[];
version: string;
startTimecode: null;
endTimecode: null;
versionId: string;
mediaType: string;
status: string;
languages: LanguageClass[];
territories: any[];
devices: any[];
keyType: string;
purpose: null;
externalItemId: string;
proxyId: null;
externalDbId: null;
mediaChildren: any[];
isDefault: boolean;
parent: MediaChildParent;
filePath: string;
mediaInfo: MediaInfo;
type: string;
approved: boolean;
mediaKey: null;
itemFields: any[];
source: Source;
fieldData: Next;
sourceId: null;
timecodeOverride: null;
seriesTitle: string;
episodeTitle: string;
genre: any[];
txDate: string;
description: string;
synopsis: string;
resolution: null | string;
restrictedAccess: boolean;
createdById: string;
userIdsWithAccess: any[];
language: TitleElement;
}
export interface MediaInfo {
imageAspectRatio: null | string;
format: string;
scanMode: null | string;
burnedInSubtitleLanguage: string;
screenAspectRatio: null | string;
subtitleFormat: null | string;
subtitleContent: null | string;
frameHeight: number | null;
frameWidth: number | null;
video: Video;
}
export interface Video {
codecId: null | string;
container: null | string;
encodingRate: number | null;
frameRate: null | string;
height: number | null;
width: number | null;
duration: number | null;
bitRate: number | null;
}
export interface MediaChildParent {
title: string;
type: string;
catalogParent: CatalogParent;
slug: string;
grandparentId: number;
id: number;
}
export interface CatalogParent {
id: number;
title: string;
}
export enum Source {
Dbb = "dbb",
}
export interface MetaItems {
items: Items;
filters: Filters;
}
export interface Filters {
territory: any[];
language: any[];
}
export interface Items {
"release-date": AnimationProductionStudio;
rating: AnimationProductionStudio;
synopsis: AnimationProductionStudio;
"animation-production-studio": AnimationProductionStudio;
}
export interface AnimationProductionStudio {
items: AnimationProductionStudioItem[];
label: string;
id: number;
slug: string;
}
export interface AnimationProductionStudioItem {
id: number;
metaType: MetaType;
metaTypeId: string;
client: null;
languages: TitleElement;
territories: string;
devices: string;
isDefault: boolean;
value: Value[];
approved: boolean;
version: null;
source: Source;
}
export interface EpisodeDataParent {
seasonId: number;
seasonNumber: string;
title: string;
titleSlug: string;
titleType: string;
titleId: number;
}
export interface PreviousSeasonEpisode {
seasonTitle?: string;
mediaCategory: Type;
thumb: string;
title: string;
image: string;
number: string;
id: number;
version: string[];
order: number;
slug: string;
season?: number;
languages?: TitleElement[];
}
export enum Type {
Episode = "episode",
Ova = "ova",
}
export interface Quality {
quality: string;
height: number;
}
export interface UserRating {
overall: number;
ja: number;
eng: number;
}
export interface VersionClass {
compliance_approved: boolean;
title: string;
version_id: string;
is_default: boolean;
runtime: string;
external_id: string;
id: number;
}

View file

@ -2,9 +2,7 @@ declare module 'hls-download' {
export default class hlsDownload {
constructor(options: {
m3u8json: {
segments: {
}[],
segments: {}[],
mediaSequence?: number,
},
output?: string,
@ -16,13 +14,13 @@ declare module 'hls-download' {
skipInit?: boolean,
timeout?: number
})
async download() : {
async download() : Promise<{
ok: boolean,
parts: {
first: number,
total: number,
compleated: number
}
}
}>
}
}

75
@types/items.d.ts vendored
View file

@ -38,37 +38,11 @@ export interface Item {
mostRecentAvod: MostRecent;
}
export enum Access {
AVODSimulcastEnglish = "A-VOD_Simulcast_English",
AVODUncutEnglish = "A-VOD_Uncut_English",
SVODSimulcastEnglish = "SVOD_Simulcast_English",
SVODUncutEnglish = "SVOD_Uncut_English",
}
export enum AltAvail {
MostRecentSvodJpnUs = "most_recent_svod_jpn_us",
}
export enum Audio {
English = "English",
}
export enum ContentType {
Episode = "episode",
Ova = "ova",
}
export enum EngAllTerritoryAvail {
MostRecentSvodEngAllTerr = "most_recent_svod_eng_all_terr",
}
export enum Genre {
ActionAdventure = "Action/Adventure",
Comedy = "Comedy",
Drama = "Drama",
Fantasy = "Fantasy",
}
export interface IDs {
externalShowId: ID;
externalSeasonId: ExternalSeasonID;
@ -76,47 +50,26 @@ export interface IDs {
externalAsianId?: string
}
export enum ExternalSeasonID {
TrsS11 = "TRS-S1-1",
TrsS22 = "TRS-S2-2",
}
export enum ID {
Trs = "TRS",
}
export interface Item {
seasonTitle: SeasonTitle;
seasonTitle: string;
seasonId: number;
episodeOrder: number;
episodeSlug: string;
created: Date;
titleSlug: TitleSlug;
titleSlug: string;
episodeNum: string;
episodeId: number;
titleId: number;
seasonNum: string;
ratings: Array<string[]>;
showImage: string;
titleName: TitleName;
titleName: string;
runtime: string;
episodeName: string;
seasonOrder: number;
titleExternalId: ID;
titleExternalId: string;
}
export enum SeasonTitle {
Season1 = "Season 1",
Season2 = "Season 2",
}
export enum TitleName {
ThatTimeIGotReincarnatedAsASlime = "That Time I Got Reincarnated as a Slime",
}
export enum TitleSlug {
ThatTimeIGotReincarnatedAsASlime = "that-time-i-got-reincarnated-as-a-slime",
}
export interface MostRecent {
image?: string;
@ -147,14 +100,6 @@ export interface MostRecent {
purchased?: boolean;
}
export enum Device {
All = "All",
}
export enum Distributor {
FunimationVenue = "FunimationVenue",
}
export interface MostRecentAvodIDS {
externalSeasonId: ExternalSeasonID;
externalAsianId: null;
@ -171,14 +116,6 @@ export enum Purchase {
Svod = "SVOD",
}
export enum MostRecentAvodQuality {
HD1080 = "HD 1080",
}
export enum Territory {
Usa = "USA",
}
export enum Version {
Simulcast = "Simulcast",
Uncut = "Uncut",
@ -187,10 +124,6 @@ export enum Version {
export interface MostRecentSvodJpnUs {
}
export enum PrimaryAvail {
MostRecentSvodUs = "most_recent_svod_us",
}
export interface QualityClass {
quality: QualityQuality;
height: number;

View file

@ -16,7 +16,7 @@ declare module 'm3u8-parsed' {
timeline: number
}[],
version: number,
mediaGroups?: {
mediaGroups: {
[type: string]: {
[index: string]: {
[language: string]: {
@ -28,7 +28,7 @@ declare module 'm3u8-parsed' {
}
}
},
playlists?: {
playlists: {
uri: string,
timeline: number,
attributes: {

3
@types/pkg.d.ts vendored Normal file
View file

@ -0,0 +1,3 @@
declare module 'pkg' {
export async function exec(config: string[]);
}

3
@types/removeNPMAbsolutePaths.d.ts vendored Normal file
View file

@ -0,0 +1,3 @@
declare module 'removeNPMAbsolutePaths' {
export default async function modulesCleanup(path: string);
}

View file

@ -1,3 +1,5 @@
declare module 'sei-helper' {
export async function question(qStr: string): string;
export function cleanupFilename(str: string): string;
export function exec(str: string, str1: string, str2: string);
}

28
@types/streamData.d.ts vendored Normal file
View file

@ -0,0 +1,28 @@
// Generated by https://quicktype.io
export interface StreamData {
items: Item[];
watchHistorySaveInterval: number;
errors?: Error[]
}
export interface Error {
detail: string,
code: number
}
export interface Item {
src: string;
kind: string;
isPromo: boolean;
videoType: string;
aips: Aip[];
experienceId: string;
showAds: boolean;
id: number;
}
export interface Aip {
out: number;
in: number;
}

7
@types/subtitleObject.d.ts vendored Normal file
View file

@ -0,0 +1,7 @@
export type Subtitle = {
path: string,
ext: string,
langName: string,
language: string,
file?: string
}

201
funi.ts
View file

@ -5,7 +5,7 @@ import fs from 'fs';
import path from 'path';
// package json
const packageJson = JSON.parse(fs.readFileSync('./package.json').toString())
import packageJson from './package.json';
// program name
console.log(`\n=== Funimation Downloader NX ${packageJson.version} ===\n`);
@ -32,8 +32,12 @@ const argv = appYargs.appArgv(cfg.cli);
// Import modules after argv has been exported
import getData from './modules/module.getdata.js';
import merger from './modules/module.merger';
import merger, { SubtitleInput } from './modules/module.merger';
import parseSelect from './modules/module.parseSelect';
import { EpisodeData, MediaChild } from './@types/episode';
import { Subtitle } from './@types/subtitleObject';
import { StreamData } from './@types/streamData';
import { DownloadedFile } from './@types/downloadedFile';
// check page
argv.p = 1;
@ -41,11 +45,14 @@ argv.p = 1;
// fn variables
let title = '',
showTitle = '',
fnEpNum = 0,
fnOutput = [],
fnEpNum: string|number = 0,
fnOutput: string[] = [],
season = 0,
tsDlPath = [],
stDlPath = [];
tsDlPath: {
path: string,
lang: string,
}[] = [],
stDlPath: Subtitle[] = [];
// main
(async () => {
@ -81,7 +88,7 @@ async function auth(){
if(authData.ok && authData.res){
const resJSON = JSON.parse(authData.res.body);
if(resJSON.token){
console.log('[INFO] Authentication success, your token: %s%s\n', authData.token.slice(0,8),'*'.repeat(32));
console.log('[INFO] Authentication success, your token: %s%s\n', resJSON.token.slice(0,8),'*'.repeat(32));
yamlCfg.saveFuniToken({'token': resJSON.token});
} else {
console.log('[ERROR]%s\n', ' No token found');
@ -199,7 +206,10 @@ async function getShow(){
epSelList = parseSelect(argv.e as string);
let fnSlug = [], is_selected = false;
let fnSlug: {
title: string,
episode: string
}[] = [], is_selected = false;
let eps = epsDataArr;
epsDataArr.sort((a, b) => {
@ -226,21 +236,21 @@ async function getShow(){
is_selected = false;
}
// console vars
let tx_snum = eps[e].item.seasonNum==1?'':` S${eps[e].item.seasonNum}`;
let tx_snum = eps[e].item.seasonNum=='1'?'':` S${eps[e].item.seasonNum}`;
let tx_type = eps[e].mediaCategory != 'episode' ? eps[e].mediaCategory : '';
let tx_enum = eps[e].item.episodeNum && eps[e].item.episodeNum !== '' ?
`#${(eps[e].item.episodeNum < 10 ? '0' : '')+eps[e].item.episodeNum}` : '#'+eps[e].item.episodeId;
`#${(parseInt(eps[e].item.episodeNum) < 10 ? '0' : '')+eps[e].item.episodeNum}` : '#'+eps[e].item.episodeId;
let qua_str = eps[e].quality.height ? eps[e].quality.quality + eps[e].quality.height : 'UNK';
let aud_str = eps[e].audio.length > 0 ? `, ${eps[e].audio.join(', ')}` : '';
let rtm_str = eps[e].item.runtime !== '' ? eps[e].item.runtime : '??:??';
// console string
eps[e].id_split[0] = eps[e].id_split[0].padStart(typeIdLen, ' ');
eps[e].id_split[0] = eps[e].id_split[0].toString().padStart(typeIdLen, ' ');
epStrId = eps[e].id_split.join('');
let conOut = `[${epStrId}] `;
conOut += `${eps[e].item.titleName+tx_snum} - ${tx_type+tx_enum} ${eps[e].item.episodeName} `;
conOut += `(${rtm_str}) [${qua_str+aud_str}]`;
conOut += is_selected ? ' (selected)' : '';
conOut += eps.length-1 == e ? '\n' : '';
conOut += eps.length-1 == parseInt(e) ? '\n' : '';
console.log(conOut);
}
if(fnSlug.length < 1){
@ -256,17 +266,19 @@ async function getShow(){
}
async function getEpisode(fnSlug){
async function getEpisode(fnSlug: {
title: string,
episode: string
}) {
let episodeData = await getData({
baseUrl: api_host,
url: `/source/catalog/episode/${fnSlug.title}/${fnSlug.episode}/`,
token: token,
useToken: true,
useProxy: true,
debug: argv.debug,
});
if(!episodeData.ok){return;}
let ep = JSON.parse(episodeData.res.body).items[0], streamIds = [];
if(!episodeData.ok || !episodeData.res){return;}
let ep = JSON.parse(episodeData.res.body).items[0] as EpisodeData, streamIds = [];
// build fn
showTitle = ep.parent.title;
title = ep.title;
@ -296,7 +308,7 @@ async function getEpisode(fnSlug){
// map medias
let media = ep.media.map(function(m){
if(m.mediaType == 'experience'){
if(m.version.match(/uncut/i)){
if(m.version.match(/uncut/i) && m.language){
uncut[m.language] = true;
}
return {
@ -326,16 +338,24 @@ async function getEpisode(fnSlug){
let selected = false;
if(m.id > 0 && m.type == 'Non-Encrypted'){
let dub_type = m.language;
let localSubs = [];
let selUncut = !argv.simul && uncut[dub_type] && m.version.match(/uncut/i)
if (!dub_type)
continue;
let localSubs: Subtitle[] = [];
let selUncut = !argv.simul && uncut[dub_type] && m.version?.match(/uncut/i)
? true
: (!uncut[dub_type] || argv.simul && m.version.match(/simulcast/i) ? true : false);
for (let curDub of argv.dub) {
: (!uncut[dub_type] || argv.simul && m.version?.match(/simulcast/i) ? true : false);
for (let 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))
});
if (!m.subtitles) {
console.log('[ERROR] Unable to find subs for episode ', m.id)
if (argv.debug)
console.log(m)
continue;
}
stDlPath.push(...m.subtitles);
localSubs = m.subtitles;
selected = true;
@ -347,7 +367,7 @@ async function getEpisode(fnSlug){
}
}
let already = [];
let already: string[] = [];
stDlPath = stDlPath.filter(a => {
if (already.includes(a.language)) {
return false;
@ -369,20 +389,19 @@ async function getEpisode(fnSlug){
token: token,
dinstid: 'uuid',
useToken: true,
useProxy: true,
debug: argv.debug,
});
if(!streamData.ok){return;}
streamData = JSON.parse(streamData.res.body);
if(streamData.errors){
console.log('[ERROR] Error #%s: %s\n',streamData.errors[0].code,streamData.errors[0].detail);
if(!streamData.ok || !streamData.res){return;}
const streamDataRes = JSON.parse(streamData.res.body) as StreamData;
if(streamDataRes.errors){
console.log('[ERROR] Error #%s: %s\n',streamDataRes.errors[0].code,streamDataRes.errors[0].detail);
return;
}
else{
for(let u in streamData.items){
if(streamData.items[u].videoType == 'm3u8'){
for(let u in streamDataRes.items){
if(streamDataRes.items[u].videoType == 'm3u8'){
tsDlPath.push({
path: streamData.items[u].src,
path: streamDataRes.items[u].src,
lang: streamId.lang
});
break;
@ -400,12 +419,12 @@ async function getEpisode(fnSlug){
}
}
function getSubsUrl(m){
function getSubsUrl(m: MediaChild[]) : Subtitle[] {
if(argv.nosubs && !argv.sub){
return [];
}
let subLangs = argv.subLang;
let subLangs = argv.subLang as appYargs.possibleSubs;
const subType = {
'enUS': 'English',
@ -419,7 +438,7 @@ function getSubsUrl(m){
subLangs = [ 'enUS' ];
}
let found = [];
let found: Subtitle[] = [];
for(let i in m){
let fpp = m[i].filePath.split('.');
@ -443,16 +462,15 @@ async function downloadStreams(){
// req playlist
let purvideo = [];
let puraudio = [];
let audioAndVideo = [];
let purvideo: DownloadedFile[] = [];
let puraudio: DownloadedFile[] = [];
let audioAndVideo: DownloadedFile[] = [];
for (let streamPath of tsDlPath) {
let plQualityReq = await getData({
url: streamPath.path,
useProxy: (argv.ssp ? false : true),
debug: argv.debug,
});
if(!plQualityReq.ok){return;}
if(!plQualityReq.ok || !plQualityReq.res){return;}
let plQualityLinkList = m3u8(plQualityReq.res.body);
@ -463,27 +481,43 @@ async function downloadStreams(){
'funiprod.akamaized.net',
];
let plServerList = [],
plStreams = {},
let plServerList: string[] = [],
plStreams: Record<string|number, {
[key: string]: string
}> = {},
plLayersStr = [],
plLayersRes = {},
plLayersRes: Record<string|number, {
width: number,
height: number
}> = {},
plMaxLayer = 1,
plNewIds = 1,
plAud = { uri: '' };
plAud: {
uri: string,
langStr: string,
language: string
} = { uri: '', langStr: '', language: '' };
// new uris
let vplReg = /streaming_video_(\d+)_(\d+)_(\d+)_index\.m3u8/;
if(plQualityLinkList.playlists[0].uri.match(vplReg)){
let audioKey = Object.keys(plQualityLinkList.mediaGroups.AUDIO).pop();
if (!audioKey)
return console.log('[ERROR] No audio key found')
if(plQualityLinkList.mediaGroups.AUDIO[audioKey]){
let audioData = plQualityLinkList.mediaGroups.AUDIO[audioKey],
audioEl = Object.keys(audioData);
audioData = audioData[audioEl[0]];
const audioDataParts = plQualityLinkList.mediaGroups.AUDIO[audioKey],
audioEl = Object.keys(audioDataParts);
const audioData = audioDataParts[audioEl[0]];
plAud = { ...audioData, ...{ langStr: audioEl[0] } };
}
plQualityLinkList.playlists.sort((a, b) => {
let av = parseInt(a.uri.match(vplReg)[3]);
let bv = parseInt(b.uri.match(vplReg)[3]);
const aMatch = a.uri.match(vplReg), bMatch = b.uri.match(vplReg);
if (!aMatch || !bMatch) {
console.log('[ERROR] Unable to match')
return 0;
}
let av = parseInt(aMatch[3]);
let bv = parseInt(bMatch[3]);
if(av > bv){
return 1;
}
@ -497,9 +531,10 @@ async function downloadStreams(){
for(let s of plQualityLinkList.playlists){
if(s.uri.match(/_Layer(\d+)\.m3u8/) || s.uri.match(vplReg)){
// set layer and max layer
let plLayerId = 0;
if(s.uri.match(/_Layer(\d+)\.m3u8/)){
plLayerId = parseInt(s.uri.match(/_Layer(\d+)\.m3u8/)[1]);
let plLayerId: number|string = 0;
const match = s.uri.match(/_Layer(\d+)\.m3u8/);
if(match){
plLayerId = parseInt(match[1]);
}
else{
plLayerId = plNewIds, plNewIds++;
@ -546,10 +581,7 @@ async function downloadStreams(){
break;
}
}
if(typeof argv.q == 'object' && argv.q.length > 1){
argv.q = argv.q[argv.q.length-1];
}
argv.q = argv.q < 1 || argv.q > plMaxLayer ? plMaxLayer : argv.q;
@ -567,7 +599,7 @@ async function downloadStreams(){
fnOutput = parseFileName(argv.fileName, title, fnEpNum, showTitle, season, plLayersRes[argv.q].width, plLayersRes[argv.q].height);
if (fnOutput.length < 1)
throw new Error('Invalid path', fnOutput);
throw new Error(`Invalid path generated for input ${argv.fileName}`);
console.log(`[INFO] Output filename: ${fnOutput.join(path.sep)}.ts`);
}
else if(argv.x > plServerList.length){
@ -593,10 +625,9 @@ async function downloadStreams(){
// download video
let reqVideo = await getData({
url: videoUrl,
useProxy: (argv.ssp ? false : true),
debug: argv.debug,
});
if (!reqVideo.ok) { break video; }
if (!reqVideo.ok || !reqVideo.res) { break video; }
let chunkList = m3u8(reqVideo.res.body);
@ -625,10 +656,9 @@ async function downloadStreams(){
break audio;
let reqAudio = await getData({
url: plAud.uri,
useProxy: (argv.ssp ? false : true),
debug: argv.debug,
});
if (!reqAudio.ok) { return; }
if (!reqAudio.ok || !reqAudio.res) { return; }
let chunkListA = m3u8(reqAudio.res.body);
@ -654,10 +684,9 @@ async function downloadStreams(){
for (let subObject of stDlPath) {
let subsSrc = await getData({
url: subObject.path,
useProxy: true,
debug: argv.debug,
});
if(subsSrc.ok){
if(subsSrc.ok && subsSrc.res){
let assData = vttConvert(subsSrc.res.body, (subsExt == '.srt' ? true : false), subObject.langName, argv.fontSize);
subObject.file = path.join(cfg.dir.content, ...fnOutput.slice(0, -1), `${fnOutput.slice(-1)}.subtitle${subObject.ext}${subsExt}`);
fs.writeFileSync(subObject.file, assData);
@ -683,7 +712,7 @@ async function downloadStreams(){
}
// check exec
const mergerBin = await merger.checkMerger(cfg.bin, argv.mp4);
const mergerBin = merger.checkMerger(cfg.bin, argv.mp4);
if ( argv.novids ){
console.log('[INFO] Video not downloaded. Skip muxing video.');
@ -697,16 +726,22 @@ async function downloadStreams(){
console.log('[WARN] FFmpeg not found...');
}
const ffext = !argv.mp4 ? 'mkv' : 'mp4';
const mergeInstance = new merger({
onlyAudio: puraudio,
onlyVid: purvideo,
output: `${path.join(cfg.dir.content, ...fnOutput)}.${ffext}`,
subtitels: stDlPath as SubtitleInput[],
videoAndAudio: audioAndVideo,
simul: argv.simul
})
if(!argv.mp4 && mergerBin.MKVmerge){
let ffext = !argv.mp4 ? 'mkv' : 'mp4';
let command = merger.buildCommandMkvMerge(argv.simul, audioAndVideo, purvideo, puraudio, stDlPath, `${path.join(cfg.dir.content,
...fnOutput)}.${ffext}`);
let command = mergeInstance.MkvMerge();
shlp.exec('mkvmerge', `"${mergerBin.MKVmerge}"`, command);
}
else if(mergerBin.FFmpeg){
let ffext = !argv.mp4 ? 'mkv' : 'mp4';
let command = merger.buildCommandFFmpeg(argv.simul, audioAndVideo, purvideo, puraudio, stDlPath, `${path.join(cfg.dir.content,
...fnOutput)}.${ffext}`);
let command = mergeInstance.FFmpeg();
shlp.exec('ffmpeg',`"${mergerBin.FFmpeg}"`,command);
}
else{
@ -717,34 +752,28 @@ async function downloadStreams(){
return;
audioAndVideo.concat(puraudio).concat(purvideo).forEach(a => fs.unlinkSync(a.path));
stDlPath.forEach(subObject => fs.unlinkSync(subObject.file));
stDlPath.forEach(subObject => subObject.file && fs.unlinkSync(subObject.file));
console.log('\n[INFO] Done!\n');
}
async function downloadFile(filename, chunkList) {
async function downloadFile(filename: string, chunkList: {
segments: {}[],
}) {
const downloadStatus = await new hlsDownload({
m3u8json: chunkList,
output: `${filename + '.ts'}`,
timeout: argv.timeout,
pcount: argv.partsize
threads: argv.partsize
}).download();
return downloadStatus.ok;
}
/**
* @param {string} input
* @param {string} title
* @param {number|string} episode
* @param {string} showTitle
* @param {number} season
* @param {number} width
* @param {number} height
* @returns {Array<string>}
*/
function parseFileName(input, title, episode, showTitle, season, width, height) {
function parseFileName(input: string, title: string, episode:number|string, showTitle: string, season: number, width: number, height: number): string[] {
const varRegex = /\${[A-Za-z1-9]+}/g;
const vars = input.match(varRegex);
if (!vars)
return [input];
for (let i = 0; i < vars.length; i++) {
const type = vars[i];
switch (type.slice(2, -1).toLowerCase()) {
@ -754,7 +783,7 @@ function parseFileName(input, title, episode, showTitle, season, width, height)
case 'episode': {
if (typeof episode === 'number') {
let len = episode.toFixed(0).toString().length;
input = input.replace(vars[i], len < argv.numbers ? '0'.repeat(argv.numbers - len) + episode : episode);
input = input.replace(vars[i], len < argv.numbers ? '0'.repeat(argv.numbers - len) + episode : episode.toString());
} else {
input = input.replace(vars[i], episode);
}
@ -765,14 +794,14 @@ function parseFileName(input, title, episode, showTitle, season, width, height)
break;
case 'season': {
let len = season.toFixed(0).toString().length;
input = input.replace(vars[i], len < argv.numbers ? '0'.repeat(argv.numbers - len) + season : season);
input = input.replace(vars[i], len < argv.numbers ? '0'.repeat(argv.numbers - len) + season : season.toString());
break;
}
case 'width':
input = input.replace(vars[i], width);
input = input.replace(vars[i], width.toString());
break;
case 'height':
input = input.replace(vars[i], height);
input = input.replace(vars[i], height.toString());
break;
default:
break;

View file

@ -1,10 +1,10 @@
#!/usr/bin/env node
// build requirements
const pkg = require('../package.json');
const fs = require('fs-extra');
const modulesCleanup = require('removeNPMAbsolutePaths');
const { exec } = require('pkg');
import fs from 'fs-extra';
import pkg from '../package.json';
import modulesCleanup from 'removeNPMAbsolutePaths';
import { exec } from 'pkg';
const buildsDir = './_builds';
const nodeVer = 'node14-';
@ -50,6 +50,7 @@ const nodeVer = 'node14-';
fs.copySync('./config/dir-path.yml', `${buildDir}/config/dir-path.yml`);
fs.copySync('./modules/cmd-here.bat', `${buildDir}/cmd-here.bat`);
fs.copySync('./modules/NotoSans-Regular.ttf', `${buildDir}/NotoSans-Regular.ttf`);
fs.copySync('./package.json', `${buildDir}/package.json`)
fs.copySync('./docs/', `${buildDir}/docs/`);
fs.copySync('./LICENSE.md', `${buildDir}/docs/LICENSE.md`);
if(fs.existsSync(`${buildsDir}/${buildFull}.7z`)){
@ -58,7 +59,7 @@ const nodeVer = 'node14-';
require('child_process').execSync(`7z a -t7z "${buildsDir}/${buildFull}.7z" "${buildDir}"`,{stdio:[0,1,2]});
}());
function getTarget(bt){
function getTarget(bt: string) : string {
switch(bt){
case 'win64':
return 'windows-x64';

View file

@ -9,20 +9,26 @@ const availableFilenameVars = [
'height'
];
const subLang = ['enUS', 'esLA', 'ptBR'];
const dubLang = ['enUS', 'esLA', 'ptBR', 'zhMN', 'jaJP'];
export type possibleDubs = (
'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'];
const appArgv = (cfg: {
[key: string]: unknown
}) => {
// init
const parseDefault = (key: string, _default: unknown) => {
const parseDefault = <T = unknown>(key: string, _default: T) : T=> {
if (Object.prototype.hasOwnProperty.call(cfg, key)) {
return cfg[key];
return cfg[key] as T;
} else
return _default;
};
const argv = yargs.parserConfiguration({
'duplicate-arguments-array': true,
'camel-case-expansion': false
@ -59,27 +65,27 @@ const appArgv = (cfg: {
group: 'Downloading:',
describe: 'Used to download all episodes from the show',
type: 'boolean',
default: parseDefault('all', false)
default: parseDefault<boolean>('all', false)
})
.option('partsize', {
group: 'Downloading:',
describe: 'The amount of parts that should be downloaded in paralell',
type: 'number',
default: parseDefault('partsize', 10)
default: parseDefault<number>('partsize', 10)
})
// quality
.option('q', {
group: 'Downloading:',
describe: 'Select video layer (0 is max)',
choices: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
default: parseDefault('videoLayer', 7),
default: parseDefault<number>('videoLayer', 7),
type: 'number',
})
// alt listing
.option('alt', {
group: 'Downloading:',
describe: 'Alternative episode listing (if available)',
default: parseDefault('altList', false),
default: parseDefault<boolean>('altList', false),
type: 'boolean',
})
// switch to subs
@ -87,20 +93,20 @@ const appArgv = (cfg: {
group: 'Downloading:',
describe: 'Download non-Japanese Dub (English Dub mode by default)',
choices: dubLang,
default: parseDefault('dub', 'enUS'),
default: parseDefault<possibleDubs>('dub', ['enUS']),
type: 'array',
})
.option('subLang', {
group: 'Downloading:',
describe: 'Set the subtitle language (English is default and fallback)',
default: parseDefault('subLang', 'enUS'),
default: parseDefault<possibleSubs>('subLang', ['enUS']),
choices: subLang,
type: 'array'
})
.option('fontSize', {
group: 'Downloading:',
describe: 'Used to set the fontsize of the subtitles',
default: parseDefault('fontSize', 55),
default: parseDefault<number>('fontSize', 55),
type: 'number'
})
.option('allSubs', {
@ -119,7 +125,7 @@ const appArgv = (cfg: {
.option('simul', {
group: 'Downloading:',
describe: 'Force downloading simulcast ver. instead of uncut ver. (if uncut ver. available)',
default: parseDefault('forceSimul', false),
default: parseDefault<boolean>('forceSimul', false),
type: 'boolean',
})
// server number
@ -128,7 +134,7 @@ const appArgv = (cfg: {
group: 'Downloading:',
describe: 'Select server',
choices: [1, 2, 3, 4],
default: parseDefault('nServer', 1),
default: parseDefault<number>('nServer', 1),
type: 'number',
})
// skip
@ -153,19 +159,19 @@ const appArgv = (cfg: {
.option('proxy', {
group: 'Proxy:',
describe: 'Set http(s)/socks proxy WHATWG url',
default: parseDefault('proxy', false),
default: parseDefault<boolean>('proxy', false),
hidden: true,
})
.option('proxy-auth', {
group: 'Proxy:',
describe: 'Colon-separated username and password for proxy',
default: parseDefault('proxy_auth', false),
default: parseDefault<string|boolean>('proxy_auth', false),
hidden: true,
})
.option('ssp', {
group: 'Proxy:',
describe: 'Don\'t use proxy for stream and subtitles downloading',
default: parseDefault('proxy_ssp', false),
default: parseDefault<boolean>('proxy_ssp', false),
hidden: true,
type: 'boolean',
})
@ -178,7 +184,7 @@ const appArgv = (cfg: {
.option('mp4', {
group: 'Muxing:',
describe: 'Mux into mp4',
default: parseDefault('mp4mux', false),
default: parseDefault<boolean>('mp4mux', false),
type: 'boolean'
})
// filenaming
@ -187,20 +193,20 @@ const appArgv = (cfg: {
describe: `Set the filename template. Use \${variable_name} to insert variables.\nYou may use ${availableFilenameVars
.map(a => `'${a}'`).join(', ')} as variables.`,
type: 'string',
default: parseDefault('fileName', '[Funimation] ${showTitle} - ${episode} [${height}p]')
default: parseDefault<string>('fileName', '[Funimation] ${showTitle} - ${episode} [${height}p]')
})
.option('numbers', {
group: 'Filename Template:',
describe: `Set how long a number in the title should be at least.\n${[[3, 5, '005'], [2, 1, '01'], [1, 20, '20']]
.map(val => `Set in config: ${val[0]}; Episode number: ${val[1]}; Output: ${val[2]}`).join('\n')}`,
type: 'number',
default: parseDefault('numbers', 2)
default: parseDefault<number>('numbers', 2)
})
// util
.option('nocleanup', {
group: 'Utilities:',
describe: 'Dont\'t delete the input files after muxing',
default: parseDefault('noCleanUp', false),
default: parseDefault<boolean>('noCleanUp', false),
type: 'boolean'
})
.option('timeout', {

View file

@ -26,7 +26,7 @@ export type Options = {
},
useToken?: boolean,
token?: string|boolean,
dinstid?: boolean,
dinstid?: boolean|string,
debug?: boolean
}
const getData = async <T = string>(options: Options) => {

View file

@ -177,6 +177,29 @@ class Merger {
return args.join(' ');
};
public static checkMerger(bin: {
mkvmerge?: string,
ffmpeg?: string
}, useMP4format: boolean) {
const merger: {
MKVmerge: undefined|string|false,
FFmpeg: undefined|string|false
} = {
MKVmerge: bin.mkvmerge,
FFmpeg: bin.ffmpeg,
};
if( !useMP4format && !merger.MKVmerge ){
console.log('[WARN] MKVMerge not found, skip using this...');
merger.MKVmerge = false;
}
if( !merger.MKVmerge && !merger.FFmpeg || useMP4format && !merger.FFmpeg ){
console.log('[WARN] FFmpeg not found, skip using this...');
merger.FFmpeg = false;
}
return merger;
}
}
export default Merger;

247
package-lock.json generated
View file

@ -29,6 +29,7 @@
"eslint": "^7.30.0",
"pkg": "^5.3.3",
"removeNPMAbsolutePaths": "^2.0.0",
"ts-node": "^10.4.0",
"typescript": "^4.4.4"
}
},
@ -169,6 +170,27 @@
"to-fast-properties": "^2.0.0"
}
},
"node_modules/@cspotcode/source-map-consumer": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz",
"integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==",
"dev": true,
"engines": {
"node": ">= 12"
}
},
"node_modules/@cspotcode/source-map-support": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz",
"integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==",
"dev": true,
"dependencies": {
"@cspotcode/source-map-consumer": "0.8.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@eslint/eslintrc": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz",
@ -274,6 +296,30 @@
"node": ">= 6"
}
},
"node_modules/@tsconfig/node10": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",
"integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==",
"dev": true
},
"node_modules/@tsconfig/node12": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz",
"integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==",
"dev": true
},
"node_modules/@tsconfig/node14": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz",
"integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==",
"dev": true
},
"node_modules/@tsconfig/node16": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz",
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
"dev": true
},
"node_modules/@types/cacheable-request": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz",
@ -569,6 +615,15 @@
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
"node_modules/acorn-walk": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
"dev": true,
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@ -643,6 +698,12 @@
"readable-stream": "^2.0.6"
}
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true
},
"node_modules/argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@ -927,6 +988,12 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@ -1128,6 +1195,15 @@
"node": ">=0.10"
}
},
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true,
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@ -2334,6 +2410,12 @@
"global": "^4.4.0"
}
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -3561,6 +3643,59 @@
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
"dev": true
},
"node_modules/ts-node": {
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz",
"integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==",
"dev": true,
"dependencies": {
"@cspotcode/source-map-support": "0.7.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"yn": "3.1.1"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-cwd": "dist/bin-cwd.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
},
"peerDependencies": {
"@swc/core": ">=1.2.50",
"@swc/wasm": ">=1.2.50",
"@types/node": "*",
"typescript": ">=2.7"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
},
"@swc/wasm": {
"optional": true
}
}
},
"node_modules/ts-node/node_modules/acorn": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz",
"integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
},
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/tslib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
@ -3861,6 +3996,15 @@
"engines": {
"node": ">=10"
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true,
"engines": {
"node": ">=6"
}
}
},
"dependencies": {
@ -3973,6 +4117,21 @@
"to-fast-properties": "^2.0.0"
}
},
"@cspotcode/source-map-consumer": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz",
"integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==",
"dev": true
},
"@cspotcode/source-map-support": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz",
"integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==",
"dev": true,
"requires": {
"@cspotcode/source-map-consumer": "0.8.0"
}
},
"@eslint/eslintrc": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz",
@ -4051,6 +4210,30 @@
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw=="
},
"@tsconfig/node10": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",
"integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==",
"dev": true
},
"@tsconfig/node12": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz",
"integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==",
"dev": true
},
"@tsconfig/node14": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz",
"integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==",
"dev": true
},
"@tsconfig/node16": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz",
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
"dev": true
},
"@types/cacheable-request": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz",
@ -4251,6 +4434,12 @@
"dev": true,
"requires": {}
},
"acorn-walk": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
"dev": true
},
"agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@ -4306,6 +4495,12 @@
"readable-stream": "^2.0.6"
}
},
"arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
@ -4519,6 +4714,12 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
"create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
"cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@ -4656,6 +4857,12 @@
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=",
"dev": true
},
"diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true
},
"dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@ -5608,6 +5815,12 @@
"global": "^4.4.0"
}
},
"make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@ -6534,6 +6747,34 @@
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
"dev": true
},
"ts-node": {
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz",
"integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==",
"dev": true,
"requires": {
"@cspotcode/source-map-support": "0.7.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"yn": "3.1.1"
},
"dependencies": {
"acorn": {
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz",
"integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==",
"dev": true
}
}
},
"tslib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
@ -6759,6 +7000,12 @@
"version": "20.2.9",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="
},
"yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"dev": true
}
}
}

View file

@ -44,12 +44,17 @@
"eslint": "^7.30.0",
"pkg": "^5.3.3",
"removeNPMAbsolutePaths": "^2.0.0",
"ts-node": "^10.4.0",
"typescript": "^4.4.4"
},
"scripts": {
"build-win64": "node modules/build win64",
"build-linux64": "node modules/build linux64",
"build-macos64": "node modules/build macos64",
"tsc": "ts-node tsc.ts",
"prebuild-win64": "npm run tsc",
"prebuild-linux64": "npm run tsc",
"prebuild-maxos64": "npm run tsc",
"build-win64": "cd lib && node modules/build win64",
"build-linux64": "cd lib && node modules/build linux64",
"build-macos64": "cd lib && node modules/build macos64",
"eslint": "eslint *.js modules",
"eslint-fix": "eslint *.js modules --fix",
"test": "echo \"Error: no test specified\" && exit 1"

63
tsc.ts Normal file
View file

@ -0,0 +1,63 @@
import { exec } from "child_process";
import fs from "fs";
import path from "path";
import { removeSync, copyFileSync } from "fs-extra";
const ignore = [
'.git',
'lib',
'node_modules',
'@types'
].map(a => path.join(__dirname, a));
(async () => {
removeSync('lib');
const tsc = exec('npx tsc');
tsc.stdout?.on("data", console.log);
tsc.stderr?.on("data", console.log);
tsc.on("close", () => {
const files = readDir(__dirname);
const filtered = files.filter(a => {
if (a.stats.isFile()) {
return a.path.split('.').pop() !== 'ts';
} else {
return true
}
})
filtered.forEach(item => {
const itemPath = path.join(__dirname, 'lib', item.path.replace(__dirname, ''));
if (item.stats.isDirectory()) {
if (!fs.existsSync(itemPath))
fs.mkdirSync(itemPath)
} else {
copyFileSync(item.path, itemPath)
}
})
})
})()
const readDir = (dir: string) : {
path: string,
stats: fs.Stats
}[] => {
const items: {
path: string,
stats: fs.Stats
}[] = [];
const content = fs.readdirSync(dir);
for (const item of content) {
const itemPath = path.join(dir, item);
if (ignore.some(a => itemPath.startsWith(a)))
continue;
const stats = fs.statSync(itemPath);
items.push({
path: itemPath,
stats
});
if (stats.isDirectory()) {
items.push(...readDir(itemPath))
}
}
return items;
}

View file

@ -14,7 +14,7 @@
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
"outDir": "./lib", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
@ -63,7 +63,12 @@
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"resolveJsonModule": true,
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
},
"exclude": [
"./videos",
"./tsc.ts"
]
}