Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * JDownloader2 EventScript - 新規リンク登録時に同一タイトルでグループ化するスクリプト
- *
- * 【概要】
- *
- * 新規リンク登録時に名前の文字列内にある括弧"()","[]"無視して
- * グループ化してパッケージに移動する
- *
- * 【説明】
- *
- * JDにURLを登録する際、カテゴリや作者名が違ったり無かったりすれば、
- * 同じタイトルであってもグループ化されず、別のパッケージに登録される。
- *
- * このスクリプトでは、リンクやパッケージの名前の()内、[]内を無視し
- * 他の部分が一致すれば一つのパッケージにグループ化します。
- * (つまりカテゴリ名や作者名を無視し、タイトルだけでグループ化する)
- *
- * 書庫ファイルや電子書籍ファイル、動画ファイルのみグループ化します
- * (zip,rar,cbz,cbr,pdf,epub,azw3,mp4,mkv,webm,avi,wmv)
- *
- * ※JDに登録する際に、常に命名規則に則った
- * 名前付けをしていることが前提となる
- *
- * "(カテゴリ) [作者名] タイトル... .rar"
- *
- * ※意図しない動作になった場合を考え
- * このスクリプトでは「リンクのリネーム」は行わない
- * 「パッケージ名変更」と「リンクの移動」のみ行う
- *
- *
- * 【使い方】
- *
- * 『Synchronous execution of script』 OFF
- * Synchronous(同期)をオフ、
- * !!!非同期にしておかないとイベント時にJDが固まる!!!
- *
- * 『Trigger』 "A new link has been added" (ON_NEW_LINK)
- *
- *
- * 新規に追加されたリンクの名前に基づいて
- * 既存のパッケージ名、もしくはリンク名(※)の
- * タイトル部分が同一であれば
- * それらを含めてグループ化、つまり一つのパッケージにまとめる
- *
- * 最終パッケージ名
- * 作者名(英数のみ)よりも作者名(英数以外含む)を優先して採用
- * カテゴリはパッケージとリンクの名前から列挙し
- * 優先順位に従って採用(category_listのリスト昇順)
- *
- *
- * ※ 既存のリンクが対象となるのは
- * 自動生成されたパッケージ内のリンクのみ
- *
- *
- * 2024/12/06 "epub" 末尾有る無しはサイズチェック(誤差範囲6%)する
- *
- */
- /**
- * @type {string[]} カテゴリに使用する文字列のリスト(優先順) (追加、削除可能)
- */
- const category_list = [
- '18禁アニメ',
- '同人アニメ',
- '同人CG集',
- '同人誌',
- '成年コミック',
- '一般小説',
- '一般コミック・少女',
- '一般コミック',
- '一般書籍'
- ];
- /**
- * @type {string[]} グループ化するリンクの拡張子 (追加、削除可能)
- */
- const targetExtensions = [
- 'rar',
- 'zip',
- '7z',
- 'cbz',
- 'cbr',
- 'pdf',
- 'epub',
- 'azw3',
- 'mp4',
- 'mkv',
- 'webm',
- 'avi',
- 'wmv'
- ];
- ///////////////////////////////////////////////////////////////////////////
- const regexp_isTargetFileExt = new RegExp('(?:^[^\.]+$)|(?:\.(?:'+ targetExtensions.join('|') + ')$)', 'i');
- /**
- * ファイル名に拡張子が無いか、もしくはリストtargetExtensions内にそれが存在するか、否か
- *
- * @param {string} n ファイル名
- * @returns {boolean} 拡張子が無い、もしくは拡張子リストtargetExtensions内に存在すればtrue、否ならfalse
- */
- function isTargetFileExt(n)
- {
- // return /(?:^[^\.]+$)|(?:\.(?:rar|zip|7z|cbz|cbr|pdf|epub|azw3|mp4|mkv|webm|avi|wmv)$)/i.test(n);
- return regexp_isTargetFileExt.test(n);
- }
- /**
- * 文字列の正規化
- *
- * @param {string} s 変換前の文字列
- * @returns {string} 変換後の文字列
- */
- function normalizeTitle(s)
- {
- if (s==null||s=="") return s;
- const NT1 = {
- //基本1文字からの変換
- "\!":"!","\"":"”","\$":"$","\%":"%","\&":"&","'":"’","~":" ̄","|":"|",
- "*":"*",":":":","<":"<",">":">","?":"?","/":"/","\\":"¥","`":"‘",",":","
- //特殊濁点半濁点→日本語濁点半濁点(分離)
- //"\u3099":"\u309B","\u309A":"\u309C"
- };
- const NT2 = {
- //記号
- " ":" ","〜":"~","×":"+","&":"&","♡":"","♥":"","―":"-","─":"-","━":"-",
- //半角カナ→全角カナ
- 'ガ': 'ガ', 'ギ': 'ギ', 'グ': 'グ', 'ゲ': 'ゲ', 'ゴ': 'ゴ',
- 'ザ': 'ザ', 'ジ': 'ジ', 'ズ': 'ズ', 'ゼ': 'ゼ', 'ゾ': 'ゾ',
- 'ダ': 'ダ', 'ヂ': 'ヂ', 'ヅ': 'ヅ', 'デ': 'デ', 'ド': 'ド',
- 'バ': 'バ', 'ビ': 'ビ', 'ブ': 'ブ', 'ベ': 'ベ', 'ボ': 'ボ',
- 'パ': 'パ', 'ピ': 'ピ', 'プ': 'プ', 'ペ': 'ペ', 'ポ': 'ポ',
- 'ヴ': 'ヴ', 'ヷ': 'ヷ', 'ヺ': 'ヺ',
- 'ア': 'ア', 'イ': 'イ', 'ウ': 'ウ', 'エ': 'エ', 'オ': 'オ',
- 'カ': 'カ', 'キ': 'キ', 'ク': 'ク', 'ケ': 'ケ', 'コ': 'コ',
- 'サ': 'サ', 'シ': 'シ', 'ス': 'ス', 'セ': 'セ', 'ソ': 'ソ',
- 'タ': 'タ', 'チ': 'チ', 'ツ': 'ツ', 'テ': 'テ', 'ト': 'ト',
- 'ナ': 'ナ', 'ニ': 'ニ', 'ヌ': 'ヌ', 'ネ': 'ネ', 'ノ': 'ノ',
- 'ハ': 'ハ', 'ヒ': 'ヒ', 'フ': 'フ', 'ヘ': 'ヘ', 'ホ': 'ホ',
- 'マ': 'マ', 'ミ': 'ミ', 'ム': 'ム', 'メ': 'メ', 'モ': 'モ',
- 'ヤ': 'ヤ', 'ユ': 'ユ', 'ヨ': 'ヨ',
- 'ラ': 'ラ', 'リ': 'リ', 'ル': 'ル', 'レ': 'レ', 'ロ': 'ロ',
- 'ワ': 'ワ', 'ヲ': 'ヲ', 'ン': 'ン',
- 'ァ': 'ァ', 'ィ': 'ィ', 'ゥ': 'ゥ', 'ェ': 'ェ', 'ォ': 'ォ',
- 'ッ': 'ッ', 'ャ': 'ャ', 'ュ': 'ュ', 'ョ': 'ョ',
- '。': '。', '、': '、', 'ー': 'ー', '「': '「', '」': '」', '・': '・',
- //よくある簡体字、繁体字→日本語常用漢字
- '卷':'巻','學':'学','黑':'黒','關':'関','繪':'絵','會':'会','亞':'亜','髙':'高','說':'説','户':'戸',
- '說':'説','说':'説' //繁体字、簡体字の"説"
- };
- //濁点半濁点結合
- const NT3={
- 'ウ゛':'ヴ','ワ゛':'ヷ','ヲ゛':'ヺ',
- 'カ゛':'ガ','キ゛':'ギ','ク゛':'グ','ケ゛':'ゲ','コ゛':'ゴ',
- 'サ゛':'ザ','シ゛':'ジ','ス゛':'ズ','セ゛':'ゼ','ソ゛':'ゾ',
- 'タ゛':'ダ','チ゛':'ヂ','ツ゛':'ヅ','テ゛':'デ','ト゛':'ド',
- 'ハ゛':'バ','ヒ゛':'ビ','フ゛':'ブ','ヘ゛':'ベ','ホ゛':'ボ',
- 'ハ゜':'パ','ヒ゜':'ピ','フ゜':'プ','ヘ゜':'ペ','ホ゜':'ポ',
- 'か゛':'が','き゛':'ぎ','く゛':'ぐ','け゛':'げ','こ゛':'ご',
- 'さ゛':'ざ','し゛':'じ','す゛':'ず','せ゛':'ぜ','そ゛':'ぞ',
- 'た゛':'だ','ち゛':'ぢ','つ゛':'づ','て゛':'で','と゛':'ど',
- 'は゛':'ば','ひ゛':'び','ふ゛':'ぶ','へ゛':'べ','ほ゛':'ぼ',
- 'は゜':'ぱ','ひ゜':'ぴ','ふ゜':'ぷ','へ゜':'ぺ','ほ゜':'ぽ',
- };
- const daku_handaku_table={'\u3099':'\u309B','\u309B':'\u309B','\u309A':'\u309C','\u309C':'\u309C'};
- const pat1=new RegExp('['+K(NT1).join('')+']', 'g');
- const pat2=new RegExp('(?:' + K(NT2).join('|') + ')', 'g');
- // const pat3=new RegExp('(['+K(NT3).map(function(s){return s[0]}).join('')+'])([\u3099\u309B\u309A\u309C])', 'g');
- const pat3=/([ウワヲカキクケコサシスセソタチツテトハヒフヘホうかきくけこさしすせそたちつてとはひふへほ])([\u3099\u309B\u309A\u309C])/g;
- return( s
- .replace(/[A-Za-z0-9]/g, function(s){return String.fromCharCode(s.charCodeAt(0)-0xFEE0)}) // 全角英数→半角英数
- .replace(pat1,function(m){return NT1[m]})
- .replace(pat2,function(m){return NT2[m]})
- .replace(pat3,function(m,s1,s2){return NT3[s1+daku_handaku_table[s2]]})
- .replace(/([\]\)])([^\s\.])/g, '$1 $2')
- .replace(/([^ ])([\[\(])/g, '$1 $2')
- .replace(/ +/g," ")
- );
- }
- /**
- * タイトル比較の前処理
- *
- * 渡されたパッケージ名/リンク名の文字列から
- * 「カテゴリ」や「作者名」などを取り除く、また表記揺れを是正する
- *
- * タイトル名以降で比較できるようにするため
- *
- * @param {string} str 元のパッケージ名/リンク名
- * @returns {string} 比較用に編集されたパッケージ名/リンク名
- */
- //function truncateName(str)
- function tN(str)
- {
- return normalizeTitle(str.trim())
- .replace(/[@@]COMIC(?: |$)/i,' ') // 不要文字列除去
- .replace(/\[[^\]]+\]/g,' ') // 括弧で括られた文字列除去(
- .replace(/\([^\)]+\)/g, ' ') // 括弧で括られた文字列除去
- .replace(/【[^】]+】/g,' ') // 括弧で括られた文字列除去
- .replace(/~[^~]+~/,' ') // サブタイトル除去
- .replace(/~([^ \d\.]+\b)( [^ \d\.]+\b)*/,' ') // サブタイトル除去
- .replace(/\b([-ー―-])[^\d\.]+\1\b/,' ') // サブタイトル除去
- .replace(/\b0+(\d+)\b/g, '$1') // 0パディング除去
- .replace(/第(\d+([-+,_ ]\d+)*)巻/,' v$1 ') // 数字と間の記号のみに修正
- .replace(/全(\d+)巻/,' v1-$1 ') // 数字と間の記号のみに修正
- .replace(/\bzen(\d+)/,' v1-$1 ') // 数字と間の記号のみに修正
- // .replace(/\bv(\d+)/g,' v$1 ') // 数字と間の記号のみに修正
- .replace(/\u0073hit|ra\u0070e/gi, '') // turbobit用NGワード除去
- .replace(/×/g,'x') // 作者名結合文字×をxに変更(タイポでxとなっていることがよくあるため、そちらに統一)
- .replace(/ +epub$/,'')
- .replace(/([^L])v1($|[ a-z])/,'$1 $2') // 第01巻は巻数削除
- .replace(/[_ ]/g,'') // 区切り文字除去(除去するタイミングは最後近くで)
- .replace(/[-ーー-―~]/g,'') // ハイフン系削除
- .replace(/([!?#$%&+@、。!?#$%&+@、。])/g,'') // 不要記号除去
- }
- /** @type {object.<string:string>} tN(str)のキャッシュ*/
- var g_cache_tN = {};
- /**
- * タイトル比較の前処理(キャッシュ版)
- *
- * 渡されたパッケージ名/リンク名の文字列から
- * 「カテゴリ」や「作者名」などを取り除く、また表記揺れを是正する
- *
- * タイトル名以降で比較できるようにするため
- *
- * ※ 結果をキャッシュする
- *
- * @param {string} str 元のパッケージ名/リンク名
- * @returns {string} 比較用に編集されたパッケージ名/リンク名
- */
- function tN_cache(str)
- {
- return (str in g_cache_tN) ? g_cache_tN[str] : (g_cache_tN[str] = tN(str));
- }
- /**
- * 自動生成されたパッケージか否かを名前から判定
- *
- * 「言語設定-日本語」環境下では以下の文字列値が
- * 自動生成されたパッケージが取り得る名前
- *
- * <ul>
- * <li>様々なファイル
- * <li>任意
- * <li>Folder
- * <li>永続オフライン
- * <li>オフラインファイル
- * </ul>
- *
- * @param {string} n パッケージ名
- * @returns {boolean} 自動生成されたパッケージの名前であればtrue、否ならfalse
- */
- function isAutoCreatedPackageName(n)
- {
- return n=='様々なファイル'||n=='任意'||n=='Folder'||n=='永続オフライン'||n=='オフラインファイル'
- }
- function getFileParts(f)
- {
- const r = (f||'').match(/^(.+(?:\.part\d+)?)(\.mp3|\.zip|\.rar|\.tar|\.epub)?(\.[\da-z]{0,6}[a-z][\da-z]{0,6})$/i);
- return r?[r[1]||'',r[2]||'',r[3]||'']:[f||'','',''];
- }
- function getFileSpec(f){return (getFileParts(f))[0]}
- function getFileSpecFull(f){const x=getFileParts(f);return x[0]+x[1];}
- function getExt(f){return (getFileParts())[2]}
- function getExtFull(f){const x=getFileParts(f);return x[1]+x[2];}
- function comicNumbering(str)
- {
- return (isNaN(str)||str.length==2)?str:(str.length==1)?('0'+str):str.replace(/^0+(\d\d)$/,'$1');
- }
- function replaceNums(str)
- {
- return str.replace(/\d+/g,function(x){return comicNumbering(x);})
- }
- function K(k){return Object.keys(k)}
- function isNameEpub(str){return /[ \.]epub(?:[ \.]|$)/.test(str)}
- /**
- * パッケージ/リンクの名前比較関数
- *
- * @param {object} l crawledLinkリンク
- * @param {object} t 比較対象のパッケージorリンク
- * @returns {boolean} 同じとみなせればtrue、否ならfalse
- */
- function compareLink(l, t)
- {
- // 2024/12/09 第1引数のcrawledLinkの加工後の名前のみキャッシュするよう変更
- const l_name_crafted = tN_cache(getFileSpec(l.name));
- const t_name_crafted = tN((t.isExpanded) ? t.name : getFileSpec(t.name));
- if (l_name_crafted != t_name_crafted)
- {
- // log('compareLink: false\r\n\t\t"'+l_name_crafted+'"\r\n\t\t"'+t_name_crafted+'"\r\n');
- return false;
- }
- // epub表記揺れ対応、epub表記有りと無しの比較ならば、サイズが誤差範囲内なら同じと判定する
- if (isNameEpub(l.name) != isNameEpub(t.name))
- {
- // size check
- const size_margin = 0.06; // マージン用閾値、許容範囲を考え自己都合に合わせて変更可
- const l_size = l.getBytesTotal();
- const min_size = l_size - Math.ceil(l_size * size_margin);
- const max_size = l_size + Math.floor(l_size * size_margin);
- const t_size = t.getBytesTotal();
- if (t_size < min_size || max_size < t_size)
- {
- // log('compareLink: false (epub size)\r\n\t\t"'+l_size+'B\t'+l_name_crafted+'"\r\n\t\t"'+t_size+'B\t'+t_name_crafted+'"\r\n');
- return false;
- }
- }
- // log('compareLink: true\r\n\t\t"'+l_name_crafted+'"\r\n\t\t"'+t_name_crafted+'"\r\n');
- return true;
- }
- function mergePackagesWithSameTitle_for_crawledLink(c_link)
- {
- // 開始時に少し待機、JD自体が重い時のエラー予防
- const interval = 1*100;
- var limitcount = 50;
- while(limitcount-- > 0)
- if (sleep(interval) || (c_link.package && c_link.package.name))
- break;
- if (c_link.package===null || c_link.package.name===null) return;
- var i=0;
- var UUIDs = [], matched_names = [];
- //
- // 新規リンクの名前とマッチしたリンクのUUIDを取得
- // マッチしたパッケージ名とリンク名を取得
- //
- getAllCrawledPackages().forEach(function(p)
- {
- // 自動作成されたパッケージでは個々のリンク名を比較
- if (isAutoCreatedPackageName(p.name))
- p.downloadLinks.forEach(function(l)
- {
- if (isTargetFileExt(l.name)
- && compareLink(c_link, l))
- {
- matched_names.push(getFileSpec(l.name)),
- UUIDs.push(l.UUID)
- }
- });
- // 自動作成されたパッケージでなければパッケージ名を比較
- else if (compareLink(c_link, p))
- // 指定拡張子以外のリンクが含まれていれば除外
- if (! p.downloadLinks.some(function(l){return !isTargetFileExt(l.name)}))
- matched_names.push(p.name),
- UUIDs = UUIDs.concat(p.downloadLinks.map(function(l){return l.UUID}));
- });
- if (matched_names.length == 0) return;
- const is_epub = matched_names.some(function(n){return isNameEpub(n)});
- var final_package_name = '';
- if (!getAllFilePackages().some(
- function(p){return (compareLink(c_link, p)) && (final_package_name = p.name)}))
- {
- // DLの方に無い場合
- if (matched_names.length == 1) return;
- var final_author = '', final_category = '';
- var authors = {}, categorys = {};
- function incKey(o,k){k in o?o[k]++:(o[k]=1)};
- function setCategoryAndAuthor(name_str, category_dict, author_dict)
- {
- const r = name_str.match(/^(?:\(([^\)]+)\) *)?(?:\[([^\]]+)\] *)?/)
- if (!r) return;
- r[1] && incKey(category_dict,r[1]);
- r[2] && r[2] != 'Novel' && incKey(author_dict, r[2]);
- }
- matched_names.forEach(function(n){setCategoryAndAuthor(n,categorys,authors)});
- // 採用する作者名を取得
- // 英数のみ以外がある場合、英数のみのものは除外
- if (K(authors).length)
- {
- // const tmp1 = K(authors).sort(function(a,b){return authors[b]-authors[a]}); // 該当数順
- const tmp1 = K(authors).sort(function(a,b){return b.length - a.length}); // 作者名の長さ順
- const tmp2 = tmp1.filter(function(k){return ! /^[- _\.a-z\d]+$/i.test(k)});
- if (tmp2.length)
- {
- final_author = tmp2[0];
- // 除外すべき英数のみ作者名の名前がある
- if (tmp1.length != tmp2.length)
- matched_names = matched_names.filter(function(n){
- return ! /^(?:\([^\)]+\) *)?\[[- _\.a-z\d]+\]/i.test(n)});
- }
- else
- final_author = tmp1[0];
- }
- if (K(categorys).length)
- category_list.some(function(c){return (c in categorys) && (final_category = c)});
- // パッケージ名を選出
- // "(category) [authors] title..."
- // "[authors] title..."
- var final_package_names = matched_names.filter(function(n){return /^\(([^\)]+)\) \[([^\]]+)\] .+/.test(n)})
- || matched_names.filter(function(n){return /^\[([^\]]+)\] .+/.test(n)});
- if (0==final_package_names.length) return;//***
- final_package_name = final_package_names[0];
- if (final_author)
- final_package_name = final_package_name.replace(/^(\([^\)]+\))? *\[[^\]]+\] */,
- function(m,m1){return (m1?(m1+' '):'')+'['+ final_author+'] '});
- if (final_category)
- final_package_name = final_package_name.replace(/^(\([^\)]+\))? */, '('+final_category+') ');
- }
- if (is_epub && !isNameEpub(final_package_name))
- final_package_name += ' epub';
- callAPI('linkgrabberv2', 'movetoNewPackage', UUIDs, null, final_package_name, null);
- }
- function waitForLinkCrawling(timeout_msec)
- {
- const polling_interval = 100;
- const timeout_min = polling_interval;
- const timeout_max = 5*60*1000;
- timeout_msec || (timeout_msec = timeout_max);
- var trycount = Math.max(Math.min(timeout_msec,timeout_max),timeout_min)
- /polling_interval;
- while(trycount-- > 0)
- {
- if (! callAPI("linkcrawler","isCrawling")
- && ! callAPI("linkgrabberv2","isCollecting"))
- return true;
- sleep(polling_interval);
- }
- return false;
- }
- disablePermissionChecks();
- if (isTargetFileExt(crawledLink.name))
- {
- // 取得できるjobがnullなので、jobが終えたかを個別で検知できない
- waitForLinkCrawling();
- const lock_key = "JD2.similar_title.lock";
- var glock = getModifyLock(lock_key);
- glock.writeLock();
- try {
- mergePackagesWithSameTitle_for_crawledLink(crawledLink);
- }
- finally
- {
- glock.writeUnlock();
- glock = null;
- }
- }
Advertisement
Comments
-
- 2024/11/14 waitFor~ でsleepが消えていたのを修正
-
- 2024/11/29 Downloadタブにあるパッケージ名と合致すれば、その名前を優先使用するよう変更。
Add Comment
Please, Sign In to add comment
Advertisement