usamimi2323

JDownloader2 EventScript - リネーム系アクション集

Oct 23rd, 2024 (edited)
50
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 60.80 KB | Source Code | 0 0
  1. /**
  2.  * JDownloader2 EventScript - リネーム系アクション集
  3.  *
  4.  * 『コンテキストメニューから実行』できる
  5.  * リネーム/検索/移動/並べ替えなどの雑多なアクションを集めたモノ
  6.  *
  7.  *
  8.  * 実装済み:
  9.  * ・リネーム各種
  10.  * ・ローカルファイル検索(EveryThing)
  11.  * ・WEB検索(ブラウザ)
  12.  * ・特定ホストだけを有効/無効
  13.  * ・パッケージ並べ替え
  14.  * ・リンクグラバーに再登録
  15.  * ・名前付き新しいパッケージに移動
  16.  *  (既存の標準メニューにも「新しいパッケージに移動」は存在するが、
  17.  *  ダイアログが開くのでその無駄を省く)
  18.  *
  19.  * なお、日本語ファイル名でのJD2への登録とファイル保存運用が基本
  20.  * 作者名やタイトルに関しては、日本語ファイル名の命名規則に
  21.  * 則っている必要がある
  22.  *
  23.  * 命名規則例: (カテゴリ) [作者名A×作者名B] タイトル 第01-25巻
  24.  *
  25.  *
  26.  * JDについては、Windows環境しかないので他環境は関知しません
  27.  *
  28.  *
  29.  * 当該コードを利用することで生じるいかなる損失、被害、影響もコード製作者は責任を負いません
  30.  * 改変転載自由、全て自己責任で
  31.  *
  32.  *
  33.  * 【使い方】
  34.  *
  35.  * ■[トリガー]
  36.  *    『ダウンロードリストコンテキストメニュー選択時』
  37.  *    『リンクグラバーリストコンテキストメニュー選択時』
  38.  *
  39.  *    同一スクリプトで両方に対応可能
  40.  *
  41.  *
  42.  *
  43.  * スクリプト内の【設定ここから】部分を、環境に合わせて書き換える
  44.  *
  45.  * ■[WEB検索]
  46.  *  Config.web_search_table内の設定を自分用に書き換える
  47.  *  自動ならリネーム系アクション集用メニューインポーターを書き換える
  48.  *  手動ならnameをメニュー名に設定する
  49.  *
  50.  *
  51.  *
  52.  * ■[コンテキストメニューへの登録]
  53.  *
  54.  *  □自動設定
  55.  *
  56.  *   別途、自動登録用スクリプトを使用することで自動で登録可能
  57.  *
  58.  * @see https://pastebin.com/ADfTHJq8
  59.  *      "JDownloader2 EventScript - リネーム系アクション集用メニューインポーター"
  60.  *
  61.  *  □手動設定
  62.  *
  63.  *   ダウンロードリストコンテキストメニュー、リンクグラバーコンテキストメニュー
  64.  *   メニュー管理画面→「アクション追加」or「+」ボタン→「EventScripter Trigger」
  65.  *   →プロパティの「名前」を下記「メニュー名」のいずれかに設定
  66.  *
  67.  * --------------------------------------------------------------------------------------------------------------------------
  68.  * メニュー名                                       説明
  69.  * --------------------------------------------------------------------------------------------------------------------------
  70.  * EveryThingで検索(タイトル)                   EveryThingを検索します((△△) [○○] ~~の~~を検索ワードに)
  71.  * EveryThingで検索(作者名)                     EveryThingで検索します((△△) [○○] ~~の○○を検索ワードに)
  72.  * ブラウザで開く                               設定したブラウザで開きます
  73.  * hogehoge.comのみを開く                       選択されたリンクからホスト名hogehoge.comを含んだリンクのみブラウザで開きます
  74.  *                                              (他の「【ホスト名】のみを開く」でバリエーションを増やせます)
  75.  * JDバックアップフォルダを開く
  76.  * 登録元ページを開く                           登録元(sourceUrl)を開く
  77.  *
  78.  *
  79.  * パッケージ名で名前を揃える                   パッケージ名でパッケージ内のリンクの名前を揃えます
  80.  *
  81.  * このリンクの名前でパッケージ内を全て揃える   選択しているリンクの名前で、パッケージとパッケージ内のリンクの名前を揃えます
  82.  *                                              (各パッケージ内でリンクを一つだけ選択)
  83.  *
  84.  * 名前を揃える                                 右クリックされたリンク、もしくは一番上のリンクの名前で
  85.  *                                              選択されているリンクの名前を揃えます ※1
  86.  *
  87.  * 名前を揃える(作者名)                         選択などは※1と同様 (○○) [△△] □□ の[△△]を揃える
  88.  * 名前を揃える(タイトル)                       選択などは※1と同様 (○○) [△△] □□ の□□を揃える
  89.  * 名前を揃える(カテゴリ+作者名)                選択などは※1と同様 (○○) [△△] □□ の(○○) [△△]を揃える
  90.  * 名前を揃える(カテゴリ+作者名+タイトル)       選択などは※1と同様 (○○) [△△] □□ 第01-04巻の(○○) [△△] □□を揃える
  91.  *
  92.  * 第○○巻化                                   v01を第01巻にリネームします
  93.  *
  94.  * []内のスワップ                               [○○×△△]→[△△×○○]
  95.  * []内の末尾切り捨て                           [○○×△△×□□]→[○○×△△]
  96.  * []内のxを×に変換                            [○○x△△]→[○○×△△]
  97.  *
  98.  * 連番桁揃え                                   連番ファイルの数字の桁を揃えます(0埋め)
  99.  *
  100.  *
  101.  * ○○ >>                                      「【追加したい文字】   >>」
  102.  *                                              リンクまたはパッケージの名前の先頭に○○を追加する
  103.  *                                              (○○) がカテゴリ名の場合は追加ではなく置換する
  104.  *
  105.  * << ××                                      「<<   【追加したい文字】」
  106.  *                                              リンクまたはパッケージの名前の終端に××を追加する
  107.  *
  108.  *
  109.  * この名前を使用して新しいパッケージに移動     右クリックされたリンクまたはパッケージの名前を使用し
  110.  *                                              選択されたリンクまたはパッケージを
  111.  *                                              新しいパッケージに移動し、まとめます
  112.  *                                              
  113.  * 同じ名前のパッケージをまとめる               同じ名前のパッケージをまとめます
  114.  *
  115.  * リンクグラバーへ再登録                       選択されたリンクまたはパッケージをリンクグラバーに再登録する
  116.  *
  117.  *
  118.  * タイトルで並べ替え(昇順)                     (△△) [○○] □□…… □□でパッケージを昇順並べ替え
  119.  * タイトルで並べ替え(降順)                     (△△) [○○] □□…… □□でパッケージを降順並べ替え
  120.  * 作者名で並べ替え(昇順)                       (△△) [○○] □□…… [○○]でパッケージを昇順並べ替え
  121.  * 作者名で並べ替え(降順)                       (△△) [○○] □□…… [○○]でパッケージを降順並べ替え
  122.  *
  123.  * 優先ホスト以外を無効                         選択しているパッケージまたはリンクのURLを
  124.  *                                              優先順のマッチングリストの先頭から順にマッチングしていき、
  125.  *                                              該当したら、それ以外のパッケージ内のリンクを無効にする
  126.  *
  127.  * hogehoge.com以外を無効                       選択されたリンクからホスト名hogehoge.comを含まないリンクを無効にする
  128.  * hogehoge.comを無効                           選択されたリンクからホスト名hogehoge.comを含んだリンクを無効にする
  129.  *
  130.  *
  131.  *
  132.  *
  133.  * --------------------------------------------------------------------------------------------------------------------------
  134.  *
  135.  * ※hogehoge.comは任意のホスト名
  136.  *
  137.  *
  138.  **/
  139.  
  140.  
  141.  
  142.  
  143. disablePermissionChecks();
  144.  
  145. const variousPackage='様々なファイル';
  146. const defaultPackageName='任意';
  147.  
  148. /** @enum {number} */
  149. const gkPart={
  150.     author: 1,
  151.     author_1st: 2,
  152.     title: 3
  153. };
  154.  
  155.  
  156. var Config =
  157. {
  158. // 【↓設定ここから】
  159.     everything_path     : 'C:\\Program Files\\Everything\\Everything.exe',
  160.     browser_path        : 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe',
  161.     filer_path          : getEnv('WINDIR')+'\\explorer.exe',
  162.  
  163.     web_search_table: {
  164.     //
  165.     // 書式 'name': [part,'URL'],
  166.     //
  167.     // name  メニュー名
  168.     //
  169.     // part  検索ワードに使うファイル名の部分
  170.     //       (gkPart.title|gkPart.author|gkPart.author_1st)
  171.     //
  172.     // 【例】(一般小説) [ワシ×オマエ] ドM哲学 ~深淵編~ 第01巻.zip
  173.     //   gkPart.title           タイトル部分   【例】 検索ワード'ドM哲学'
  174.     //   gkPart.author          []内の作者名部分全て 【例】 検索ワード'ワシ OR オマエ'
  175.     //   gkPart.author_1st      []内の作者名部分先頭 【例】 検索ワード'ワシ'
  176.     //
  177.     // URL   %REP%が検索ワードに置換されたURLがブラウザに渡される
  178.     //       (デフォルト値) null => "https://name/?s=%REP%"
  179.     //
  180.     //
  181.         //*****[ 自分用に書き換える ]*****/
  182.         'x***.net'          : [gkPart.author_1st,   null],
  183.         'bs***.com'         : [gkPart.author_1st,   null],
  184.         '*****.se'          : [gkPart.title,        'https://*****.se/search/%REP%/'],
  185.         '888**.**'          : [gkPart.author_1st,   'https://888**.**/search.php?q=%REP%'],
  186.         '******nexus.com'   : [gkPart.author_1st,   'https://******nexus.com/public/search.php?x=12&y=17&q=%REP%'],
  187.         '678**.***'         : [gkPart.author_1st,   null],
  188.         '*****77.***'       : [gkPart.author_1st,   null],
  189.         'dl-***.***'        : [gkPart.author_1st,   null],
  190.         'raw*****.cc'       : [gkPart.author_1st,   null],
  191.         '*****-zone.***'    : [gkPart.author_1st,   'http://www.*****-zone.***/?submit=Search&s=%REP%'],
  192.         '******core.***'    : [gkPart.author_1st,   null],
  193.         '******omg.***'     : [gkPart.author_1st,   null],
  194.         'n******.net'       : [gkPart.author_1st,   'https://n******.net/search/?q=%REP%'],
  195.  
  196.         'google.com (作者)'       : [gkPart.author,   'https://www.google.com/search?q=%REP%&ie=utf-8&oe=utf-8'],
  197.         'google.com (タイトル)' : [gkPart.title,    'https://www.google.com/search?q=%REP%&ie=utf-8&oe=utf-8'],
  198.     },
  199.    
  200. //
  201. // 以下は、メニュー名の特定ワードに反応します
  202. //
  203. // "(○○) >>" でファイル名先頭に文字列(○○)を追加/置換(先頭には(カテゴリ)しか追加しない)
  204. // "<< ○○"   でファイル名末尾に文字列○○を追加
  205. // "aaaa.comを無効"     でaaaa.comというホストのリンクを無効化
  206. // "bbbb.com以外を無効" でbbbb.comというホストのリンク以外を無効化
  207.  
  208.     // 「優先ホスト以外を無効」で使用する優先順のマッチングリスト
  209.     // 先頭から順にマッチングしていき、該当したらそれ以外を無効にする
  210.     disableLink_RulePatterns: [
  211.         /hexload\.com/,
  212.         /frdl\.to/,
  213.         /dashfile\.net/,
  214.         /dailyuploads\.net/     // reCaptchaが必須になった
  215.     ],
  216.  
  217. //
  218. //
  219.     use_WSH: true,
  220. //  use_WSH: false,
  221.     open_browser_interval: 2000,        // ミリ秒
  222.     open_browser_max:10,
  223.    
  224.     waittime_for_expand_package:100,    // ミリ秒
  225.     expand_after_newpackage: false,     // パッケージに移動後にツリー展開したくない場合はfalseにする
  226.  
  227. // 【↑設定ここまで】
  228.  
  229.     dispatch_table: {
  230.    
  231. //      メニュー名 : 呼び出す関数
  232.    
  233. // 外部プログラム系
  234.         'EveryThingで検索(タイトル)'             : openEveryThingByTitle,
  235.         'EveryThingで検索(作者名)'                    : openEveryThingByAuthorName,
  236.         'ブラウザで開く'                         : openLink,
  237.         'uploadbank.comのみを開く'                 : openLinkOnlyUploadbank,       // ブラウザで開いてReCaptcha通過後にURLを登録するため
  238.         'JDバックアップフォルダを開く'             : openJDCfgFolder,
  239.         '登録元ページを開く'                       : openSourceURL,
  240.        
  241. // リネーム系
  242.         'パッケージ名で名前を揃える'               : renameLinksByPackageName,
  243.         'このリンクの名前でパッケージ内を全て揃える': renamePackageAndLinksByContextLinkName,
  244.  
  245.         '名前を揃える'                                : renameLinkByLinkName,     // 複数のリンクを選択してから右クリックし、右クリック直下のファイル名 (○○) [△△] □□.zip→(○○) [△△] □□を揃える
  246.         '名前を揃える(作者名)'                     : renameLinkByLinkAuthor,   // (○○) [△△] □□.zip→[△△]部を揃える
  247.         '名前を揃える(タイトル)'                  : renameLinkByLinkTitle,    // (○○) [△△] □□ ~××~第01巻.zip→□□部を揃える
  248.         '名前を揃える(カテゴリ+作者名)'                : renameLinkByLinkCategoryAuthor,
  249.         '名前を揃える(カテゴリ+作者名+タイトル)'   : renameLinkByLinkLongTitle,
  250.  
  251.         '先頭括弧を後方送り'                       : renameBracketsMoveToEndAndAddDojinshi,
  252.         '末尾の括弧削除'                         : renameRemoveTrailingBrackets,
  253.         '第○○巻化'                               : renameNumberingFormat,
  254.  
  255.         '[]内のスワップ'                          : renameAuthorSwap, // [○○×△△]→[△△×○○]
  256.         '[]内の末尾切り捨て'                        : renameAuthorChop, // [○○×△△×□□]→[○○×△△]
  257.         '[]内のxを×に変換'                           : renameAuthorXtoBatsu, // [○○x△△]→[○○×△△]
  258.        
  259.         '元の名前に戻す'                         : renameToOriginalNameByComment,
  260.         '連番桁揃え'                               : renameSequentialNumbersWithZeroPadding,   // 数字のみのファイル名のみ
  261. // 移動系
  262.         'この名前を使用して新しいパッケージに移動'  : moveToNewPackageWithFileName,
  263.         '同じ名前のパッケージをまとめる'         : moveSameNamePackagesToPackage,
  264.         'リンクグラバーへ再登録'                 : addLinksToLinkGrabber,
  265.         'リンクを同じ名前のパッケージに移動'       : moveLinkToSameNamePackage,
  266.        
  267.         'タイトルで並べ替え(昇順)'                   : sortPackagesByTitleAscending,
  268.         'タイトルで並べ替え(降順)'                   : sortPackagesByTitleDescending,
  269.         '作者名で並べ替え(昇順)'                  : sortPackagesByAuthorAscending,
  270.         '作者名で並べ替え(降順)'                  : sortPackagesByAuthorDescending,
  271.        
  272.         '優先ホスト以外を無効'                        : disableLinksMyRules,
  273. //      'dashfile.net以外を無効'                   : disableLinksWithoutDashfile,
  274. //      'rosefile.net以外を無効'                   : disableLinksWithoutRosefile,
  275. //      'btafile.com以外を無効'                        : disableLinksWithoutBtafile,
  276. //      'dailyuploads.net以外を無効'               : disableLinksWithoutDailyuploads,
  277. //      'uploadbank.netを無効'                       : disableLinksUploadbank
  278.     },
  279.    
  280.     menu : null,
  281.     name : null,
  282.     selection : null
  283. };
  284.  
  285.  
  286. // init
  287.  
  288. try
  289. {
  290.     Config.menu = menu;
  291.     Config.name = name;
  292.     Config.icon = icon;
  293.     Config.shortCutString = shortCutString;
  294.  
  295.     if (Config.menu == "DOWNLOAD_TABLE_CONTEXT_MENU_BUTTON")
  296.     {
  297.         Config.selection = dlSelection;
  298.        
  299.         Config.APIName          = 'downloadsV2';
  300.         Config.getAllPackages   = getAllFilePackages;
  301.         Config.getAllLinks      = getAllDownloadLinks;
  302.         Config.getPackageByUUID = getDownloadPackageByUUID;
  303.         Config.getLinkByUUID    = getDownloadLinkByUUID;
  304.     }
  305.     else if (Config.menu == "LINKGRABBER_TABLE_CONTEXT_MENU_BUTTON")
  306.     {
  307.         Config.selection = lgSelection;
  308.        
  309.         Config.APIName          = 'linkgrabberv2';
  310.         Config.getAllPackages   = getAllCrawledPackages;
  311.         Config.getAllLinks      = getAllCrawledLinks;
  312.         Config.getPackageByUUID = getCrawledPackageByUUID;
  313.         Config.getLinkByUUID    = getCrawledLinkByUUID;
  314.     }
  315.     else
  316.     {
  317.         alert(Config.menu);
  318.         throw Error();
  319.     }
  320.     if (!getEnvironment().isWindows())
  321.         Config.use_WSH = false;
  322. }
  323. catch(e)
  324. {
  325.     throw Error(
  326.         'イベントトリガーのタイプが正しく設定されていません\r\n\r\n'+
  327.         '解決方法:イベントスクリプトの設定から本スクリプトのトリガーに\r\n'+
  328.         '     【ダウンロードリストコンテキストメニュー選択時】か、\r\n'+
  329.         '     【リンクグラバーコンテキストメニュー選択時】を選択し、\r\n'+
  330.         '     リストビュー画面の右クリックのコンテキストメニューから実行してください\r\n'
  331.     );
  332. }
  333.  
  334.  
  335. (Config.dispatch_table[Config.name] || defaultDispatcher)();
  336.  
  337.  
  338.  
  339. function defaultDispatcher()
  340. {
  341.     switch(true)
  342.     {
  343.     case Config.name in Config.web_search_table:
  344.         searchOnWEBSite();
  345.         break;
  346.     case /^.+ *>>$/.test(Config.name):
  347.         renameAddMenunameToBegin();
  348.         break;
  349.     case /^<< *.+$/.test(Config.name):
  350.         renameAddMenunameToEnd();
  351.         break;
  352.     case /^[-\da-zA-Z\.]+を無効$/.test(Config.name):
  353.         disableLinksByHost();
  354.         break;
  355.     case /^[-\da-zA-Z\.]+以外を無効$/.test(Config.name):
  356.         disableLinksByHostExclude();
  357.         break;
  358.        
  359. //  case /^[^\.]+\.[^\.]+(\.[^\.]+)?$/.test(name):
  360. //      searchOnWEBSite();
  361. //      break;
  362.     }
  363. }
  364.  
  365.  
  366. function isDefaultFolderName(n){return n==variousPackage||n==defaultPackageName||n=='folder'}
  367. function isDownloadTable(){return(Config.menu == "DOWNLOAD_TABLE_CONTEXT_MENU_BUTTON")}
  368. function isLinkGrabberTable(){return(Config.menu == "LINKGRABBER_TABLE_CONTEXT_MENU_BUTTON")}
  369. function getSelection(){return(Config.selection)}
  370. function zeroPadding(s,l){return((s.length<l?'0'.repeat(l-s.length):'')+s)}
  371.  
  372. function getDefaultDownloadFolder()
  373. {
  374.     return callAPI("config", "get", "org.jdownloader.settings.GeneralSettings", null, "DefaultDownloadFolder");
  375. }
  376.  
  377. function stripDoubleQuotes(s)
  378. {
  379.     return s.replace(/^[\s\xA0]*"?|"?[\s\xA0]*$/g, '');
  380. }
  381. //function setDoubleQuotes(s)
  382. function DQ(s)
  383. {
  384.     return '"'+ s.trim() +'"';
  385. }
  386.  
  387. function K(k){return Object.keys(k)}
  388.  
  389. /**
  390.  * 文字列の正規化
  391.  *
  392.  * @param   {string} s 変換前の文字列
  393.  * @returns {string} 変換後の文字列
  394.  */
  395. function normalizeTitle(s)
  396. {
  397.     /**
  398.      * @type {object.<string:string>} 半角記号の変換テーブル
  399.      */
  400.     const NT1 = {
  401. "\!":"!","\"":"”","\$":"$","\%":"%","\&":"&","'":"’","~":" ̄","|":"|",
  402. "*":"*",":":":","<":"<",">":">","?":"?","/":"/","\\":"¥","`":"‘",",":","
  403. //特殊濁点半濁点→日本語濁点半濁点(分離)
  404. //"\u3099":"\u309B","\u309A":"\u309C"
  405.     };
  406.     /**
  407.      * @type {object.<string:string>}} 文字列の変換テーブル
  408.      */
  409.     const NT2 = {
  410. //記号
  411. " ":" ","〜":"~","&times;":"+","&amp;":"&","♡":"","♥":"","―":"-","─":"-","━":"-",
  412.  
  413. //半角カナ→全角カナ
  414. 'ガ': 'ガ', 'ギ': 'ギ', 'グ': 'グ', 'ゲ': 'ゲ', 'ゴ': 'ゴ',
  415. 'ザ': 'ザ', 'ジ': 'ジ', 'ズ': 'ズ', 'ゼ': 'ゼ', 'ゾ': 'ゾ',
  416. 'ダ': 'ダ', 'ヂ': 'ヂ', 'ヅ': 'ヅ', 'デ': 'デ', 'ド': 'ド',
  417. 'バ': 'バ', 'ビ': 'ビ', 'ブ': 'ブ', 'ベ': 'ベ', 'ボ': 'ボ',
  418. 'パ': 'パ', 'ピ': 'ピ', 'プ': 'プ', 'ペ': 'ペ', 'ポ': 'ポ',
  419. 'ヴ': 'ヴ', 'ヷ': 'ヷ', 'ヺ': 'ヺ',
  420. 'ア': 'ア', 'イ': 'イ', 'ウ': 'ウ', 'エ': 'エ', 'オ': 'オ',
  421. 'カ': 'カ', 'キ': 'キ', 'ク': 'ク', 'ケ': 'ケ', 'コ': 'コ',
  422. 'サ': 'サ', 'シ': 'シ', 'ス': 'ス', 'セ': 'セ', 'ソ': 'ソ',
  423. 'タ': 'タ', 'チ': 'チ', 'ツ': 'ツ', 'テ': 'テ', 'ト': 'ト',
  424. 'ナ': 'ナ', 'ニ': 'ニ', 'ヌ': 'ヌ', 'ネ': 'ネ', 'ノ': 'ノ',
  425. 'ハ': 'ハ', 'ヒ': 'ヒ', 'フ': 'フ', 'ヘ': 'ヘ', 'ホ': 'ホ',
  426. 'マ': 'マ', 'ミ': 'ミ', 'ム': 'ム', 'メ': 'メ', 'モ': 'モ',
  427. 'ヤ': 'ヤ', 'ユ': 'ユ', 'ヨ': 'ヨ',
  428. 'ラ': 'ラ', 'リ': 'リ', 'ル': 'ル', 'レ': 'レ', 'ロ': 'ロ',
  429. 'ワ': 'ワ', 'ヲ': 'ヲ', 'ン': 'ン',
  430. 'ァ': 'ァ', 'ィ': 'ィ', 'ゥ': 'ゥ', 'ェ': 'ェ', 'ォ': 'ォ',
  431. 'ッ': 'ッ', 'ャ': 'ャ', 'ュ': 'ュ', 'ョ': 'ョ',
  432. '。': '。', '、': '、', 'ー': 'ー', '「': '「', '」': '」', '・': '・',
  433. //よくある簡体字、繁体字→日本語常用漢字
  434. '卷':'巻','學':'学','黑':'黒','關':'関','繪':'絵','會':'会','亞':'亜','髙':'高','說':'説','户':'戸',
  435. '說':'説','说':'説'     //繁体字、簡体字の"説"
  436.     };
  437.    
  438.     /** @type {object.<string:string>} 濁点半濁点結合 */
  439.     const NT3={
  440. 'ウ゛':'ヴ','ワ゛':'ヷ','ヲ゛':'ヺ',
  441. 'カ゛':'ガ','キ゛':'ギ','ク゛':'グ','ケ゛':'ゲ','コ゛':'ゴ',
  442. 'サ゛':'ザ','シ゛':'ジ','ス゛':'ズ','セ゛':'ゼ','ソ゛':'ゾ',
  443. 'タ゛':'ダ','チ゛':'ヂ','ツ゛':'ヅ','テ゛':'デ','ト゛':'ド',
  444. 'ハ゛':'バ','ヒ゛':'ビ','フ゛':'ブ','ヘ゛':'ベ','ホ゛':'ボ',
  445. 'ハ゜':'パ','ヒ゜':'ピ','フ゜':'プ','ヘ゜':'ペ','ホ゜':'ポ',
  446. 'か゛':'が','き゛':'ぎ','く゛':'ぐ','け゛':'げ','こ゛':'ご',
  447. 'さ゛':'ざ','し゛':'じ','す゛':'ず','せ゛':'ぜ','そ゛':'ぞ',
  448. 'た゛':'だ','ち゛':'ぢ','つ゛':'づ','て゛':'で','と゛':'ど',
  449. 'は゛':'ば','ひ゛':'び','ふ゛':'ぶ','へ゛':'べ','ほ゛':'ぼ',
  450. 'は゜':'ぱ','ひ゜':'ぴ','ふ゜':'ぷ','へ゜':'ぺ','ほ゜':'ぽ',
  451.     };
  452.     if (s==null||s=="") return s;
  453. //  var pat1=new RegExp('(?:['+Object.keys(NT1).map(c=>'\\'+c).join('')+']', 'g');
  454. //  alert('(?:['+Object.keys(NT1).join('')+']');
  455.  
  456.  
  457.  
  458.     const pat1=new RegExp('['+K(NT1).join('')+']', 'g');
  459.     const pat2=new RegExp('(?:' + K(NT2).join('|') + ')', 'g');
  460. //  const pat3=new RegExp('(['+Object.keys(NT3).map(function(s){return s[0]}).join('')+'])([\u3099\u309B\u309A\u309C])', 'g');
  461.     const pat3=/([ウワヲカキクケコサシスセソタチツテトハヒフヘホうかきくけこさしすせそたちつてとはひふへほ])([\u3099\u309B\u309A\u309C])/g;
  462.     return( s
  463.             .replace(/[A-Za-z0-9]/g, function(s){return String.fromCharCode(s.charCodeAt(0)-0xFEE0)})
  464.             .replace(pat1,function(m){return(m in NT1?NT1[m]:m)})
  465.             .replace(pat2,function(m){return(m in NT2?NT2[m]:m)})
  466.             .replace(pat3,function(m,s1,s2){var x=s1+{'\u3099':'\u309B','\u309B':'\u309B','\u309A':'\u309C','\u309C':'\u309C'}[s2];return(x in NT3?NT3[x]:x)})
  467.             .replace(/([\]\)])([^\s\.])/g, '$1 $2')
  468.             .replace(/([^ ])([\[\(])/g, '$1 $2')
  469.             .replace(/  +/g," ")
  470.         );
  471. }
  472.  
  473. /**
  474.  * WSHを利用し確認メッセージボックスを表示して結果を得る
  475.  *
  476.  * @param {string|null} message メッセージボックスのメッセージ
  477.  * @param {string|null} title   メッセージボックスのタイトル
  478.  * @returns {bool} ユーザーの操作によって「はい」が選択されたならtrue
  479.  *    それ以外ならfalse
  480.  */
  481. function ConfirmBox(message, title)
  482. {
  483.     var ret = "";
  484.     var created = false;
  485.     var rand_str = Math.random().toString(36).slice(-8);
  486.    
  487.     // TEMPフォルダにJSファイルを作成し、cscript.exeで実行
  488.     var tempdir = getEnv("TEMP");
  489.     var tempfile = tempdir+"\\"+"JD_confirm_"+rand_str+".js";
  490.     var scriptdata =
  491.         'var a=WScript.Arguments;if(0<a.Count()){var t="",m=a.Item(0);'
  492.         +'1<a.Count()&&(t=a.Item(1));var s=new ActiveXObject("WScript.Shell");'
  493.         +'1==s.Popup(m,-1,t,33)&&WScript.StdOut.Write("OK");s=null};';
  494.     var f = getPath(tempfile);
  495.     try
  496.     {
  497.         if (f.exists())
  498.         {
  499.             f.deleteFile(tempfile,false);
  500.         }
  501.         writeFile(tempfile, scriptdata, false);
  502.         created = true;
  503.         ret = callSync('cscript.exe',"//NOLOGO",tempfile,_T(message),_T(title));
  504.     }
  505.     catch(e)
  506.     {
  507.     }
  508.     finally
  509.     {
  510.         if (created) deleteFile(tempfile,false);
  511.     }
  512.     return ret=="OK";
  513. }
  514.  
  515. function T(s){return (s===undefined||s===null)?'':s}
  516. function getFileParts(f)
  517. {
  518.     const r = T(f).match(/^(.+)(\.part\d+|\.mp3|\.zip|\.rar|\.tar|\.epub)?(\.[\da-z]{2,6})$/i);
  519.     return (r && !/^\d+$/.test(r[3]))?[T(r[1]),T(r[2]),T(r[3])]:[T(f),'',''];
  520. }
  521. function getFileSpec(f){return (getFileParts(f))[0]}
  522. function getFileSpecFull(f){const x=getFileParts(f);return x[0]+x[1];}
  523. function getExt(f){return (getFileParts(f))[2]}
  524. function getExtFull(f){const x=getFileParts(f);return x[1]+x[2];}
  525.  
  526. /**
  527.  * 先頭括弧内のカテゴリを置換する
  528.  *
  529.  * @param filename {string} 置換前の文字列
  530.  * @param cat {string} 置き換えるカテゴリ文字列
  531.  * @returns {string} 置換後の文字列
  532.  */
  533. function replaceCategory(filename, cat)
  534. {
  535.     if (!cat) return filename;
  536.  
  537. //  var regexp_category_and_author = /^ *\((?:一般|同人|成年|18禁|アニメ|BL|官能|少女|ライトノベル|書籍)[^\)]*\)(?: *\[[^\]]+\])? *$/;
  538.     var regexp_category = /^\((?:一般|同人|成年|18禁|アニメ|BL|官能|少女|ライトノベル|書籍)[^\)]*\) */;
  539.    
  540. //  if (!regexp_category_and_author.test(str) || !regexp_category.test(filename))
  541.     if (!regexp_category.test(cat) || !regexp_category.test(filename))
  542.         return cat.trim() + ' ' + filename;
  543.    
  544.     return filename.replace(regexp_category, cat.trim() + ' ');
  545. }
  546.  
  547.  
  548. /**
  549.  * パッケージ上で右クリックしたのなら選択中のパッケージリストを
  550.  *     リンク上で右クリックしたのなら選択中のリンクリストを返す
  551.  *
  552.  * @function
  553.  * @param   {object} sel lgSelection or dlSelection
  554.  * @returns {object[]}   packages or links or []
  555.  */
  556. function getItemsByContextType(sel)
  557. {
  558.     return (sel.isPackageContext() ? sel.packages : (sel.isLinkContext() ? sel.links : []));
  559. }
  560.  
  561. /**
  562.  * 右クリックされたパッケージかリンクを返す
  563.  *
  564.  * @param   {object} sel  lgSelection or dlSelection
  565.  * @returns {object|null} package or link or null
  566.  */
  567. function getContextItemByContextType(sel)
  568. {
  569.     return (sel.isPackageContext() ? sel.contextPackage : (sel.isLinkContext() ? sel.contextLink : null));
  570. }
  571.  
  572.  
  573. /**
  574.  * 汎用リネーム関数(1アイテム完結)
  575.  *     対象は選択されたリンク
  576.  *
  577.  * @param {(orig_name:string)=>string} callback_func 『リネーム前の名前』を受け取って
  578.  *    『リネーム後の名前』を返すコールバック関数
  579.  *     このコールバック関数は各リンク・パッケージのリネーム前に毎回呼び出される
  580.  */
  581. function genericRenamer(callback_func)
  582. {
  583.     getItemsByContextType(getSelection()).forEach(function(l)
  584.     {
  585.         const orig_name = l.name;
  586.         const new_name = (callback_func)(orig_name);
  587.         if (new_name && orig_name != new_name)
  588.             l.name = new_name;
  589.     });
  590. }
  591.  
  592.  
  593. /**
  594.  *  ファイル名の先頭に指定文字列を追加
  595.  *     ※ (○○) がカテゴリ名っぽかったら、置換モード
  596.  *
  597.  *   メニュー名 "(一般小説) >>"
  598.  *   "[作者名] 作品名.zip" =>  "(一般小説) [作者名] 作品名.zip"
  599.  *
  600.  */
  601. function renameAddMenunameToBegin()
  602. {
  603.     genericRenamer(function(orig)
  604.     {
  605.         const res = Config.name.match(/^(.+?) *>>$/);
  606.         if (!res) return '';
  607.         return replaceCategory(orig, res[1]);
  608.     });
  609. }
  610.  
  611.  
  612. /**
  613.  * ファイル名の末尾にメニュー名を追加
  614.  *    ※ファイル名内にメニュー名があればリネームしない
  615.  *
  616.  *  メニュー名 "<< 寄せ集め"
  617.  *   "[作者名] 作品名.zip" =>  "[作者名] 作品名 寄せ集め.zip"
  618.  *
  619.  */
  620. function renameAddMenunameToEnd()
  621. {
  622.     genericRenamer(function(orig){
  623.         const res = Config.name.match(/^<< *(.+?) *$/);
  624.         if (!res) return '';
  625.         if (orig.indexOf(res[1]) != -1) return '';
  626.         return getFileSpec(orig) + ' ' + res[1] + getExt(orig);
  627.     });
  628. }
  629.  
  630. function renameRemoveTrailingBrackets()
  631. {
  632.     genericRenamer(function(orig){
  633.         var regexp_bottom_various_brackets = / *(?:\[.+?\]|\(.+?\)|【.+?)(.*?)$/;
  634.         return orig_name.replace(regexp_bottom_various_brackets, "$1");
  635.     });
  636. }
  637.  
  638. /**
  639.  * 先頭括弧を後方送り
  640.  *
  641.  *  同人誌用
  642.  *  例)"(C100) [作者名] 作品名.zip" =>  "(同人誌) [作者名] 作品名(C100).zip"
  643.  */
  644. function renameBracketsMoveToEndAndAddDojinshi()
  645. {
  646.     genericRenamer(function(orig){
  647.         var topbrackets_and_otherpart
  648.             = /^(\([^\)]*\)) *(.+?)((?:\.part\d+|\.mp3|\.zip|\.rar|\.tar)?\.[0-9a-z]{2,6})?$/i;
  649.         return replaceCategory(orig.replace(topbrackets_and_otherpart, "$2 $1$3"), '(同人誌)');
  650.     });
  651. }
  652.  
  653.  
  654. function comicNumbering(str)
  655. {
  656.     return (isNaN(str)||str.length==2)?str:(str.length==1)?('0'+str):str.replace(/^0+(\d\d)$/,'$1');
  657. }
  658. function replaceNums(str)
  659. {
  660.     return str.replace(/\d+/g,function(x){return comicNumbering(x);})
  661. }
  662.  
  663. /**
  664.  * 第○○巻化
  665.  *    v02→第02巻、v01-03→第01-03巻 へのリネーム
  666.  *
  667.  * 【右クリックされた項目】
  668.  *   パッケージをクリックしていればパッケージ名を対象とする
  669.  *   リンクをクリックしていればリンク名を対象とする
  670.  *
  671.  * 【選択されている項目】
  672.  *   選択されているパッケージ、リンクの名前をリネーム対象とする
  673.  */
  674.  
  675. function renameNumberingFormat()
  676. {
  677.     genericRenamer(function(orig){
  678.         const anno = {w:' 透かし有り',s:' 寄せ集め',b:' 別スキャン',e:' (完)',A:' 単行本'};
  679.         var dest = '';
  680.         var res = [];
  681.        
  682.         // 既に「巻」がある
  683.         if (res = /^(.+?)(第|全)?(\d{1,3}(?:[-+]\d{1,3})*巻.*)$/.exec(orig))
  684.         {
  685.             if (res[2]) return '';
  686.            
  687.             // 「第」抜け
  688.             dest = res[1]+'第'+res[3];
  689.         }   // 「話」
  690.         else if (res = /^(.+?)[ _]ch?[-\.]?(\d{1,}(?:-\d{1,})?)(.*)$/i.exec(orig))
  691.             dest = res[1] +' 第'+ replaceNums(res[2]) +'話'+ res[3];
  692.         else
  693.         {
  694.             // "Lv." 誤認識を予め排除
  695.             const reg_vol_num = /^(.+?[^Ll])(?:(?:v|[vV]ol(?:ume)?|VOL(?:UME)?)[ \.]? *)(\d{1,3}(?:[-+]\d{1,3})*)(e?A|[esbw]+)?\b(.*?)$/;
  696.             const reg_numonly = /^(.+?) (\d{1,3}(?:[-+]\d{1,3})*)(e?A|[esbw]+)?( .+?)?$/;
  697.            
  698.             if ( (res = reg_vol_num.exec(orig)) || (res = reg_numonly.exec(orig)) )
  699. //              ||  (res = /^(.+?) *(?:[vV](?:[oO][lL][ \.]? *)?)?(\d{1,3}(?:[-+]\d{1,3})*)(e?A|[esbw]+)?\b(.*)$/.exec(orig)) )
  700.             {
  701.                 dest = res[1] +' 第'+ replaceNums(res[2]) +'巻';
  702.                 if (res[3] && (res[3] in anno))
  703.                     dest += anno[res[3]];
  704.                 if (res[4])
  705.                     dest += res[4];
  706.                 dest = dest.replace(/  +/g, ' ').trim();
  707.             }
  708.         }
  709.         return dest;
  710.     });
  711. }
  712.  
  713. /**
  714.  * []内のスワップ
  715.  * [○○×△△]→[△△×○○]
  716.  */
  717. function renameAuthorSwap()
  718. {
  719.     genericRenamer(function(orig){return orig.replace(/\[(.+?)×([^×]+?)\]/, '[$2×$1]');});
  720. }
  721.  
  722. /**
  723.  * []内の末尾切り捨て
  724.  * [○○×△△×□□]→[○○×△△]
  725.  */
  726. function renameAuthorChop()
  727. {
  728.     genericRenamer(function(orig){return orig.replace(/\[(.+?)×(?:[^×]+?)\]/, '[$1]');});
  729. }
  730.  
  731. /**
  732.  * []内のxを×に変換
  733.  * [○○x△△]→[○○×△△]
  734.  */
  735. function renameAuthorXtoBatsu()
  736. {
  737.     genericRenamer(function(orig){return orig.replace(/\[([^×]+?)x([^×]+?)\]/, '[$1×$2]');});
  738. }
  739.  
  740.  
  741. /**
  742.  * 汎用リネーム関数(前処理でヒントを取得してのリネーム)
  743.  *
  744.  * @param {(contextItemName:string)=>string} funcGetHint
  745.  *    右クリック直下のアイテムの名前が渡されるので、
  746.  *    各リンク・パッケージをリネームする際に渡されるヒントを返す
  747.  *    (右クリック直下のアイテムが無ければ、選択された一番上のアイテム)
  748.  * @param {(orig_name:string, hint:string)=>string} funcRename
  749.  *     各リンク・パッケージをリネームする毎に呼び出される(リネーム直前)
  750.  *     各リンク・パッケージそれぞれの変更前の名前とfuncGetHintで
  751.  *     返したヒントが渡されるので、変更後の新しい名前を返す
  752.  */
  753. function genericRenamerWithHint(funcGetHint, funcRename)
  754. {
  755.     const sel = getSelection();
  756.  
  757.     var items=null;
  758.     var contextItemName = '';
  759.     var contextItemUUID = 0;
  760.  
  761. //  * 2024/01/20    希にsel.contextPackageが右クリック直下のパッケージではなく、
  762. //                  最後に選択したパッケージor選択の一番下のパッケージになる場合がある。
  763. //                  JDの再起動で直るが、スクリプトからでは判別不能のため代替措置は無理。
  764.  
  765.     if (sel.isPackageContext())
  766.     {
  767.         items = sel.packages;
  768.         contextItemName = sel.contextPackage.name;
  769.         contextItemUUID = sel.contextPackage.UUID;
  770.     }
  771.     else if (sel.isLinkContext())
  772.     {
  773.         items = sel.links;
  774.         contextItemName = sel.contextLink.name;
  775.         contextItemUUID = sel.contextLink.UUID;
  776.     }
  777.     if (! items || items.length <= 1) return;
  778.     if (contextItemName == '') contextItemName = items[0].name;
  779.    
  780.     const hint = funcGetHint ? ((funcGetHint)(contextItemName)) : contextItemName;
  781.     if (!hint) return;
  782.    
  783.     items.forEach(function(l)
  784.     {
  785.         if (l.UUID == contextItemUUID) return;
  786.        
  787.         const orig_name = l.name;
  788.         const new_name = funcRename ? ((funcRename)(orig_name, hint)) : hint;
  789.         if (new_name && orig_name != new_name)
  790.             l.name = new_name;
  791.     });
  792. }
  793.  
  794.  
  795. function renameToOriginalNameByComment()
  796. {
  797.     getSelection().links.forEach(function (l)
  798.     {
  799.         var r = T(l.getComment()).match(/(?:^|\|)orgfilename=([^\|]*)/);
  800.         if (! r) return;
  801.         l.name = r[1];
  802.     });
  803. }
  804.  
  805. /**
  806.  * 連番桁数揃え
  807.  * ※数字のみのファイル名が対象
  808.  */
  809. function renameSequentialNumbersWithZeroPadding()
  810. {
  811.     const min_length = 3;// 最小桁数
  812.     const regexp_name = /^0*(\d*?)(\..+)?$/i;
  813.  
  814.     var max_length = 0;         // 最大桁数を取得
  815.     getSelection().links.forEach(function (x){
  816.         var r = getFileSpec(x.name).match(regexp_name);
  817.         if (r && (r[1].length > max_length))
  818.             max_length = r[1].length;
  819.     });
  820.     max_length = Math.max(min_length, max_length);
  821.     genericRenamer(
  822.         function(orig){
  823.             return orig.replace(regexp_name,function(m,m1,m2){return zeroPadding(m1.trim(),max_length)+m2});
  824.         }
  825.     );
  826. }
  827.  
  828.  
  829. /**
  830.  * 名前を揃える
  831.  *
  832.  * 【クリックされたファイルの名前】で、【選択されているファイルの名前】を揃える
  833.  *
  834.  * パッケージ名同士を揃える必要はないし、パッケージを跨いで名前を揃える必要もないため
  835.  * 同一パッケージ内のリンクのみ対象
  836.  * 一つのパッケージ内でのみ有効
  837.  */
  838. function renameLinkByLinkName()
  839. {
  840.     var sel = getSelection();
  841.     if (sel.isPackageContext()) return;         // パッケージがクリックされているか
  842.     if (1 != sel.packages.length) return;       // 2つ以上のパッケージが選択されているか
  843.     if (! sel.packages[0].expanded) return;     // パッケージが閉じているか
  844.    
  845.     genericRenamerWithHint(null, null);
  846. }
  847.  
  848.  
  849. /**
  850.  * ファイル名[作者名]を揃える
  851.  *
  852.  * 【クリックされたファイルの[]内】で、【選択されているファイルの[]内】を揃える
  853.  *
  854.  */
  855. function renameLinkByLinkAuthor()
  856. {
  857. // ( ()先頭括弧 繰り返し ) ( [] 繰り返し)
  858.     var regexp_name
  859.         = /^((?:\([^\(\)]+\) *)*)((?:\[[^\[\]]*(?:\[[^\[\]]+\][^\[\]]*)*\] *)*)(.+?)$/i;
  860.     genericRenamerWithHint(
  861.         function(orig){const r=orig.match(regexp_name);return (r&&r[2])?r[2]:null},
  862.         function(orig, hint){return orig.replace(regexp_name,function(m,m1,m2,m3){return (m1?(m1.trim()+' '):'')+hint.trim()+' '+m3.trim()})}
  863.     );
  864. }
  865. /**
  866.  * ファイル名(カテゴリ) [作者名]を揃える
  867.  *
  868.  * 【クリックされたファイルの(○○) [△△]】で、【選択されているファイルの(○○) [△△]】を揃える
  869.  * 無ければ(○○) [△△]を追加する
  870.  *
  871.  */
  872. function renameLinkByLinkCategoryAuthor()
  873. {
  874. // ( ()先頭括弧 繰り返し ) ( [] 繰り返し)
  875.     var regexp_name
  876.         = /^((?:\([^\(\)]+\) *)*)((?:\[[^\[\]]*(?:\[[^\[\]]+\][^\[\]]*)*\] *)*)(.+?)$/i;
  877.     genericRenamerWithHint(
  878.         function(orig){const r=orig.match(regexp_name);return (r&&r[1]&&r[2])?(r[1].trim()+' '+r[2].trim()):null},
  879.         function(orig, hint){return orig.replace(regexp_name,function(m,m1,m2,m3){return hint+' '+m3.trim()})}
  880.     );
  881. }
  882.  
  883. /**
  884.  * ファイル名 タイトルを揃える
  885.  *
  886.  * 【クリックされたファイルの(○○) [△△] □□の□□部分】で、【選択されているファイルの□□】を揃える
  887.  *
  888.  *
  889.  * 巻数話数があるものだけ
  890.  */
  891. function renameLinkByLinkTitle()
  892. {
  893.     var regexp_name
  894.         = /^((?:\([^\(\)]+\) *)*(?:\[[^\[\]]*(?:\[[^\[\]]+\][^\[\]]*)*\] *)*)(.+?) *((?:v(?:ol)?[-\._]?\d+|No[-\._]\d+|[第全]\d+|ch?[-\._]?\d+|[\(\[]| *透かし[あ有]り | *(?:雑誌)?寄せ集め| *別スキャン| *単行本| 20\d\d[-年]?\d+(?:-\d+)?月?号?| \d+).*)(?:\.part\d+\.rar|\.zip|\.[0-9a-z]{2,6})?$/i;
  895.     genericRenamerWithHint(
  896.         function(orig){const r=orig.match(regexp_name);return r?r[2]:null;},
  897.         function(orig, hint){return orig.replace(regexp_name,function (m,m1,m2,m3){return m1.trim()+' '+hint.trim()+' '+m3.trim();});}
  898.     );
  899. }
  900.  
  901. /**
  902.  * ファイル名 (カテゴリ)[作者名]タイトルまで揃える
  903.  *
  904.  * 【クリックされたファイルの(○○) [△△] □□】で、【選択されているファイル】を揃える
  905.  *
  906.  *
  907.  * 巻数話数があるものだけ
  908.  */
  909. function renameLinkByLinkLongTitle()
  910. {
  911.     var regexp_name
  912.         = /^((?:\([^\(\)]+\) *)*(?:\[[^\[\]]*(?:\[[^\[\]]+\][^\[\]]*)*\] *)*.+?) *((?:v(?:ol)?[-\._]?\d+|No[-\._]\d+|[第全]\d+|ch?[-\._]?\d+|[\(\[]| *透かし[あ有]り | *(?:雑誌)?寄せ集め| *別スキャン| *単行本| 20\d\d[-年]?\d+(?:-\d+)?月?号?| \d+).*)(?:\.part\d+\.rar|\.zip|\.[0-9a-z]{2,6})?$/i;
  913.     genericRenamerWithHint(
  914.         function(orig){const r=orig.match(regexp_name);return r?r[1]:null;},
  915.         function(orig, hint){return orig.replace(regexp_name,function(m,m1,m2){return hint.trim()+' '+m2.trim()});}
  916.     );
  917. }
  918.  
  919. //
  920. // パッケージ内リネーム系
  921. //
  922.  
  923. /**
  924.  * このリンクの名前でパッケージ内をリネーム
  925.  *
  926.  * 【右クリックされた項目】--- (複数パッケージを同時処理するため)
  927.  *
  928.  * 【選択されている項目】
  929.  *     『右クリックされたリンクのファイル名』が取得できない場合、
  930.  *     『選択されたリンクのファイル名』で、パッケージ名と
  931.  *     そのパッケージ内全リンクの名前を揃える。
  932.  *
  933.  *  ※ 安全のため、デフォルトパッケージ名に関わる『様々なファイル』『任意』が
  934.  *     先頭に含まれた名前のパッケージはリネームしない
  935.  *     リネームしたければパッケージ名を先に変更しておく
  936.  *
  937.  *  ※ 各パッケージ内で「リンクは一つだけ選択」する
  938.  *     二つ以上のリンクが選択されたパッケージはリネームしない
  939.  *     拡張子は維持
  940.  */
  941. function renamePackageAndLinksByContextLinkName()
  942. {
  943.     const defaultExtension = '.rar';
  944.  
  945.     var bad_package_uiid_list = {};
  946.     var items = getSelection().links;
  947. //  if (0 == items.length) return;
  948.    
  949.     // パッケージが重複するリンク+デフォルト名を弾くため
  950.     // 弾くパッケージUUIDをbad_package_uiid_listに登録
  951.     // 同一パッケージのリンクは配列内で隣接する
  952.     var prev_puuid = 0;
  953.     items.forEach(function (l)
  954.     {
  955.         var p = l.package;
  956.         if (isDefaultFolderName(p.name)
  957.             || p.UUID == prev_puuid)
  958.             bad_package_uiid_list[p.UUID] = 1;
  959.         prev_puuid = p.UUID;
  960.     });
  961.    
  962.     items.forEach(function (l)
  963.     {
  964.         var p = l.package;
  965.        
  966.         // パッケージが重複するリンクorデフォルト名なら、次へ
  967.         if (p.UUID in bad_package_uiid_list) return;
  968.        
  969.         var dest = getFileSpec(l.name);
  970.         if (! dest) return;
  971.        
  972.         // パッケージをリネーム
  973.         p.name = dest;
  974.        
  975.         // デフォルト拡張子を設定
  976.         // パッケージ内の先頭の拡張子をデフォルトにする
  977.         // 無ければ.rar
  978.         var default_ext = '';
  979.         if (!p.downloadLinks.some(function(x){
  980.             var e=getExt(x.name);
  981.             return e && (default_ext=e);
  982.         }))
  983.             default_ext = defaultExtension;
  984.  
  985.         // パッケージ内の全リンクをリネーム
  986.         p.downloadLinks.forEach(function(x)
  987.         {
  988.             //リネーム元リンクをスキップ
  989.             if (x.UUID == l.UUID) return;
  990.            
  991.             // リンクをリネーム
  992.             x.name = dest + (getExt(x.name)||default_ext);
  993.         });
  994.     });
  995. }
  996.  
  997. /**
  998.  * パッケージ名で名前を揃える
  999.  *
  1000.  * 【パッケージ名】で【パッケージ内全ファイルの名前】を揃える
  1001.  *  /^様々なファイル/ なパッケージではリネームしない
  1002.  *
  1003.  * 選択対象:リンクが選択された場合、親パッケージが対象になる
  1004.  *           右クリックは対象とはしない
  1005.  *
  1006.  * ※書庫ファイル以外がある場合はスキップ
  1007.  *
  1008.  */
  1009.  
  1010. function renameLinksByPackageName()
  1011. {
  1012.     /** @type {Array} */
  1013.     var skips = [];
  1014.     /** @type {string} */
  1015.     const defaultExtension = '.rar';
  1016.     getSelection().packages.forEach(function(p)
  1017.     {
  1018.         if ((! p.name) || isDefaultFolderName(p.name)) return;
  1019.        
  1020.         /** @type {string} */
  1021.         var default_ext = '';
  1022.         if (p.downloadLinks.some(function(x){
  1023.             /** @type {string} */
  1024.             var e = getExt(x.name);
  1025.            
  1026.             // 連番画像ファイルなど、パッケージ名でリネームする事故防止
  1027.             if (e != '' && !/\.(?:zip|rar|cbr|cbz|7z|html?|epub)$/i.test(e))
  1028.                 return true;
  1029.  
  1030.             if (e && !default_ext) default_ext = e;
  1031.         }))
  1032.         {
  1033. //          alert(DQ(p.name)+'\r\n\r\nに対するリネームの実行を中止しました\r\n\r\n【パッケージ名で名前を揃える】アクションは\r\n書庫ファイルに対してのみ実行可能です');
  1034.             return;
  1035.         }
  1036.         if (!default_ext) default_ext = defaultExtension;
  1037.        
  1038.         p.downloadLinks.forEach(function(l){l.name = p.name+(getExt(l.name)||default_ext)});
  1039.     });
  1040. }
  1041.  
  1042. /*
  1043. //old version
  1044. function renameLinksByPackageName()
  1045. {
  1046.     getSelection().packages.forEach(function(p)
  1047.     {
  1048.         var dest = p.name;
  1049.         if ((!dest) || isDefaultFolderName(dest))
  1050.             return;
  1051.        
  1052.         if (p.downloadLinks.some(function(l){return false == /(?:^|\.(?:zip|rar|cbr|cbz|7z|html?))$/i.test(getExt(l.name));}))
  1053.         {
  1054.             alert('リネームの実行を中止しました\r\n\r\n【パッケージ名で名前を揃える】アクションは\r\n書庫ファイルに対してのみ実行可能です');
  1055.             return;
  1056.         }
  1057.        
  1058.         var default_ext = '';
  1059.         p.downloadLinks.forEach(function(l)
  1060.         {
  1061.             // 拡張子
  1062.             var ext = getExt(l.name);
  1063.             if (ext == '')
  1064.                 if (default_ext == '')
  1065.                     ext = '.rar';
  1066.                 else
  1067.                     ext = default_ext;
  1068.             if (default_ext == '')
  1069.                 default_ext = ext;
  1070.            
  1071.             l.name = dest + ext;
  1072.         });
  1073.     });
  1074. }
  1075. */
  1076.  
  1077.  
  1078. /**
  1079.  * ファイル名から作者名またはタイトルを返す
  1080.  *
  1081.  * @param {string} filename ファイル名
  1082.  * @param {number} part 部分を指定する
  1083.  * @param {[string='|']} delim 該当するものが複数だった場合に、結合するための区切り文字
  1084.  * @returns {string} 指定された部分を切り出して返す
  1085.  */
  1086. function getKeywordFromFilename(filename, part, delim)
  1087. {
  1088.     var search_word = '';
  1089.     var r = filename.match(/^(?:(?:\([^\(\)]+?\) *)*\[([^\]]+)\] *)?((?:(?:COMIC|\u30B3\u30DF\u30C3\u30AF) *)?([a-z]+(?: [a-z]+?)*?(?:$|\.|(?= v\d+))|(?:[-+ a-z]+)+|[^ -]+))/i);
  1090.     if (r)
  1091.         if (part == gkPart.title
  1092.             || (r[1] && (r[1] == '\u30A2\u30F3\u30BD\u30ED\u30B8\u30FC'
  1093.             || r[1] == '\u96D1\u8A8C')))
  1094.             search_word = r[2].replace(/(?:[!?!?。、]+)$/,'');//検索ワードの末尾感嘆符などは不要
  1095.         else if (r[1])
  1096.         {
  1097.             if (part == gkPart.author)
  1098.                 search_word = r[1].split(/\u00D7|\uFF06|\uFF0F/).join((delim===undefined) ? '|' : delim);
  1099.             else if (part == gkPart.author_1st)
  1100.                 search_word = (r[1].split(/\u00D7|\uFF06|\uFF0F/))[0];
  1101.         }
  1102.     return(search_word.trim());
  1103. }
  1104.  
  1105. /**
  1106.  * ブラウザを開く個数が設定上限を超えているかどうか、
  1107.  *     超えていた場合、
  1108.  *     Config.use_WSHがtrueならば、WSHを使って確認メッセージボックスを出して
  1109.  *     制限を超えて開くかどうかYES/NOの結果をboolで返す
  1110.  *     Config.use_WSHがfalseならば、alertで警告を出してfalseを返す
  1111.  *
  1112.  * @param {number} url_count 上限数
  1113.  * @returns {boolean} true=YES/false=NO|cancel
  1114.  */
  1115. function confirmAboutOpenBrowserCount(url_count)
  1116. {
  1117.     const max_open = Config.open_browser_max;
  1118.     if (! url_count) return false;
  1119.     if (url_count <= max_open) return true;
  1120.     if (! Config.use_WSH)
  1121.         alert(url_count+" 個の検索用URLをブラウザで開こうとしています\r\n"
  1122.             +"現在、一度に開けるのは "+max_open +" 個までです。\r\n"
  1123.             +"設定上限を超えていますので中断します");
  1124.     else if (ConfirmBox(url_count + " 個の検索用URLをブラウザで開こうとしています。\r\n\r\n本当に実行しますか?","確認"))
  1125.         return true;
  1126.     return false;
  1127. }
  1128.  
  1129. function internalOpenUrls(urls)
  1130. {
  1131.     const interval_time = Config.open_browser_interval;
  1132.     if (! (Array.isArray(urls) && confirmAboutOpenBrowserCount(urls.length)))
  1133.         return;
  1134.     urls.forEach(function (url,i)
  1135.     {
  1136.         if (i) sleep(interval_time);
  1137.         callSync( Config.browser_path, '"'+ url +'"');
  1138.     });
  1139. }
  1140.  
  1141. /**
  1142.  * WEBブラウザで検索
  1143.  *
  1144.  * 選択されたリンクのファイル名から検索ワードを取得して、重複を取り除いた後、ブラウザで検索
  1145.  *
  1146.  *
  1147.  * ESL連想配列に
  1148.  * {"メニュー名":["URL(%REP%をキーワードに置換して引数として渡す)", position(gkPart.author|gkPart.title)]}
  1149.  *   メニュー名は、リストビューのコンテキストメニュー(右クリックメニュー)での名前
  1150.  *   URLは、"https://www.google.com/?q=%REP%"で、%REP%を検索ワードに置換してブラウザに渡す
  1151.  *   positionは、ファイル名から取得する検索ワードの位置。
  1152.  *     例)"(カテゴリ) [作者名] ワイのポエム -サブタイトルや- 第101巻.zip"
  1153.  *        gkPart.author:検索ワード="作者名"
  1154.  *        gkPart.title:検索ワード="ワイのポエム"
  1155.  */
  1156. function searchOnWEBSite()
  1157. {
  1158.     const WST = Config.web_search_table;
  1159.     const _name = Config.name;
  1160.     if (! _name in WST) return;
  1161.    
  1162.     const parttype = WST[_name][0];
  1163.     const template_url = (WST[_name][1] || ('https://'+_name+'/?s=%REP%'));
  1164.    
  1165.     var items = [];
  1166.    
  1167.     // 選択されたリンク.親が展開されているか?そのリンクから検索ワード:親の名前から検索ワード
  1168.     getSelection().links.forEach(function (l)
  1169.     {
  1170.         var n = l.package.expanded ? l.name :l.package.name;
  1171.         if (n == '' || isDefaultFolderName(n))
  1172.             return;
  1173.         var search_word = getKeywordFromFilename(n, parttype, ' ');
  1174.         if (parttype != gkPart.title && search_word == '')
  1175.             search_word = getKeywordFromFilename(n, gkPart.title);
  1176.        
  1177.         if (search_word != '') items.push(template_url.replace("%REP%", search_word));
  1178.     });
  1179.  
  1180.     // 順番を維持して重複削除
  1181.     items = items.filter(function(v,i){return items.indexOf(v)===i});
  1182.     internalOpenUrls(items);
  1183. }
  1184.  
  1185. /**
  1186.  * パッケージ内のURLパターンにマッチするリンクを有効/無効にする
  1187.  *(マッチしないパッケージでは何もしない)
  1188.  */
  1189. function setEnableSelectedPackagesWithUrlPattern(enabled, pattern)
  1190. {
  1191.     getSelection().packages.forEach(function(p)
  1192.     {
  1193.         // デフォルトパッケージ名ならスキップ
  1194.         if (isDefaultFolderName(p.name))
  1195.             return;
  1196.        
  1197.         // パターンにマッチしたURLリンクを持たないパッケージはスキップ
  1198.         if (! p.downloadLinks.some(function(l){return pattern.test(l.contentURL)}) )
  1199.             return;
  1200.        
  1201.         p.downloadLinks.forEach(function(l){l.setEnabled( pattern.test(l.contentURL)?enabled:!enabled )});
  1202.     });
  1203. }
  1204.  
  1205. function disableLinksWithoutDashfile()
  1206. {
  1207.     setEnableSelectedPackagesWithUrlPattern(true, /^https?:\/\/dashfile\.net\/.+/);
  1208. }
  1209.  
  1210. function disableLinksWithoutRosefile()
  1211. {
  1212.     setEnableSelectedPackagesWithUrlPattern(true, /^https?:\/\/rosefile\.net\/.+/);
  1213. }
  1214. function disableLinksWithoutBtafile()
  1215. {
  1216.     setEnableSelectedPackagesWithUrlPattern(true, /^https?:\/\/btafile\.com\/.+/);
  1217. }
  1218.  
  1219. function disableLinksWithoutDailyuploads()
  1220. {
  1221.     setEnableSelectedPackagesWithUrlPattern(true, /^https?:\/\/dailyuploads\.net\/.+/);
  1222. }
  1223.  
  1224. function disableLinksUploadbank()
  1225. {
  1226.     setEnableSelectedPackagesWithUrlPattern(false, /^https?:\/\/www\.uploadbank\.com\/.+/);
  1227. }
  1228.  
  1229.  
  1230. function disableLinksMyRules()
  1231. {
  1232.     getSelection().packages.forEach(function(p)
  1233.     {
  1234.         // デフォルトパッケージ名ならスキップ
  1235.         if (isDefaultFolderName(p.name))
  1236.             return;
  1237.        
  1238.         patterns.some(function(pattern)
  1239.         {
  1240.             // パターンにマッチしたURLリンクを持たないパッケージはスキップ
  1241.             if (! p.downloadLinks.some(function(l){return pattern.test(l.contentURL)}) )
  1242.                 return false;
  1243.            
  1244.             p.downloadLinks.forEach(function(l){l.setEnabled( pattern.test(l.contentURL) )});
  1245.             return true;
  1246.         });
  1247.     });
  1248.  
  1249. }
  1250.  
  1251. function disableLinksByHost()
  1252. {
  1253.     var res = Config.name.match(/^([-\da-zA-Z\.]+)を無効$/);
  1254.     if (!res) return '';
  1255.    
  1256.     setEnableSelectedPackagesWithUrlPattern(false, new RegExp('^https?:\/\/' + res[1] + '\/.+'));
  1257. }
  1258.  
  1259.  
  1260. function disableLinksByHostExclude()
  1261. {
  1262.     var res = Config.name.match(/^([-\da-zA-Z\.]+)以外を無効$/);
  1263.     if (!res) return '';
  1264.    
  1265.     setEnableSelectedPackagesWithUrlPattern(true, new RegExp('^https?:\/\/' + res[1] + '\/.+'));
  1266. }
  1267.  
  1268. /**
  1269.  * ブラウザで開く
  1270.  *
  1271.  * 【右クリックされた項目】---
  1272.  * 【選択されている項目】リンクのcontentURLをブラウザで開く
  1273.  *
  1274.  * RegExp url_regexp = (NULL | 正規表現オブジェクト) マッチングするURLのみを開く
  1275.  */
  1276. function openLink(url_regexp)
  1277. {
  1278.     var items = getSelection().links.map(function(l){return l.contentURL});
  1279.     if (url_regexp)
  1280.         items = items.filter(function(url){return url_regexp.test(url)});
  1281.     internalOpenUrls(items);
  1282. }
  1283.  
  1284.  
  1285. /**
  1286.  * uploadbank.comのリンクをブラウザで開く
  1287.  *
  1288.  * 【右クリックされた項目】---
  1289.  * 【選択されている項目】リンクのcontentURLでuploadbank.comのみブラウザで開く
  1290.  */
  1291. function openLinkOnlyUploadbank()
  1292. {
  1293.     openLink(/^https:\/\/www\.uploadbank\.com/i);
  1294. }
  1295.  
  1296. /**
  1297.  * 選択されたリンクの登録元ページをブラウザで開く
  1298.  *
  1299.  * 【右クリックされた項目】---
  1300.  * 【選択されている項目】リンクのoriginURLをブラウザで開く
  1301.  */
  1302. function openSourceURL()
  1303. {
  1304.     var items = {};
  1305.     getSelection().links.forEach(function(l){l.originURL&&(items[l.originURL]=1)});
  1306.     internalOpenUrls(Object.keys(items));
  1307. }
  1308.  
  1309.  
  1310.  
  1311. /**
  1312.  * EveryThingで検索(作者名)
  1313.  *
  1314.  * 【右クリックされた項目】パッケージorリンクの名前から最初の[]内の単語をEveryThingで検索
  1315.  * 【選択されている項目】---
  1316.  *
  1317.  * ※ 複数項目は不要
  1318.  */
  1319. function openEveryThingByAuthorName()
  1320. {
  1321.     openEveryThing(gkPart.author)
  1322. }
  1323. /**
  1324.  * EveryThingで検索(タイトル)
  1325.  *
  1326.  * 【右クリックされた項目】パッケージorリンクの名前から最初の[]の後ろの単語をEveryThingで検索
  1327.  * 【選択されている項目】---
  1328.  */
  1329. function openEveryThingByTitle()
  1330. {
  1331.     openEveryThing(gkPart.title)
  1332. }
  1333.  
  1334. function openEveryThing(part)
  1335. {
  1336.     const contextItem = getContextItemByContextType(getSelection());
  1337.     if (!contextItem) return;
  1338.     const search_word = getKeywordFromFilename(getFileSpec(contextItem.name), part||gkPart.author);
  1339.     if (search_word)
  1340.         callSync( Config.everything_path, '-s', '"'+search_word+'"' );
  1341. }
  1342.  
  1343.  
  1344. /**
  1345.  * 同じ名前のパッケージをまとめる
  1346.  *
  1347.  *
  1348.  * 【右クリックされた項目】---
  1349.  *
  1350.  * 【選択されている項目】選択されているパッケージで同じ名前のものがあればまとめる
  1351.  */
  1352. function moveSameNamePackagesToPackage()
  1353. {
  1354.     var duptable = {};
  1355.     var hitnames = {};
  1356.     getSelection().packages.forEach(function(x)
  1357.     {
  1358.         if (x.name in duptable)
  1359.         {
  1360.             duptable[x.name].push(x.UUID);
  1361.             hitnames[x.name] = duptable[x.name];
  1362.         }
  1363.         else
  1364.             duptable[x.name] = new Array(x.UUID);
  1365.     });
  1366.    
  1367.     for (var x in hitnames)
  1368.     {
  1369.         var link_uuids = [];
  1370.         var p_uuids = hitnames[x];
  1371.         for (var i=1; i < p_uuids.length; i++)
  1372.         {
  1373.             var p = (Config.getPackageByUUID)(p_uuids[i]);
  1374.             if (!p) continue;
  1375.            
  1376.             var items = p.downloadLinks;
  1377.             if (items.length==0) continue;
  1378.            
  1379.             link_uuids = link_uuids.concat( items.map(function (l){return l.UUID;}) );
  1380.         }
  1381.        
  1382.         if (link_uuids)
  1383.             callAPI(Config.APIName, 'moveLinks', link_uuids, null, p_uuids[0]);
  1384.     }
  1385. }
  1386.  
  1387. /**
  1388.  * リンクを同じ名前のパッケージに移動(turbobit)
  1389.  */
  1390. function moveLinkToSameNamePackage()
  1391. {
  1392.     var links = getSelection().links;
  1393.     var all_p = (Config.getAllPackages)();
  1394.    
  1395.     links.forEach(function(l)
  1396.     {
  1397.         var fname = getFileSpec(l.name).replace(/[_ ]/g,'');
  1398.         all_p.some(function(p)
  1399.         {
  1400.             if (fname == p.name.replace(/[_ ]/g,'').replace(/shit|rape/gi, ''))
  1401.             {
  1402.                 callAPI(Config.APIName, 'moveLinks', [l.UUID], null, p.UUID);
  1403.                 return true;
  1404.             }
  1405. //          return true;
  1406.         });
  1407.     });
  1408. }
  1409.  
  1410.  
  1411. /**
  1412.  *
  1413.  * この名前を使用して新しいパッケージに移動
  1414.  *
  1415.  *
  1416.  * 【右クリックされた項目】
  1417.  *   名前/ダウンロードフォルダを新しいパッケージ名に使用
  1418.  *   取得できなければ何もしない
  1419.  *
  1420.  *   ※既存パッケージ名が使用された場合は、既存パッケージへの移動に切替
  1421.  *
  1422.  * 【選択されている項目】
  1423.  *   選択されているリンクを新しいパッケージに移動
  1424.  *   パッケージのみが選択されている項目はパッケージ内全リンクを移動
  1425.  *
  1426.  */
  1427. function moveToNewPackageWithFileName()
  1428. {
  1429.     var sel = getSelection();
  1430.     var expand_after_newpackage = !!Config,expand_after_newpackage;
  1431.     var waittime = Config.waittime_for_expand_package;
  1432.    
  1433.     var newname = '';
  1434.     var dlpath = null;
  1435.     var new_p = null;
  1436.     var context_package_UUID = -1;          // 新規パッケージの位置用
  1437.     var prev_context_package_UUID = -1;     // 新規パッケージの位置用予備
  1438.    
  1439.     if (sel.isLinkContext())
  1440.     {
  1441.         newname = getFileSpec(sel.contextLink.name);
  1442.         dlpath = sel.contextLink.package.downloadFolder;
  1443.         context_package_UUID = sel.contextLink.package.UUID;
  1444.     }
  1445.     else if (sel.isPackageContext())
  1446.     {
  1447.         new_p = sel.contextPackage;
  1448.        
  1449.         newname = new_p.name;
  1450.         dlpath = new_p.downloadFolder;
  1451.         context_package_UUID = new_p.UUID;
  1452.     }
  1453.     if (newname == '') return;
  1454.  
  1455.     var all_p = (Config.getAllPackages)();
  1456.     var prev = 0;
  1457.     if (all_p.some(function(p)
  1458.     {
  1459.         if (p.UUID == context_package_UUID)
  1460.             return true;
  1461.         prev = p.UUID;
  1462.     }))
  1463.         prev_context_package_UUID = prev;
  1464.    
  1465.     // 既存パッケージ名かどうかチェック、ES5なのでArray.prototype.findが無い
  1466.     if (! new_p)
  1467.     {
  1468.         all_p.some(function(p)
  1469.         {
  1470.             if (p.name == newname)
  1471.             {
  1472.                 new_p = p;
  1473.                 return true;
  1474.             }
  1475.         });
  1476.     }
  1477.  
  1478.     var items = sel.links.map(function(l){return l.UUID});
  1479.    
  1480.     if (new_p)
  1481.     {   // 既存パッケージ
  1482.         callAPI(Config.APIName, 'moveLinks', items, null, new_p.UUID);
  1483.         if (context_package_UUID == -1)
  1484.         {
  1485.             sleep(10);
  1486.             callAPI(Config.APIName, 'movePackages', [new_p.UUID], context_package_UUID);
  1487.         }
  1488.        
  1489.         sleep(500);
  1490. //      new_p.setExpanded(expand_after_newpackage);
  1491.     }
  1492.     else
  1493.     {   // 新規パッケージ
  1494.         callAPI(Config.APIName, 'movetoNewPackage', items, null, newname, dlpath);
  1495.         sleep(waittime);// UIが反映されるのはPCスペック依存なので少し待機
  1496.         var l = (Config.getLinkByUUID)(items[0]);
  1497.         callAPI(Config.APIName, 'movePackages', [l.package.UUID], (Config.getPackageByUUID)(context_package_UUID)?context_package_UUID:prev_context_package_UUID);
  1498.         sleep(300);
  1499. //      l.package.setExpanded(expand_after_newpackage);
  1500.     }
  1501. }
  1502.  
  1503. /**
  1504.  * パッケージを並べ替える
  1505.  *
  1506.  * @param {object[]} packs ソートするパッケージの配列(渡された配列自体もソートされる)
  1507.  * @param {(object,object)=>number} condition ソート関数に渡すコールバック関数
  1508.  * @returns {object[]} ソート済みパッケージの配列
  1509.  */
  1510. function sortPackages(packs, condition)
  1511. {
  1512.     if (packs.length <= 1) return packs;
  1513.    
  1514.     packs.sort(condition);
  1515.     for (var i=1; i<packs.length;++i)
  1516.         callAPI(Config.APIName, 'movePackages', [packs[i].UUID], packs[i-1].UUID);
  1517.    
  1518.     return packs;
  1519. }
  1520.  
  1521. /**
  1522.  * 全てのパッケージを並べ替える(メニューを開いたリストビューに対して)
  1523.  *
  1524.  * @param {(object,object)=>number} condition ソート関数に渡すコールバック関数
  1525.  * @returns {object[]} ソート済みパッケージの配列
  1526.  */
  1527. function sortAllPackages(func_cond)
  1528. {
  1529.     return sortPackages(Config.getAllPackages(), func_cond);
  1530. }
  1531.  
  1532. /**
  1533.  * 選択されたパッケージを並べ替える
  1534.  *
  1535.  * @param {(object,object)=>number} condition ソート関数に渡すコールバック関数
  1536.  * @returns {object[]} ソート済みパッケージの配列
  1537.  */
  1538. function sortSelectedPackages(func_cond)
  1539. {
  1540.     const sel_p = getSelection().packages;
  1541.     return (sel_p.length <= 1)
  1542.         ? sortAllPackages(func_cond)
  1543.         : sortPackages(sel_p, func_cond);
  1544. }
  1545.  
  1546. /**
  1547.  * 作者順に並べ替え
  1548.  *
  1549.  * sortPackagesByAuthor
  1550.  */
  1551. function sortPackagesByAuthorAscending()
  1552. {
  1553.     sortAllPackages(sortfunc_CompareAuthorA);
  1554. }
  1555. function sortPackagesByAuthorDescending()
  1556. {
  1557.     sortAllPackages(sortfunc_CompareAuthorB);
  1558. }
  1559. function sortPackagesByAuthor(order)
  1560. {
  1561.     sortAllPackages(order?sortfunc_CompareAuthorB:sortfunc_CompareAuthorA);
  1562. }
  1563.  
  1564. function getAuthorsString(s)
  1565. {
  1566.     const exp_getAuthor = /^(?:\([^\)]+\) *)*\[([^\]]+)\] .+$/;
  1567.     const r = s.match(exp_getAuthor);
  1568.     if (!r) return '';
  1569. //  return r[1].split(/[×&、,,]/).map(function(a){return a.trim()}).filter(function(a){return !!a}).sort().join('×');
  1570. //  return r[1].split(/[×&、,,]/, 2).map(function(a){return a.trim()}).filter(function(a){return !!a}).sort().join('×');
  1571.     return r[1].split(/[×&、,,]/, 2).map(function(a){return a.trim()}).filter(function(a){return !!a}).join('×');
  1572. }
  1573. function sortfunc_CompareAuthorA(a,b)
  1574. {
  1575.     const author_a = getAuthorsString(a.name);
  1576.     const author_b = getAuthorsString(b.name);
  1577.     return author_a.localeCompare(author_b, 'ja');
  1578. }
  1579.  
  1580. function sortfunc_CompareAuthorB(b,a)
  1581. {
  1582.     const author_a = getAuthorsString(a.name);
  1583.     const author_b = getAuthorsString(b.name);
  1584.     return author_a.localeCompare(author_b, 'ja');
  1585. }
  1586.  
  1587. /**
  1588.  * タイトル順に並べ替え
  1589.  *
  1590.  * sortPackagesByTitle
  1591.  */
  1592. function sortPackagesByTitleAscending()
  1593. {
  1594.     sortAllPackages(sortfunc_CompareTitleA);
  1595. }
  1596. function sortPackagesByTitleDescending()
  1597. {
  1598.     sortAllPackages(sortfunc_CompareTitleB);
  1599. }
  1600. function sortPackagesByTitle(order)
  1601. {
  1602.     sortAllPackages(order?sortfunc_CompareTitleB:sortfunc_CompareTitleA);
  1603. }
  1604.  
  1605. function sortfunc_CompareTitleA(a,b)
  1606. {
  1607.     /** @type {RegExp} 先頭から続く(~~)[~~]を削除するパターン */
  1608.     const exp_getTitle = /^(?:\([^\)]+\) *|\[[^\]]+\] *)*/;
  1609.     const title_a = a.name.replace(exp_getTitle, '');
  1610.     const title_b = b.name.replace(exp_getTitle, '');
  1611.    
  1612.     return title_a.localeCompare(title_b, 'ja');
  1613. }
  1614. function sortfunc_CompareTitleB(b,a)
  1615. {
  1616.     /** @type {RegExp} 先頭から続く(~~)[~~]を削除するパターン */
  1617.     const exp_getTitle = /^(?:\([^\)]+\) *|\[[^\]]+\] *)*/;
  1618.     const title_a = a.name.replace(exp_getTitle, '');
  1619.     const title_b = b.name.replace(exp_getTitle, '');
  1620.    
  1621.     return title_a.localeCompare(title_b, 'ja');
  1622. }
  1623.  
  1624. /**
  1625.  * リンクグラバーへ再登録
  1626.  *
  1627.  * 選択されているリンクを全てリンクグラバーに追加
  1628.  * URL、ファイル名、パッケージ名、パッケージのダウンロード先フォルダのみ設定
  1629.  *
  1630.  * 選択されているリンク(パッケージ選択時はパッケージ内全リンク)
  1631.  *
  1632.  * ※ リンクグラバーの登録は時間が掛かる処理であるため、処理時間が異常に長過ぎた場合、
  1633.  *    登録後のファイル名の設定をせずにスクリプトを終了
  1634.  */
  1635. function addLinksToLinkGrabber()
  1636. {
  1637.     if (!isDownloadTable()) return;
  1638.  
  1639.     var sellinks = getSelection().links;
  1640.     if (0 == sellinks.length) return;
  1641.  
  1642.     var jobid_list = [];
  1643.     var url2jobid_table = {};
  1644.  
  1645.     sellinks.forEach(function(l)
  1646.     {
  1647.         //
  1648.         // addLinks時にファイル名を設定できないので、JOBID取得後、
  1649.         // queryLinksでリンクを取得してファイル名を設定する
  1650.         //
  1651.         var jobid = callAPI("linkgrabberv2", "addLinks",
  1652.         {
  1653.             links: (l.pluginURL||l.contentURL),
  1654.             packageName: l.package.name,
  1655.             overwritePackagizerRules:true,
  1656.             destinationFolder:l.package.downloadFolder,
  1657.             deepDecrypt:false,
  1658.             assignJobID:true
  1659.         });
  1660.         if (!jobid) return;
  1661.        
  1662.         jobid_list.push(jobid.id);
  1663.         url2jobid_table[(l.pluginURL||l.contentURL)] = jobid.id;
  1664.     });
  1665.    
  1666.     var link_count = sellinks.length;
  1667.     var limit_time = link_count * 2000 + 30000;
  1668.     var check_interval = 400;
  1669.     var start_time = (new Date()).getTime();
  1670.    
  1671.     while (1)
  1672.     {
  1673.         sleep(check_interval);
  1674.        
  1675.         //
  1676.         // addLinksは即完了しない。 queryLinksでjobUUIDsを指定すると
  1677.         // 完了しているJOBによって登録されたCrawledLinkのリストが返ってくる
  1678.         //
  1679.         var c_links = callAPI('linkgrabberv2','queryLinks',
  1680.         {
  1681.             jobUUIDs:jobid_list,
  1682.             availability:true,
  1683.             url:true,
  1684.             status:true,
  1685.             uuid:true
  1686.         });
  1687.        
  1688.         // 追加されたリンクのURLが同じかどうかで判定し、ファイル名を設定
  1689.         c_links.forEach(function(cl)
  1690.         {
  1691.             const cl_url = cl.url;
  1692.             const l = getCrawledLinkByUUID(cl.uuid);
  1693.             sellinks.some(function(sel_l)
  1694.             {
  1695.                 if (cl_url == sel_l.pluginURL
  1696.                     || cl_url == sel_l.contentURL)
  1697.                 {
  1698.                     l.name = sel_l.name;
  1699.                     return true;
  1700.                 }
  1701.             });
  1702.            
  1703.             // 登録完了したリンクのJobIDを、queryLinksに使うJobID Listから削除
  1704.             const jobid = url2jobid_table[cl_url];
  1705.             if (! jobid) return;
  1706.            
  1707.             jobid_list = jobid_list.filter(function(j){return j != jobid});
  1708.         });
  1709.        
  1710.         // 完了判定
  1711.         link_count -= c_links.length;
  1712.         if (link_count <= 0 || 0 == jobid_list.length) break;
  1713.        
  1714.         // タイムオーバー判定
  1715.         //getCurrentTimeStamp()
  1716.         if (limit_time < ((new Date()).getTime() - start_time))
  1717.         {
  1718.             alert('リンクグラバーへの再登録で時間オーバーになりました');
  1719.             break;
  1720.         }
  1721.        
  1722.         // 残ったJobID ListのqueryLinksへ戻る
  1723.     }
  1724. }
  1725.  
  1726. function openJDCfgFolder()
  1727. {
  1728.     callSync( Config.filer_path,  '"'+JD_HOME+'\\cfg"' );
  1729. }
  1730.  
  1731.  
Add Comment
Please, Sign In to add comment