visuddhinanda 2 months ago
parent
commit
b2adad80ca

+ 65 - 0
api-v12/app/Http/Api/AiAssistantApi.php

@@ -0,0 +1,65 @@
+<?php
+
+namespace App\Http\Api;
+
+use App\Models\AiModel;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Storage;
+use Illuminate\Support\Facades\App;
+
+class AiAssistantApi
+{
+    public static function getByUuid($id)
+    {
+        $user = AiModel::where('uid', $id)->first();
+        return self::userInfo($user);
+    }
+    public static function userInfo($user)
+    {
+        if (!$user) {
+            Log::warning('$user=null;');
+            return [
+                'id' => 0,
+                'nickName' => 'unknown',
+                'userName' => 'unknown',
+                'realName' => 'unknown',
+                'avatar' => '',
+            ];
+        }
+        $data = [
+            'id' => $user->uid,
+            'nickName' => $user->name,
+            'userName' => $user->real_name,
+            'realName' => $user->real_name,
+            'roles' => ['ai'],
+            'sn' => 0,
+        ];
+
+        if ($user->avatar) {
+            $img = str_replace('.jpg', '_s.jpg', $user->avatar);
+            if (App::environment('local')) {
+                $data['avatar'] = Storage::url($img);
+            } else {
+                $data['avatar'] = Storage::temporaryUrl($img, now()->addDays(6));
+            }
+        } else {
+            $logo = null;
+            foreach (config('mint.ai.logo') as $key => $value) {
+                if (strpos($user->model, $key) !== false) {
+                    $logo = $value;
+                    break;
+                } else if (strpos($user->url, $key) !== false) {
+                    $logo = $value;
+                    break;
+                }
+            }
+            $base = config('app.url') . '/assets/images/avatar/';
+            if ($logo === null) {
+                $data['avatar'] = $base . 'ai-assistant.png';
+            } else {
+                $data['avatar'] = $base . $logo;
+            }
+        }
+        return $data;
+    }
+}

+ 185 - 0
api-v12/app/Http/Api/AiTaskPrepare.php

@@ -0,0 +1,185 @@
+<?php
+
+namespace App\Http\Api;
+
+use App\Models\Task;
+use App\Models\PaliText;
+use App\Models\PaliSentence;
+use App\Models\AiModel;
+use App\Models\Sentence;
+
+use App\Http\Api\Mq;
+use App\Http\Api\ChannelApi;
+
+use Illuminate\Support\Facades\Log;
+use App\Http\Controllers\AuthController;
+
+class AiTaskPrepare
+{
+    /**
+     * 读取task信息,将任务拆解为单句小任务
+     *
+     * @param  string  $taskId 任务uuid
+     * @return array 拆解后的提示词数组
+     */
+    public static function translate(string $taskId, bool $send = true)
+    {
+        $task = Task::findOrFail($taskId);
+        $description = $task->description;
+        $rows = explode("\n", $description);
+        $params = [];
+        foreach ($rows as $key => $row) {
+            if (strpos($row, '=') !== false) {
+                $param = explode('=', trim($row, '|'));
+                $params[$param[0]] = $param[1];
+            }
+        }
+        if (!isset($params['type'])) {
+            Log::error('no $params.type');
+            return false;
+        }
+
+        //get sentences in article
+        $sentences = array();
+        $totalLen = 0;
+        switch ($params['type']) {
+            case 'sentence':
+                if (!isset($params['id'])) {
+                    Log::error('no $params.id');
+                    return false;
+                }
+                $sentences[] = explode('-', $params['id']);
+                break;
+            case 'para':
+                if (!isset($params['book']) || !isset($params['paragraphs'])) {
+                    Log::error('no $params.book or paragraphs');
+                    return false;
+                }
+                $sent = PaliSentence::where('book', $params['book'])
+                    ->where('paragraph', $params['paragraphs'])->orderBy('word_begin')->get();
+                foreach ($sent as $key => $value) {
+                    $sentences[] = [
+                        'id' => [
+                            $value->book,
+                            $value->paragraph,
+                            $value->word_begin,
+                            $value->word_end,
+                        ],
+                        'strlen' => $value->length
+                    ];
+                    $totalLen += $value->length;
+                }
+                break;
+            case 'chapter':
+                if (!isset($params['book']) || !isset($params['paragraphs'])) {
+                    Log::error('no $params.book or paragraphs');
+                    return false;
+                }
+                $chapterLen = PaliText::where('book', $params['book'])
+                    ->where('paragraph', $params['paragraphs'])->value('chapter_len');
+                $sent = PaliSentence::where('book', $params['book'])
+                    ->whereBetween('paragraph', [$params['paragraphs'], $params['paragraphs'] + $chapterLen - 1])
+                    ->orderBy('paragraph')
+                    ->orderBy('word_begin')->get();
+                foreach ($sent as $key => $value) {
+                    $sentences[] = [
+                        'id' => [
+                            $value->book,
+                            $value->paragraph,
+                            $value->word_begin,
+                            $value->word_end,
+                        ],
+                        'strlen' => $value->length
+                    ];
+                    $totalLen += $value->length;
+                }
+                break;
+            default:
+                return false;
+                break;
+        }
+
+        //render prompt
+        $mdRender = new MdRender([
+            'format' => 'prompt',
+            'footnote' => false,
+            'paragraph' => false,
+        ]);
+        $m = new \Mustache_Engine(array(
+            'entity_flags' => ENT_QUOTES,
+            'escape' => function ($value) {
+                return $value;
+            }
+        ));
+
+        # ai model
+        $aiModel = AiModel::findOrFail($task->executor_id);
+        $modelToken = AuthController::getUserToken($aiModel->uid);
+        $aiModel['token'] = $modelToken;
+        $sumLen = 0;
+        $mqData = [];
+        foreach ($sentences as $key => $sentence) {
+            $sumLen += $sentence['strlen'];
+            $sid = implode('-', $sentence['id']);
+            Log::debug($sid);
+            $sentChannelInfo = explode('@', $params['channel']);
+            $channelId = $sentChannelInfo[0];
+            $data = [];
+            $data['origin'] = '{{' . $sid . '}}';
+            $data['translation'] = '{{sent|id=' . $sid;
+            $data['translation'] .= '|channel=' . $channelId;
+            $data['translation'] .= '|text=translation}}';
+            if (isset($params['nissaya']) && !empty($params['nissaya'])) {
+                $nissayaChannel = explode('@', $params['nissaya']);
+                $channelInfo = ChannelApi::getById($nissayaChannel[0]);
+                if ($channelInfo) {
+                    //查看句子是否存在
+                    $nissayaSent = Sentence::where('book_id', $sentence['id'][0])
+                        ->where('paragraph', $sentence['id'][1])
+                        ->where('word_start', $sentence['id'][2])
+                        ->where('word_end', $sentence['id'][3])
+                        ->where('channel_uid', $nissayaChannel[0])->first();
+                    if ($nissayaSent && !empty($nissayaSent->content)) {
+                        $nissayaData = [];
+                        $nissayaData['channel'] = $channelInfo;
+                        $nissayaData['data'] = '{{sent|id=' . $sid;
+                        $nissayaData['data'] .= '|channel=' . $nissayaChannel[0];
+                        $nissayaData['data'] .= '|text=translation}}';
+                        $data['nissaya'] = $nissayaData;
+                    }
+                }
+            }
+
+            //Log::debug('mustache render', ['tpl' => $description, 'data' => $data]);
+            $content = $m->render($description, $data);
+            $prompt = $mdRender->convert($content, []);
+            //gen mq
+            $aiMqData = [
+                'model' => $aiModel,
+                'task' => [
+                    'info' => $task,
+                    'progress' => [
+                        'current' => $sumLen,
+                        'total' => $totalLen
+                    ],
+                ],
+                'prompt' => $prompt,
+                'sentence' => [
+                    'book_id' => $sentence['id'][0],
+                    'paragraph' => $sentence['id'][1],
+                    'word_start' => $sentence['id'][2],
+                    'word_end' => $sentence['id'][3],
+                    'channel_uid' => $channelId,
+                    'content' => $prompt,
+                    'content_type' => 'markdown',
+                    'access_token' => $sentChannelInfo[1] ?? $params['token'],
+                ],
+            ];
+            array_push($mqData, $aiMqData);
+        }
+        if ($send) {
+            Mq::publish('ai_translate', $mqData);
+        }
+        return $mqData;
+    }
+}

+ 42 - 0
api-v12/app/Http/Api/AuthApi.php

@@ -0,0 +1,42 @@
+<?php
+
+namespace App\Http\Api;
+
+use Illuminate\Support\Facades\Log;
+use Illuminate\Http\Request;
+use Firebase\JWT\JWT;
+use Firebase\JWT\Key;
+
+class AuthApi
+{
+    public static function getToken(Request $request)
+    {
+        $token = $request->bearerToken();
+        return $token;
+    }
+    public static function current(Request $request)
+    {
+        $token = $request->bearerToken();
+        if ($token) {
+            try {
+                $jwt = JWT::decode($token, new Key(config('app.key'), 'HS512'));
+            } catch (\Exception $e) {
+                return false;
+            }
+            if ($jwt->exp < time()) {
+                //过期
+                return false;
+            } else {
+                //有效的token
+                return ['user_uid' => $jwt->uid, 'user_id' => $jwt->id];
+            }
+        } else if (isset($_COOKIE['user_uid'])) {
+            return [
+                'user_uid' => $_COOKIE['user_uid'],
+                'user_id' => $_COOKIE['user_id']
+            ];
+        } else {
+            return false;
+        }
+    }
+}

+ 3539 - 0
api-v12/app/Http/Api/BookTitle.php

@@ -0,0 +1,3539 @@
+<?php
+namespace App\Http\Api;
+
+class BookTitle{
+    //缅文书名缩写映射表
+    public static function my(){
+        return [
+  [
+    "title1" => "abhi,dha",
+    "title2" => "အဘိ၊ဓ",
+    "bookname" => "dhammasaṅgaṇīpāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "abhi,1",
+    "title2" => "အဘိ၊၁",
+    "bookname" => "dhammasaṅgaṇīpāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "abhi,ka",
+    "title2" => "အဘိ၊က",
+    "bookname" => "kathāvatthupāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "abhi,pu",
+    "title2" => "အဘိ၊ပု",
+    "bookname" => "puggalapaññattipāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "abhi,ṭṭha,1",
+    "title2" => "အဘိ၊ဋ္ဌ၊၁",
+    "bookname" => "dhammasaṅgaṇī-aṭṭhakathā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "abhi,ṭṭha,2",
+    "title2" => "အဘိ၊ဋ္ဌ၊၂",
+    "bookname" => "vibhaṅga-aṭṭhakathā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "abhi,ṭṭha,3",
+    "title2" => "အဘိ၊ဋ္ဌ၊၃",
+    "bookname" => "pañcapakaraṇa-aṭṭhakathā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "abhi,vi",
+    "title2" => "အဘိ၊ဝိ",
+    "bookname" => "vibhaṅgapāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "abhi,vi",
+    "title2" => "အဘိ၊၂",
+    "bookname" => "vibhaṅgapāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "aṃ,1",
+    "title2" => "အံ၊၁",
+    "bookname" => "aṅguttaranikāya",
+    "volume" => 1
+  ],
+  [
+    "title1" => "aṃ,2",
+    "title2" => "အံ၊၂",
+    "bookname" => "aṅguttaranikāya",
+    "volume" => 2
+  ],
+  [
+    "title1" => "aṃ,3",
+    "title2" => "အံ၊၃",
+    "bookname" => "aṅguttaranikāya",
+    "volume" => 3
+  ],
+  [
+    "title1" => "aṃ,ṭī,1",
+    "title2" => "အံ၊ဋီ၊၁",
+    "bookname" => "aṅguttaranikāya-ṭīkā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "aṃ,ṭī,2",
+    "title2" => "အံ၊ဋီ၊၂",
+    "bookname" => "aṅguttaranikāya-ṭīkā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "aṃ,ṭī,3",
+    "title2" => "အံ၊ဋီ၊၃",
+    "bookname" => "aṅguttaranikāya-ṭīkā",
+    "volume" => 3
+  ],
+  [
+    "title1" => "aṃ,ṭṭha,1",
+    "title2" => "အံ၊ဋ္ဌ၊၁",
+    "bookname" => "aṅguttaranikāya-aṭṭhakathā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "aṃ,ṭṭha,2",
+    "title2" => "အံ၊ဋ္ဌ၊၂",
+    "bookname" => "aṅguttaranikāya-aṭṭhakathā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "aṃ,ṭṭha,3",
+    "title2" => "အံ၊ဋ္ဌ၊၃",
+    "bookname" => "aṅguttaranikāya-aṭṭhakathā",
+    "volume" => 3
+  ],
+  [
+    "title1" => "anuṭī,2",
+    "title2" => "အနုဋီ၊၂",
+    "bookname" => "vibhaṅga-anuṭīkā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "anuṭī,3",
+    "title2" => "အနုဋီ၊၃",
+    "bookname" => "pañcapakaraṇa-anuṭīkā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "apa,1",
+    "title2" => "အပ၊၁",
+    "bookname" => "therāpadānapāḷi",
+    "volume" => 1
+  ],
+  [
+    "title1" => "apa,2",
+    "title2" => "အပ၊၂",
+    "bookname" => "therāpadānapāḷi",
+    "volume" => 2
+  ],
+  [
+    "title1" => "apa,ṭṭha,1",
+    "title2" => "အပ၊ဋ္ဌ၊၁",
+    "bookname" => "apadāna-aṭṭhakathā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "apa,ṭṭha,2",
+    "title2" => "အပ၊ဋ္ဌ၊၂",
+    "bookname" => "apadāna-aṭṭhakathā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "buddhavaṃ,ṭṭha",
+    "title2" => "ဗုဒ္ဓဝံ၊ဋ္ဌ",
+    "bookname" => "buddhavaṃsa-aṭṭhakathā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "cariyā,ṭṭha",
+    "title2" => "စရိယာ၊ဋ္ဌ",
+    "bookname" => "cariyāpiṭaka-aṭṭhakathā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "cūḷani",
+    "title2" => "စူဠနိ",
+    "bookname" => "cūḷaniddesapāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "cūḷani,ṭṭha",
+    "title2" => "စူဠနိ၊ဋ္ဌ",
+    "bookname" => "cūḷaniddesa-aṭṭhakathā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "dhamma",
+    "title2" => "ဓမ္မ",
+    "bookname" => "dhammapadapāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "dhamma,ṭṭha,1",
+    "title2" => "ဓမ္မ၊ဋ္ဌ၊၁",
+    "bookname" => "dhammapada-aṭṭhakathā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "dhamma,ṭṭha,2",
+    "title2" => "ဓမ္မ၊ဋ္ဌ၊၂",
+    "bookname" => "dhammapada-aṭṭhakathā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "dī,1",
+    "title2" => "ဒီ၊၁",
+    "bookname" => "dīghanikāya",
+    "volume" => 1
+  ],
+  [
+    "title1" => "dī,2",
+    "title2" => "ဒီ၊၂",
+    "bookname" => "dīghanikāya",
+    "volume" => 2
+  ],
+  [
+    "title1" => "dī,3",
+    "title2" => "ဒီ၊၃",
+    "bookname" => "dīghanikāya",
+    "volume" => 3
+  ],
+  [
+    "title1" => "dī,ṭī,1",
+    "title2" => "ဒီ၊ဋီ၊၁",
+    "bookname" => "dīghanikāya-ṭīkā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "dī,ṭī,2",
+    "title2" => "ဒီ၊ဋီ၊၂",
+    "bookname" => "dīghanikāya-ṭīkā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "dī,ṭī,3",
+    "title2" => "ဒီ၊ဋီ၊၃",
+    "bookname" => "dīghanikāya-ṭīkā",
+    "volume" => 3
+  ],
+  [
+    "title1" => "dī,ṭṭha,1",
+    "title2" => "ဒီ၊ဋ္ဌ၊၁",
+    "bookname" => "dīghanikāya-aṭṭhakathā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "dī,ṭṭha,2",
+    "title2" => "ဒီ၊ဋ္ဌ၊၂",
+    "bookname" => "dīghanikāya-aṭṭhakathā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "dī,ṭṭha,3",
+    "title2" => "ဒီ၊ဋ္ဌ၊၃",
+    "bookname" => "dīghanikāya-aṭṭhakathā",
+    "volume" => 3
+  ],
+  [
+    "title1" => "itivuta,ṭṭha",
+    "title2" => "ဣတိဝုတ၊ဋ္ဌ",
+    "bookname" => "itivuttaka-aṭṭhakathā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "jā,1",
+    "title2" => "ဇာ၊၁",
+    "bookname" => "jātakapāḷi",
+    "volume" => 1
+  ],
+  [
+    "title1" => "jā,2",
+    "title2" => "ဇာ၊၂",
+    "bookname" => "jātakapāḷi",
+    "volume" => 2
+  ],
+  [
+    "title1" => "jā,ṭṭha,1",
+    "title2" => "ဇာ၊ဋ္ဌ၊၁",
+    "bookname" => "jātaka-aṭṭhakathā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "jā,ṭṭha,2",
+    "title2" => "ဇာ၊ဋ္ဌ၊၂",
+    "bookname" => "jātaka-aṭṭhakathā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "jā,ṭṭha,3",
+    "title2" => "ဇာ၊ဋ္ဌ၊၃",
+    "bookname" => "jātaka-aṭṭhakathā",
+    "volume" => 3
+  ],
+  [
+    "title1" => "jā,ṭṭha,4",
+    "title2" => "ဇာ၊ဋ္ဌ၊၄",
+    "bookname" => "jātaka-aṭṭhakathā",
+    "volume" => 4
+  ],
+  [
+    "title1" => "jā,ṭṭha,5",
+    "title2" => "ဇာ၊ဋ္ဌ၊၅",
+    "bookname" => "jātaka-aṭṭhakathā",
+    "volume" => 5
+  ],
+  [
+    "title1" => "jā,ṭṭha,6",
+    "title2" => "ဇာ၊ဋ္ဌ၊၆",
+    "bookname" => "jātaka-aṭṭhakathā",
+    "volume" => 6
+  ],
+  [
+    "title1" => "jā,ṭṭha,7",
+    "title2" => "ဇာ၊ဋ္ဌ၊၇",
+    "bookname" => "jātaka-aṭṭhakathā",
+    "volume" => 7
+  ],
+  [
+    "title1" => "jātaka,1",
+    "title2" => "ဇာတက၊၁",
+    "bookname" => "jātakapāḷi",
+    "volume" => 1
+  ],
+  [
+    "title1" => "jātaka,2",
+    "title2" => "ဇာတက၊၂",
+    "bookname" => "jātakapāḷi",
+    "volume" => 2
+  ],
+  [
+    "title1" => "khuddaka",
+    "title2" => "ခုဒ္ဒက",
+    "bookname" => "khuddakapāṭhapāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "khuddaka,ṭṭha",
+    "title2" => "ခုဒ္ဒက၊ဋ္ဌ",
+    "bookname" => "khuddakapāṭha-aṭṭhakathā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "ma,1",
+    "title2" => "မ၊၁",
+    "bookname" => "majjhimanikaya",
+    "volume" => 1
+  ],
+  [
+    "title1" => "ma,2",
+    "title2" => "မ၊၂",
+    "bookname" => "majjhimanikaya",
+    "volume" => 2
+  ],
+  [
+    "title1" => "ma,3",
+    "title2" => "မ၊၃",
+    "bookname" => "majjhimanikaya",
+    "volume" => 3
+  ],
+  [
+    "title1" => "ma,ṭī,1",
+    "title2" => "မ၊ဋီ၊၁",
+    "bookname" => "majjhimanikaya-ṭīkā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "ma,ṭī,2",
+    "title2" => "မ၊ဋီ၊၂",
+    "bookname" => "majjhimanikaya-ṭīkā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "ma,ṭī,3",
+    "title2" => "မ၊ဋီ၊၃",
+    "bookname" => "majjhimanikaya-ṭīkā",
+    "volume" => 3
+  ],
+  [
+    "title1" => "ma,ṭṭha,1",
+    "title2" => "မ၊ဋ္ဌ၊၁",
+    "bookname" => "majimanikaya-aṭṭhakathā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "ma,ṭṭha,2",
+    "title2" => "မ၊ဋ္ဌ၊၂",
+    "bookname" => "majimanikaya-aṭṭhakathā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "ma,ṭṭha,3",
+    "title2" => "မ၊ဋ္ဌ၊၃",
+    "bookname" => "majimanikaya-aṭṭhakathā",
+    "volume" => 3
+  ],
+  [
+    "title1" => "ma,ṭṭha,4",
+    "title2" => "မ၊ဋ္ဌ၊၄",
+    "bookname" => "majimanikaya-aṭṭhakathā",
+    "volume" => 4
+  ],
+  [
+    "title1" => "mahāni",
+    "title2" => "မဟာနိ",
+    "bookname" => "mahāniddesapāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "mahāni,ṭṭha",
+    "title2" => "မဟာနိ၊ဋ္ဌ",
+    "bookname" => "mahāniddesa-aṭṭhakathā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "milinda",
+    "title2" => "မိလိန္ဒ",
+    "bookname" => "milindapañhapāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "mūlaṭī,1",
+    "title2" => "မူလဋီ၊၁",
+    "bookname" => "dhammasaṅgaṇī-mūlaṭīkā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "mūlaṭī,2",
+    "title2" => "မူလဋီ၊၂",
+    "bookname" => "vibhaṅga-mūlaṭīkā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "netti",
+    "title2" => "နေတ္တိ",
+    "bookname" => "nettippakaraṇapāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "netti,ṭṭha",
+    "title2" => "နေတ္တိ၊ဋ္ဌ",
+    "bookname" => "nettippakaraṇa-aṭṭhakathā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "netti,vibhā",
+    "title2" => "နေတ္တိ၊ဝိဘာ",
+    "bookname" => "nettivibhāvinī",
+    "volume" => 0
+  ],
+  [
+    "title1" => "paṭisaṃ",
+    "title2" => "ပဋိသံ",
+    "bookname" => "paṭisambhidāmaggapāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "paṭisaṃ,ṭṭha,1",
+    "title2" => "ပဋိသံ၊ဋ္ဌ၊၁",
+    "bookname" => "paṭisambhidāmagga-aṭṭhakathā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "paṭisaṃ,ṭṭha,2",
+    "title2" => "ပဋိသံ၊ဋ္ဌ၊၂",
+    "bookname" => "paṭisambhidāmagga-aṭṭhakathā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "paṭisambhidāmaga,pāḷi",
+    "title2" => "ပဋိသမ္ဘိဒါမဂ်၊ပါဠိ",
+    "bookname" => "paṭisambhidāmaggapāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "paṭṭhāna,1",
+    "title2" => "ပဋ္ဌာန၊၁",
+    "bookname" => "paṭṭhānapāḷi",
+    "volume" => 1
+  ],
+  [
+    "title1" => "paṭṭhāna,2",
+    "title2" => "ပဋ္ဌာန၊၂",
+    "bookname" => "paṭṭhānapāḷi",
+    "volume" => 2
+  ],
+  [
+    "title1" => "paṭṭhāna,3",
+    "title2" => "ပဋ္ဌာန၊၃",
+    "bookname" => "paṭṭhānapāḷi",
+    "volume" => 3
+  ],
+  [
+    "title1" => "paṭṭhāna,4",
+    "title2" => "ပဋ္ဌာန၊၄",
+    "bookname" => "paṭṭhānapāḷi",
+    "volume" => 4
+  ],
+  [
+    "title1" => "peta,ṭṭha",
+    "title2" => "ပေတ၊ဋ္ဌ",
+    "bookname" => "petavatthupāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "peṭako",
+    "title2" => "ပေဋကော",
+    "bookname" => "peṭakopadesapāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "saṃ,1",
+    "title2" => "သံ၊၁",
+    "bookname" => "saṃyuttanikāya",
+    "volume" => 1
+  ],
+  [
+    "title1" => "saṃ,2",
+    "title2" => "သံ၊၂",
+    "bookname" => "saṃyuttanikāya",
+    "volume" => 2
+  ],
+  [
+    "title1" => "saṃ,3",
+    "title2" => "သံ၊၃",
+    "bookname" => "saṃyuttanikāya",
+    "volume" => 3
+  ],
+  [
+    "title1" => "saṃ,ṭī,1",
+    "title2" => "သံ၊ဋီ၊၁",
+    "bookname" => "saṃyuttanikāya-ṭīkā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "saṃ,ṭī,2",
+    "title2" => "သံ၊ဋီ၊၂",
+    "bookname" => "saṃyuttanikāya-ṭīkā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "saṃ,ṭṭha,1",
+    "title2" => "သံ၊ဋ္ဌ၊၁",
+    "bookname" => "saṃyuttanikāya-aṭṭhakathā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "saṃ,ṭṭha,2",
+    "title2" => "သံ၊ဋ္ဌ၊၂",
+    "bookname" => "saṃyuttanikāya-aṭṭhakathā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "saṃ,ṭṭha,3",
+    "title2" => "သံ၊ဋ္ဌ၊၃",
+    "bookname" => "saṃyuttanikāya-aṭṭhakathā",
+    "volume" => 3
+  ],
+  [
+    "title1" => "sārattha,ṭī,1",
+    "title2" => "သာရတ္ထ၊ဋီ၊၁",
+    "bookname" => "sāratthadīpanī-ṭīkā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "sārattha,ṭī,2",
+    "title2" => "သာရတ္ထ၊ဋီ၊၂",
+    "bookname" => "sāratthadīpanī-ṭīkā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "sārattha,ṭī,3",
+    "title2" => "သာရတ္ထ၊ဋီ၊၃",
+    "bookname" => "sāratthadīpanī-ṭīkā",
+    "volume" => 3
+  ],
+  [
+    "title1" => "suttani,ṭṭha,1",
+    "title2" => "သုတ္တနိ၊ဋ္ဌ၊၁",
+    "bookname" => "suttanipāta-aṭṭhakathā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "suttani,ṭṭha,2",
+    "title2" => "သုတ္တနိ၊ဋ္ဌ၊၂",
+    "bookname" => "suttanipāta-aṭṭhakathā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "suttaniṭṭha,2",
+    "title2" => "သုတ္တနိဋ္ဌ၊၂",
+    "bookname" => "suttanipāta-aṭṭhakathā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "thera,ṭṭha,1",
+    "title2" => "ထေရ၊ဋ္ဌ၊၁",
+    "bookname" => "theragāthā-aṭṭhakathā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "thera,ṭṭha,2",
+    "title2" => "ထေရ၊ဋ္ဌ၊၂",
+    "bookname" => "theragāthā-aṭṭhakathā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "theragāthā",
+    "title2" => "ထေရဂါထာ",
+    "bookname" => "theragāthāpāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "therī",
+    "title2" => "ထေရီ",
+    "bookname" => "therīgāthāpāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "therī,ṭṭha",
+    "title2" => "ထေရီ၊ဋ္ဌ",
+    "bookname" => "therīgāthā-aṭṭhakathā",
+    "volume" => 0
+  ],
+  [ "title1" => "udāna", "title2" => "ဥဒါန", "bookname" => "udānapāḷi", "volume" => 0 ],
+  [
+    "title1" => "udāna,ṭṭha",
+    "title2" => "ဥဒါန၊ဋ္ဌ",
+    "bookname" => "udāna-aṭṭhakathā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "vajira",
+    "title2" => "ဝဇိရ",
+    "bookname" => "vajirabuddhi-ṭīkā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "vajira,ṭī",
+    "title2" => "ဝဇိရ၊ဋီ",
+    "bookname" => "vajirabuddhi-ṭīkā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "vi,1",
+    "title2" => "ဝိ၊၁",
+    "bookname" => "pārājikapāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "vi,2",
+    "title2" => "ဝိ၊၂",
+    "bookname" => "pācittiyapāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "vi,3",
+    "title2" => "ဝိ၊၃",
+    "bookname" => "mahāvaggapāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "vi,4",
+    "title2" => "ဝိ၊၄",
+    "bookname" => "cūḷavaggapāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "vi,5",
+    "title2" => "ဝိ၊၅",
+    "bookname" => "parivārapāḷi",
+    "volume" => 0
+  ],
+  [
+    "title1" => "vi,ṭṭha,1",
+    "title2" => "ဝိ၊ဋ္ဌ၊၁",
+    "bookname" => "pārājikakaṇḍa-aṭṭhakathā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "vi,ṭṭha,2",
+    "title2" => "ဝိ၊ဋ္ဌ၊၂",
+    "bookname" => "pārājikakaṇḍa-aṭṭhakathā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "vi,ṭṭha,3",
+    "title2" => "ဝိ၊ဋ္ဌ၊၃",
+    "bookname" => "pācittiyādi-aṭṭhakathā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "vi,ṭṭha,4",
+    "title2" => "ဝိ၊ဋ္ဌ၊၄",
+    "bookname" => "cūḷavaggādi-aṭṭhakathā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "vimāna,ṭṭha",
+    "title2" => "ဝိမာန၊ဋ္ဌ",
+    "bookname" => "vimānavatthu-aṭṭhakathā",
+    "volume" => 0
+  ],
+  [
+    "title1" => "vimati,1",
+    "title2" => "ဝိမတိ၊၁",
+    "bookname" => "vimativinodanī-ṭīkā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "vimati,2",
+    "title2" => "ဝိမတိ၊၂",
+    "bookname" => "vimativinodanī-ṭīkā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "visuddhi,1",
+    "title2" => "ဝိသုဒ္ဓိ၊၁",
+    "bookname" => "visuddhimagga",
+    "volume" => 1
+  ],
+  [
+    "title1" => "visuddhi,2",
+    "title2" => "ဝိသုဒ္ဓိ၊၂",
+    "bookname" => "visuddhimagga",
+    "volume" => 2
+  ],
+  [
+    "title1" => "visuddhi,ṭī,1",
+    "title2" => "ဝိသုဒ္ဓိ၊ဋီ၊၁",
+    "bookname" => "visuddhimagga-mahāṭīkā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "visuddhi,ṭī,2",
+    "title2" => "ဝိသုဒ္ဓိ၊ဋီ၊၂",
+    "bookname" => "visuddhimagga-mahāṭīkā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "visuddhi,ṭī,1",
+    "title2" => "မဟာဋီ၊၁",
+    "bookname" => "visuddhimagga-mahāṭīkā",
+    "volume" => 1
+  ],
+  [
+    "title1" => "visuddhi,ṭī,2",
+    "title2" => "မဟာဋီ၊၂",
+    "bookname" => "visuddhimagga-mahāṭīkā",
+    "volume" => 2
+  ],
+  [
+    "title1" => "yamaka,1",
+    "title2" => "ယမက၊၁",
+    "bookname" => "yamakapāḷi",
+    "volume" => 1
+  ],
+  [
+    "title1" => "yamaka,2",
+    "title2" => "ယမက၊၂",
+    "bookname" => "yamakapāḷi",
+    "volume" => 2
+  ],
+  [
+    "title1" => "yamaka,3",
+    "title2" => "ယမက၊၃",
+    "bookname" => "yamakapāḷi",
+    "volume" => 3
+  ]
+  ];
+
+    }
+    public static function get(){
+        return [
+  [
+    "id" => 1,
+    "book" => 1,
+    "name" => "Namakkārapāḷi",
+    "term" => "namakkārapāḷi",
+    "v_title" => "namakkārapāḷi",
+    "m_title" => "namakkārapāḷi",
+    "p_title" => "namakkārapāḷi",
+    "abbr" => "namakkārapāḷi"
+  ],
+  [
+    "id" => 2,
+    "book" => 1,
+    "name" => "namakkāraṭīkā",
+    "term" => "namakkāraṭīkā",
+    "v_title" => "namakkāraṭīkā",
+    "m_title" => "namakkāraṭīkā",
+    "p_title" => "namakkāraṭīkā",
+    "abbr" => "namakkāraṭīkā"
+  ],
+  [
+    "id" => 3,
+    "book" => 2,
+    "name" => "Mahāpaṇāmapāṭha(Buddhavandanā)",
+    "term" => "mahāpaṇāmapāṭha",
+    "v_title" => "mahāpaṇāmapāṭha",
+    "m_title" => "mahāpaṇāmapāṭha",
+    "p_title" => "mahāpaṇāmapāṭha",
+    "abbr" => "mahāpaṇāmapāṭha"
+  ],
+  [
+    "id" => 4,
+    "book" => 2,
+    "name" => "tigumbacetiya thomanā",
+    "term" => "tigumbacetiya thomanā",
+    "v_title" => "tigumbacetiya thomanā",
+    "m_title" => "tigumbacetiya thomanā",
+    "p_title" => "tigumbacetiya thomanā",
+    "abbr" => "tigumbacetiya thomanā"
+  ],
+  [
+    "id" => 5,
+    "book" => 2,
+    "name" => "vāsamālinīkya",
+    "term" => "vāsamālinīkya",
+    "v_title" => "vāsamālinīkya",
+    "m_title" => "vāsamālinīkya",
+    "p_title" => "vāsamālinīkya",
+    "abbr" => "vāsamālinīkya"
+  ],
+  [
+    "id" => 6,
+    "book" => 3,
+    "name" => "Lakkhaṇāto",
+    "term" => "lakkhaṇāto",
+    "v_title" => "lakkhaṇāto",
+    "m_title" => "lakkhaṇāto",
+    "p_title" => "lakkhaṇāto",
+    "abbr" => "lakkhaṇāto"
+  ],
+  [
+    "id" => 7,
+    "book" => 4,
+    "name" => "Suttavandanā",
+    "term" => "suttavandanā",
+    "v_title" => "suttavandanā",
+    "m_title" => "suttavandanā",
+    "p_title" => "suttavandanā",
+    "abbr" => "suttavandanā"
+  ],
+  [
+    "id" => 12,
+    "book" => 9,
+    "name" => "Abhidhānappadīpikāṭīkā",
+    "term" => "abhidhānappadīpikāṭīkā",
+    "v_title" => "abhidhānappadīpikāṭīkā",
+    "m_title" => "abhidhānappadīpikāṭīkā",
+    "p_title" => "abhidhānappadīpikāṭīkā",
+    "abbr" => "abhidhānappadīpikāṭīkā"
+  ],
+  [
+    "id" => 13,
+    "book" => 10,
+    "name" => "Subodhālaṅkāro",
+    "term" => "subodhālaṅkāro",
+    "v_title" => "subodhālaṅkāro",
+    "m_title" => "subodhālaṅkāro",
+    "p_title" => "subodhālaṅkāro",
+    "abbr" => "subodhālaṅkāro"
+  ],
+  [
+    "id" => 14,
+    "book" => 11,
+    "name" => "Subodhālaṅkāraṭīkā",
+    "term" => "subodhālaṅkāraṭīkā",
+    "v_title" => "subodhālaṅkāraṭīkā",
+    "m_title" => "subodhālaṅkāraṭīkā",
+    "p_title" => "subodhālaṅkāraṭīkā",
+    "abbr" => "subodhālaṅkāraṭīkā"
+  ],
+  [
+    "id" => 15,
+    "book" => 12,
+    "name" => "Bālāvatāra",
+    "term" => "bālāvatāra",
+    "v_title" => "bālāvatāra",
+    "m_title" => "bālāvatāra",
+    "p_title" => "bālāvatāra",
+    "abbr" => "bālāvatāra"
+  ],
+  [
+    "id" => 16,
+    "book" => 13,
+    "name" => "Moggallānasuttapāṭho",
+    "term" => "moggallānasuttapāṭho",
+    "v_title" => "moggallānasuttapāṭho",
+    "m_title" => "moggallānasuttapāṭho",
+    "p_title" => "moggallānasuttapāṭho",
+    "abbr" => "moggallānasuttapāṭho"
+  ],
+  [
+    "id" => 17,
+    "book" => 13,
+    "name" => "moggallānabyākaraṇaṃ",
+    "term" => "moggallānabyākaraṇaṃ",
+    "v_title" => "moggallānabyākaraṇaṃ",
+    "m_title" => "moggallānabyākaraṇaṃ",
+    "p_title" => "moggallānabyākaraṇaṃ",
+    "abbr" => "moggallānabyākaraṇaṃ"
+  ],
+  [
+    "id" => 18,
+    "book" => 14,
+    "name" => "Kaccāyanabyākaraṇaṃ",
+    "term" => "kaccāyanabyākaraṇaṃ",
+    "v_title" => "kaccāyanabyākaraṇaṃ",
+    "m_title" => "kaccāyanabyākaraṇaṃ",
+    "p_title" => "kaccāyanabyākaraṇaṃ",
+    "abbr" => "kaccāyanabyākaraṇaṃ"
+  ],
+  [
+    "id" => 19,
+    "book" => 14,
+    "name" => "mahākaccāyanasaddāpāṭha",
+    "term" => "mahākaccāyanasaddāpāṭha",
+    "v_title" => "mahākaccāyanasaddāpāṭha",
+    "m_title" => "mahākaccāyanasaddāpāṭha",
+    "p_title" => "mahākaccāyanasaddāpāṭha",
+    "abbr" => "mahākaccāyanasaddāpāṭha"
+  ],
+  [
+    "id" => 20,
+    "book" => 15,
+    "name" => "Saddanītippakaraṇaṃ (padamālā)",
+    "term" => "saddanītippakaraṇaṃ (padamālā)",
+    "v_title" => "saddanītippakaraṇaṃ (padamālā)",
+    "m_title" => "saddanītippakaraṇaṃ (padamālā)",
+    "p_title" => "saddanītippakaraṇaṃ (padamālā)",
+    "abbr" => "saddanītippakaraṇaṃ (padamālā)"
+  ],
+  [
+    "id" => 21,
+    "book" => 16,
+    "name" => "Saddanītippakaraṇaṃ",
+    "term" => "saddanītippakaraṇaṃ (dhātumālā)",
+    "v_title" => "saddanītippakaraṇaṃ (dhātumālā)",
+    "m_title" => "saddanītippakaraṇaṃ (dhātumālā)",
+    "p_title" => "saddanītippakaraṇaṃ (dhātumālā)",
+    "abbr" => "saddanītippakaraṇaṃ (dhātumālā)"
+  ],
+  [
+    "id" => 22,
+    "book" => 17,
+    "name" => "Padarūpasiddhi",
+    "term" => "padarūpasiddhi",
+    "v_title" => "padarūpasiddhi",
+    "m_title" => "padarūpasiddhi",
+    "p_title" => "padarūpasiddhi",
+    "abbr" => "padarūpasiddhi"
+  ],
+  [
+    "id" => 23,
+    "book" => 18,
+    "name" => "Moggallāna pañcikā ṭīkā",
+    "term" => "moggallāna pañcikā ṭīkā",
+    "v_title" => "moggallāna pañcikā ṭīkā",
+    "m_title" => "moggallāna pañcikā ṭīkā",
+    "p_title" => "moggallāna pañcikā ṭīkā",
+    "abbr" => "moggallāna pañcikā ṭīkā"
+  ],
+  [
+    "id" => 24,
+    "book" => 19,
+    "name" => "Payogasiddhipāḷi",
+    "term" => "payogasiddhipāḷi",
+    "v_title" => "payogasiddhipāḷi",
+    "m_title" => "payogasiddhipāḷi",
+    "p_title" => "payogasiddhipāḷi",
+    "abbr" => "payogasiddhipāḷi"
+  ],
+  [
+    "id" => 25,
+    "book" => 20,
+    "name" => "Vuttodayaṃ",
+    "term" => "vuttodayaṃ",
+    "v_title" => "vuttodayaṃ",
+    "m_title" => "vuttodayaṃ",
+    "p_title" => "vuttodayaṃ",
+    "abbr" => "vuttodayaṃ"
+  ],
+  [
+    "id" => 26,
+    "book" => 21,
+    "name" => "Abhidhānappadīpikā",
+    "term" => "abhidhānappadīpikā",
+    "v_title" => "abhidhānappadīpikā",
+    "m_title" => "abhidhānappadīpikā",
+    "p_title" => "abhidhānappadīpikā",
+    "abbr" => "abhidhānappadīpikā"
+  ],
+  [
+    "id" => 27,
+    "book" => 22,
+    "name" => "Niruttidīpanīpāṭha",
+    "term" => "niruttidīpanīpāṭha",
+    "v_title" => "niruttidīpanīpāṭha",
+    "m_title" => "niruttidīpanīpāṭha",
+    "p_title" => "niruttidīpanīpāṭha",
+    "abbr" => "niruttidīpanīpāṭha"
+  ],
+  [
+    "id" => 28,
+    "book" => 23,
+    "name" => "Paramatthadīpanī",
+    "term" => "paramatthadīpanī",
+    "v_title" => "paramatthadīpanī",
+    "m_title" => "paramatthadīpanī",
+    "p_title" => "paramatthadīpanī",
+    "abbr" => "paramatthadīpanī"
+  ],
+  [
+    "id" => 29,
+    "book" => 24,
+    "name" => "Anudīpanīpāṭha",
+    "term" => "anudīpanīpāṭha",
+    "v_title" => "anudīpanīpāṭha",
+    "m_title" => "anudīpanīpāṭha",
+    "p_title" => "anudīpanīpāṭha",
+    "abbr" => "anudīpanīpāṭha"
+  ],
+  [
+    "id" => 30,
+    "book" => 25,
+    "name" => "Paṭṭhānuddesa dīpanīpāṭha",
+    "term" => "paṭṭhānuddesa dīpanīpāṭha",
+    "v_title" => "paṭṭhānuddesa dīpanīpāṭha",
+    "m_title" => "paṭṭhānuddesa dīpanīpāṭha",
+    "p_title" => "paṭṭhānuddesa dīpanīpāṭha",
+    "abbr" => "paṭṭhānuddesa dīpanīpāṭha"
+  ],
+  [
+    "id" => 31,
+    "book" => 26,
+    "name" => "Caturārakkhadīpanī",
+    "term" => "caturārakkhadīpanī",
+    "v_title" => "caturārakkhadīpanī",
+    "m_title" => "caturārakkhadīpanī",
+    "p_title" => "caturārakkhadīpanī",
+    "abbr" => "caturārakkhadīpanī"
+  ],
+  [
+    "id" => 32,
+    "book" => 27,
+    "name" => "Kavidappaṇanīti",
+    "term" => "kavidappaṇanīti",
+    "v_title" => "kavidappaṇanīti",
+    "m_title" => "kavidappaṇanīti",
+    "p_title" => "kavidappaṇanīti",
+    "abbr" => "kavidappaṇanīti"
+  ],
+  [
+    "id" => 33,
+    "book" => 28,
+    "name" => "Nītimañjarī",
+    "term" => "nītimañjarī",
+    "v_title" => "nītimañjarī",
+    "m_title" => "nītimañjarī",
+    "p_title" => "nītimañjarī",
+    "abbr" => "nītimañjarī"
+  ],
+  [
+    "id" => 34,
+    "book" => 29,
+    "name" => "Dhammanīti",
+    "term" => "dhammanīti",
+    "v_title" => "dhammanīti",
+    "m_title" => "dhammanīti",
+    "p_title" => "dhammanīti",
+    "abbr" => "dhammanīti"
+  ],
+  [
+    "id" => 35,
+    "book" => 30,
+    "name" => "Mahārahanīti",
+    "term" => "mahārahanīti",
+    "v_title" => "mahārahanīti",
+    "m_title" => "mahārahanīti",
+    "p_title" => "mahārahanīti",
+    "abbr" => "mahārahanīti"
+  ],
+  [
+    "id" => 36,
+    "book" => 31,
+    "name" => "Lokanīti",
+    "term" => "lokanīti",
+    "v_title" => "lokanīti",
+    "m_title" => "lokanīti",
+    "p_title" => "lokanīti",
+    "abbr" => "lokanīti"
+  ],
+  [
+    "id" => 37,
+    "book" => 32,
+    "name" => "Suttantanīti",
+    "term" => "suttantanīti",
+    "v_title" => "suttantanīti",
+    "m_title" => "suttantanīti",
+    "p_title" => "suttantanīti",
+    "abbr" => "suttantanīti"
+  ],
+  [
+    "id" => 38,
+    "book" => 32,
+    "name" => "vasalasutta",
+    "term" => "vasalasutta",
+    "v_title" => "vasalasutta",
+    "m_title" => "vasalasutta",
+    "p_title" => "vasalasutta",
+    "abbr" => "vasalasutta"
+  ],
+  [
+    "id" => 39,
+    "book" => 33,
+    "name" => "Sūrassatīnīti",
+    "term" => "sūrassatīnīti",
+    "v_title" => "sūrassatīnīti",
+    "m_title" => "sūrassatīnīti",
+    "p_title" => "sūrassatīnīti",
+    "abbr" => "sūrassatīnīti"
+  ],
+  [
+    "id" => 40,
+    "book" => 34,
+    "name" => "Cāṇakyanītipāḷi",
+    "term" => "cāṇakyanītipāḷi",
+    "v_title" => "cāṇakyanītipāḷi",
+    "m_title" => "cāṇakyanītipāḷi",
+    "p_title" => "cāṇakyanītipāḷi",
+    "abbr" => "cāṇakyanītipāḷi"
+  ],
+  [
+    "id" => 41,
+    "book" => 35,
+    "name" => "Naradakkhadīpanī",
+    "term" => "naradakkhadīpanī",
+    "v_title" => "naradakkhadīpanī",
+    "m_title" => "naradakkhadīpanī",
+    "p_title" => "naradakkhadīpanī",
+    "abbr" => "naradakkhadīpanī"
+  ],
+  [
+    "id" => 42,
+    "book" => 36,
+    "name" => "Rasavāhinī",
+    "term" => "rasavāhinī",
+    "v_title" => "rasavāhinī",
+    "m_title" => "rasavāhinī",
+    "p_title" => "rasavāhinī",
+    "abbr" => "rasavāhinī"
+  ],
+  [
+    "id" => 43,
+    "book" => 37,
+    "name" => "Sīmavisodhanī",
+    "term" => "sīmavisodhanī",
+    "v_title" => "sīmavisodhanī",
+    "m_title" => "sīmavisodhanī",
+    "p_title" => "sīmavisodhanī",
+    "abbr" => "sīmavisodhanī"
+  ],
+  [
+    "id" => 44,
+    "book" => 38,
+    "name" => "Vessantarāgīti",
+    "term" => "vessantarāgīti",
+    "v_title" => "vessantarāgīti",
+    "m_title" => "vessantarāgīti",
+    "p_title" => "vessantarāgīti",
+    "abbr" => "vessantarāgīti"
+  ],
+  [
+    "id" => 45,
+    "book" => 39,
+    "name" => "Dīghanikāye",
+    "term" => "saṅgayana-puccha vissajjanā dīghanikāye",
+    "v_title" => "saṅgayana-puccha vissajjanā dīghanikāye",
+    "m_title" => "saṅgayana-puccha vissajjanā dīghanikāye",
+    "p_title" => "saṅgayana-puccha vissajjanā dīghanikāye",
+    "abbr" => "(saṅgayana-puccha vissajjanā) dīghanikāye"
+  ],
+  [
+    "id" => 46,
+    "book" => 40,
+    "name" => "Majjhimanikāya",
+    "term" => "saṅgayana-puccha vissajjanā majjhimanikāya",
+    "v_title" => "saṅgayana-puccha vissajjanā majjhimanikāya",
+    "m_title" => "saṅgayana-puccha vissajjanā majjhimanikāya",
+    "p_title" => "saṅgayana-puccha vissajjanā majjhimanikāya",
+    "abbr" => "(saṅgayana-puccha vissajjanā) majjhimanikāya"
+  ],
+  [
+    "id" => 47,
+    "book" => 41,
+    "name" => "Saṃyuttanikāye",
+    "term" => "saṅgayana-puccha vissajjanā saṃyuttanikāye",
+    "v_title" => "saṅgayana-puccha vissajjanā saṃyuttanikāye",
+    "m_title" => "saṅgayana-puccha vissajjanā saṃyuttanikāye",
+    "p_title" => "saṅgayana-puccha vissajjanā saṃyuttanikāye",
+    "abbr" => "(saṅgayana-puccha vissajjanā) saṃyuttanikāye"
+  ],
+  [
+    "id" => 48,
+    "book" => 42,
+    "name" => "Aṅguttaranikāye",
+    "term" => "saṅgayana-puccha vissajjanā aṅguttaranikāye",
+    "v_title" => "saṅgayana-puccha vissajjanā aṅguttaranikāye",
+    "m_title" => "saṅgayana-puccha vissajjanā aṅguttaranikāye",
+    "p_title" => "saṅgayana-puccha vissajjanā aṅguttaranikāye",
+    "abbr" => "(saṅgayana-puccha vissajjanā) aṅguttaranikāye"
+  ],
+  [
+    "id" => 49,
+    "book" => 43,
+    "name" => "Vinayapiṭaka",
+    "term" => "saṅgayana-puccha vissajjanā vinayapiṭaka",
+    "v_title" => "saṅgayana-puccha vissajjanā vinayapiṭaka",
+    "m_title" => "saṅgayana-puccha vissajjanā vinayapiṭaka",
+    "p_title" => "saṅgayana-puccha vissajjanā vinayapiṭaka",
+    "abbr" => "(saṅgayana-puccha vissajjanā) vinayapiṭaka"
+  ],
+  [
+    "id" => 50,
+    "book" => 44,
+    "name" => "Abhidhammapiṭaka",
+    "term" => "saṅgayana-puccha vissajjanā abhidhammapiṭaka",
+    "v_title" => "saṅgayana-puccha vissajjanā abhidhammapiṭaka",
+    "m_title" => "saṅgayana-puccha vissajjanā abhidhammapiṭaka",
+    "p_title" => "saṅgayana-puccha vissajjanā abhidhammapiṭaka",
+    "abbr" => "(saṅgayana-puccha vissajjanā) abhidhammapiṭaka"
+  ],
+  [
+    "id" => 51,
+    "book" => 45,
+    "name" => "Aṭṭhakathā",
+    "term" => "saṅgayana-puccha vissajjanā aṭṭhakathā",
+    "v_title" => "saṅgayana-puccha vissajjanā aṭṭhakathā",
+    "m_title" => "saṅgayana-puccha vissajjanā aṭṭhakathā",
+    "p_title" => "saṅgayana-puccha vissajjanā aṭṭhakathā",
+    "abbr" => "(saṅgayana-puccha vissajjanā) aṭṭhakathā"
+  ],
+  [
+    "id" => 52,
+    "book" => 46,
+    "name" => "Milidaṭīkā",
+    "term" => "milidaṭīkā",
+    "v_title" => "milidaṭīkā",
+    "m_title" => "milidaṭīkā",
+    "p_title" => "milidaṭīkā",
+    "abbr" => "milidaṭīkā"
+  ],
+  [
+    "id" => 53,
+    "book" => 47,
+    "name" => "Padamañjarī",
+    "term" => "padamañjarī",
+    "v_title" => "padamañjarī",
+    "m_title" => "padamañjarī",
+    "p_title" => "padamañjarī",
+    "abbr" => "padamañjarī"
+  ],
+  [
+    "id" => 54,
+    "book" => 48,
+    "name" => "Padasādhanaṃ",
+    "term" => "padasādhanaṃ",
+    "v_title" => "padasādhanaṃ",
+    "m_title" => "padasādhanaṃ",
+    "p_title" => "padasādhanaṃ",
+    "abbr" => "padasādhanaṃ"
+  ],
+  [
+    "id" => 55,
+    "book" => 49,
+    "name" => "Saddabindu pakaraṇaṃ",
+    "term" => "saddabindu pakaraṇaṃ",
+    "v_title" => "saddabindu pakaraṇaṃ",
+    "m_title" => "saddabindu pakaraṇaṃ",
+    "p_title" => "saddabindu pakaraṇaṃ",
+    "abbr" => "saddabindu pakaraṇaṃ"
+  ],
+  [
+    "id" => 56,
+    "book" => 50,
+    "name" => "Kaccāyana dhātu mañjūsā",
+    "term" => "kaccāyana  dhātu mañjūsā",
+    "v_title" => "kaccāyana  dhātu mañjūsā",
+    "m_title" => "kaccāyana  dhātu mañjūsā",
+    "p_title" => "kaccāyana  dhātu mañjūsā",
+    "abbr" => "kaccāyana  dhātu mañjūsā"
+  ],
+  [
+    "id" => 57,
+    "book" => 51,
+    "name" => "Samantakūṭavaṇṇanā",
+    "term" => "samantakūṭavaṇṇanā",
+    "v_title" => "samantakūṭavaṇṇanā",
+    "m_title" => "samantakūṭavaṇṇanā",
+    "p_title" => "samantakūṭavaṇṇanā",
+    "abbr" => "samantakūṭavaṇṇanā"
+  ],
+  [
+    "id" => 58,
+    "book" => 52,
+    "name" => "Vuttisametā",
+    "term" => "moggallāna vuttivivaraṇapañcikā.",
+    "v_title" => "moggallāna vuttivivaraṇapañcikā.",
+    "m_title" => "moggallāna vuttivivaraṇapañcikā.",
+    "p_title" => "moggallāna vuttivivaraṇapañcikā.",
+    "abbr" => "moggallāna vuttivivaraṇapañcikā."
+  ],
+  [
+    "id" => 59,
+    "book" => 53,
+    "name" => "Thupavaṃsa",
+    "term" => "thupavaṃsa",
+    "v_title" => "thupavaṃsa",
+    "m_title" => "thupavaṃsa",
+    "p_title" => "thupavaṃsa",
+    "abbr" => "thupavaṃsa"
+  ],
+  [
+    "id" => 60,
+    "book" => 54,
+    "name" => "Dāṭhāvaṃsa",
+    "term" => "dāṭhāvaṃsa",
+    "v_title" => "dāṭhāvaṃsa",
+    "m_title" => "dāṭhāvaṃsa",
+    "p_title" => "dāṭhāvaṃsa",
+    "abbr" => "dāṭhāvaṃsa"
+  ],
+  [
+    "id" => 61,
+    "book" => 55,
+    "name" => "Dhātupāṭha vilāsiniyā",
+    "term" => "dhātupāṭha  vilāsiniyā",
+    "v_title" => "dhātupāṭha  vilāsiniyā",
+    "m_title" => "dhātupāṭha  vilāsiniyā",
+    "p_title" => "dhātupāṭha  vilāsiniyā",
+    "abbr" => "dhātupāṭha  vilāsiniyā"
+  ],
+  [
+    "id" => 62,
+    "book" => 56,
+    "name" => "Dhātuvaṃsa",
+    "term" => "dhātuvaṃsa",
+    "v_title" => "dhātuvaṃsa",
+    "m_title" => "dhātuvaṃsa",
+    "p_title" => "dhātuvaṃsa",
+    "abbr" => "dhātuvaṃsa"
+  ],
+  [
+    "id" => 63,
+    "book" => 57,
+    "name" => "Hatthavanagallavihāra vaṃso",
+    "term" => "hatthavanagallavihāra  vaṃso",
+    "v_title" => "hatthavanagallavihāra  vaṃso",
+    "m_title" => "hatthavanagallavihāra  vaṃso",
+    "p_title" => "hatthavanagallavihāra  vaṃso",
+    "abbr" => "hatthavanagallavihāra  vaṃso"
+  ],
+  [
+    "id" => 64,
+    "book" => 58,
+    "name" => "Jinacaritaya",
+    "term" => "jinacaritaya",
+    "v_title" => "jinacaritaya",
+    "m_title" => "jinacaritaya",
+    "p_title" => "jinacaritaya",
+    "abbr" => "jinacaritaya"
+  ],
+  [
+    "id" => 65,
+    "book" => 59,
+    "name" => "Jinavaṃsadīpaṃ",
+    "term" => "jinavaṃsadīpaṃ",
+    "v_title" => "jinavaṃsadīpaṃ",
+    "m_title" => "jinavaṃsadīpaṃ",
+    "p_title" => "jinavaṃsadīpaṃ",
+    "abbr" => "jinavaṃsadīpaṃ"
+  ],
+  [
+    "id" => 66,
+    "book" => 60,
+    "name" => "Telakaṭāhagāthā",
+    "term" => "telakaṭāhagāthā",
+    "v_title" => "telakaṭāhagāthā",
+    "m_title" => "telakaṭāhagāthā",
+    "p_title" => "telakaṭāhagāthā",
+    "abbr" => "telakaṭāhagāthā"
+  ],
+  [
+    "id" => 67,
+    "book" => 61,
+    "name" => "Cūḷaganthavaṃsapāḷi",
+    "term" => "cūḷaganthavaṃsapāḷi",
+    "v_title" => "cūḷaganthavaṃsapāḷi",
+    "m_title" => "cūḷaganthavaṃsapāḷi",
+    "p_title" => "cūḷaganthavaṃsapāḷi",
+    "abbr" => "cūḷaganthavaṃsapāḷi"
+  ],
+  [
+    "id" => 68,
+    "book" => 62,
+    "name" => "Sāsanavaṃsappadīpikā",
+    "term" => "sāsanavaṃsappadīpikā",
+    "v_title" => "sāsanavaṃsappadīpikā",
+    "m_title" => "sāsanavaṃsappadīpikā",
+    "p_title" => "sāsanavaṃsappadīpikā",
+    "abbr" => "sāsanavaṃsappadīpikā"
+  ],
+  [
+    "id" => 69,
+    "book" => 63,
+    "name" => "Mahāvaṃsapāḷi",
+    "term" => "mahāvaṃsapāḷi",
+    "v_title" => "mahāvaṃsapāḷi",
+    "m_title" => "mahāvaṃsapāḷi",
+    "p_title" => "mahāvaṃsapāḷi",
+    "abbr" => "mahāvaṃsapāḷi"
+  ],
+  [
+    "id" => 70,
+    "book" => 64,
+    "name" => "Visuddhimaggo(Paṭhamo bhāgo)",
+    "term" => "visuddhimagga",
+    "v_title" => "visuddhimagga",
+    "m_title" => "visuddhimagga",
+    "p_title" => "visuddhimagga",
+    "abbr" => "visuddhi."
+  ],
+  [
+    "id" => 71,
+    "book" => 65,
+    "name" => "Visuddhimaggo(Dutiyo bhāgo)",
+    "term" => "visuddhimagga",
+    "v_title" => "visuddhimagga",
+    "m_title" => "visuddhimagga",
+    "p_title" => "visuddhimagga",
+    "abbr" => "visuddhi."
+  ],
+  [
+    "id" => 72,
+    "book" => 66,
+    "name" => "Visuddhimagga-mahāṭīkā(Paṭhamo bhāgo)",
+    "term" => "Visuddhimagga-mahāṭīkā",
+    "v_title" => "visuddhimagga-mahāṭīkā",
+    "m_title" => "visuddhimagga-mahāṭīkā",
+    "p_title" => "Visuddhimagga-mahāṭīkā",
+    "abbr" => "visuddhi. ṭī."
+  ],
+  [
+    "id" => 73,
+    "book" => 67,
+    "name" => "Visuddhimagga-mahāṭīkā(Dutiyo bhāgo)",
+    "term" => "Visuddhimagga-mahāṭīkā",
+    "v_title" => "visuddhimagga-mahāṭīkā",
+    "m_title" => "visuddhimagga-mahāṭīkā",
+    "p_title" => "Visuddhimagga-mahāṭīkā",
+    "abbr" => "visuddhi. ṭī."
+  ],
+  [
+    "id" => 74,
+    "book" => 68,
+    "name" => "Visuddhimagga nidānakathā",
+    "term" => "visuddhimagga-nidānakathā",
+    "v_title" => "visuddhimagga-nidānakathā",
+    "m_title" => "visuddhimagga-nidānakathā",
+    "p_title" => "visuddhimagga-nidānakathā",
+    "abbr" => "visuddhimagga-nidānakathā"
+  ],
+  [
+    "id" => 75,
+    "book" => 69,
+    "name" => "Paṭṭhānapāḷi(Dutiyo bhāgo)",
+    "term" => "paṭṭhānapāḷi",
+    "v_title" => "paṭṭhānapāḷi",
+    "m_title" => "paṭṭhānapāḷi",
+    "p_title" => "paṭṭhānapāḷi",
+    "abbr" => "paṭṭhāna."
+  ],
+  [
+    "id" => 76,
+    "book" => 70,
+    "name" => "Paṭṭhānapāḷi(Tatiyo bhāgo)",
+    "term" => "paṭṭhānapāḷi",
+    "v_title" => "paṭṭhānapāḷi",
+    "m_title" => "paṭṭhānapāḷi",
+    "p_title" => "paṭṭhānapāḷi",
+    "abbr" => "paṭṭhāna."
+  ],
+  [
+    "id" => 77,
+    "book" => 71,
+    "name" => "Paṭṭhānapāḷi(Catuttho bhāgo)",
+    "term" => "paṭṭhānapāḷi",
+    "v_title" => "paṭṭhānapāḷi",
+    "m_title" => "paṭṭhānapāḷi",
+    "p_title" => "paṭṭhānapāḷi",
+    "abbr" => "paṭṭhāna."
+  ],
+  [
+    "id" => 78,
+    "book" => 72,
+    "name" => "Paṭṭhānapāḷi(Pañcamo bhāgo)",
+    "term" => "paṭṭhānapāḷi",
+    "v_title" => "paṭṭhānapāḷi",
+    "m_title" => "paṭṭhānapāḷi",
+    "p_title" => "paṭṭhānapāḷi",
+    "abbr" => "paṭṭhāna."
+  ],
+  [
+    "id" => 79,
+    "book" => 73,
+    "name" => "Dhammasaṅgaṇīpāḷi",
+    "term" => "dhammasaṅgaṇīpāḷi",
+    "v_title" => "dhammasaṅgaṇīpāḷi",
+    "m_title" => "dhammasaṅgaṇīpāḷi",
+    "p_title" => "dhammasaṅgaṇīpāḷi",
+    "abbr" => "abhi. dha."
+  ],
+  [
+    "id" => 80,
+    "book" => 74,
+    "name" => "Vibhaṅgapāḷi",
+    "term" => "vibhaṅgapāḷi",
+    "v_title" => "vibhaṅgapāḷi",
+    "m_title" => "vibhaṅgapāḷi",
+    "p_title" => "vibhaṅgapāḷi",
+    "abbr" => "abhi. vi."
+  ],
+  [
+    "id" => 81,
+    "book" => 75,
+    "name" => "Dhātukathāpāḷi",
+    "term" => "dhātukathāpāḷi",
+    "v_title" => "dhātukathāpāḷi",
+    "m_title" => "dhātukathāpāḷi",
+    "p_title" => "dhātukathāpāḷi",
+    "abbr" => "abhi. dhā."
+  ],
+  [
+    "id" => 82,
+    "book" => 76,
+    "name" => "Puggalapaññattipāḷi",
+    "term" => "puggalapaññattipāḷi",
+    "v_title" => "puggalapaññattipāḷi",
+    "m_title" => "puggalapaññattipāḷi",
+    "p_title" => "puggalapaññattipāḷi",
+    "abbr" => "abhi. pu."
+  ],
+  [
+    "id" => 83,
+    "book" => 77,
+    "name" => "Kathāvatthupāḷi",
+    "term" => "kathāvatthupāḷi",
+    "v_title" => "kathāvatthupāḷi",
+    "m_title" => "kathāvatthupāḷi",
+    "p_title" => "kathāvatthupāḷi",
+    "abbr" => "abhi. ka."
+  ],
+  [
+    "id" => 84,
+    "book" => 78,
+    "name" => "Yamakapāḷi (paṭhamo bhāgo)",
+    "term" => "yamakapāḷi",
+    "v_title" => "yamakapāḷi",
+    "m_title" => "yamakapāḷi",
+    "p_title" => "yamakapāḷi",
+    "abbr" => "yamaka."
+  ],
+  [
+    "id" => 85,
+    "book" => 79,
+    "name" => "Yamakapāḷi (dutiyo bhāgo)",
+    "term" => "yamakapāḷi",
+    "v_title" => "yamakapāḷi",
+    "m_title" => "yamakapāḷi",
+    "p_title" => "yamakapāḷi",
+    "abbr" => "yamaka."
+  ],
+  [
+    "id" => 86,
+    "book" => 80,
+    "name" => "Yamakapāḷi (tatiyo bhāgo)",
+    "term" => "yamakapāḷi",
+    "v_title" => "yamakapāḷi",
+    "m_title" => "yamakapāḷi",
+    "p_title" => "yamakapāḷi",
+    "abbr" => "yamaka."
+  ],
+  [
+    "id" => 87,
+    "book" => 81,
+    "name" => "Paṭṭhānapāḷi(Paṭhamo bhāgo)",
+    "term" => "paṭṭhānapāḷi",
+    "v_title" => "paṭṭhānapāḷi",
+    "m_title" => "paṭṭhānapāḷi",
+    "p_title" => "paṭṭhānapāḷi",
+    "abbr" => "paṭṭhāna."
+  ],
+  [
+    "id" => 88,
+    "book" => 82,
+    "name" => "Dasakanipātapāḷi",
+    "term" => "aṅguttaranikāya",
+    "v_title" => "aṅguttaranikāya-2",
+    "m_title" => "aṅguttaranikāya",
+    "p_title" => "aṅguttaranikāya",
+    "abbr" => "aṃ."
+  ],
+  [
+    "id" => 89,
+    "book" => 83,
+    "name" => "Ekādasakanipātapāḷi",
+    "term" => "aṅguttaranikāya",
+    "v_title" => "aṅguttaranikāya-2",
+    "m_title" => "aṅguttaranikāya",
+    "p_title" => "aṅguttaranikāya",
+    "abbr" => "aṃ."
+  ],
+  [
+    "id" => 90,
+    "book" => 84,
+    "name" => "Ekakanipātapāḷi",
+    "term" => "aṅguttaranikāya",
+    "v_title" => "aṅguttaranikāya-1",
+    "m_title" => "aṅguttaranikāya",
+    "p_title" => "aṅguttaranikāya",
+    "abbr" => "aṃ."
+  ],
+  [
+    "id" => 91,
+    "book" => 85,
+    "name" => "Dukanipātapāḷi",
+    "term" => "aṅguttaranikāya",
+    "v_title" => "aṅguttaranikāya-1",
+    "m_title" => "aṅguttaranikāya",
+    "p_title" => "aṅguttaranikāya",
+    "abbr" => "aṃ."
+  ],
+  [
+    "id" => 92,
+    "book" => 86,
+    "name" => "Tikanipātapāḷi",
+    "term" => "aṅguttaranikāya",
+    "v_title" => "aṅguttaranikāya-1",
+    "m_title" => "aṅguttaranikāya",
+    "p_title" => "aṅguttaranikāya",
+    "abbr" => "aṃ."
+  ],
+  [
+    "id" => 93,
+    "book" => 87,
+    "name" => "Catukkanipātapāḷi",
+    "term" => "aṅguttaranikāya",
+    "v_title" => "aṅguttaranikāya-2",
+    "m_title" => "aṅguttaranikāya",
+    "p_title" => "aṅguttaranikāya",
+    "abbr" => "aṃ."
+  ],
+  [
+    "id" => 94,
+    "book" => 88,
+    "name" => "Pañcakanipātapāḷi",
+    "term" => "aṅguttaranikāya",
+    "v_title" => "aṅguttaranikāya-1",
+    "m_title" => "aṅguttaranikāya",
+    "p_title" => "aṅguttaranikāya",
+    "abbr" => "aṃ."
+  ],
+  [
+    "id" => 95,
+    "book" => 89,
+    "name" => "Chakkanipātapāḷi",
+    "term" => "aṅguttaranikāya",
+    "v_title" => "aṅguttaranikāya-2",
+    "m_title" => "aṅguttaranikāya",
+    "p_title" => "aṅguttaranikāya",
+    "abbr" => "aṃ."
+  ],
+  [
+    "id" => 96,
+    "book" => 90,
+    "name" => "Sattakanipātapāḷi",
+    "term" => "aṅguttaranikāya",
+    "v_title" => "aṅguttaranikāya-2",
+    "m_title" => "aṅguttaranikāya",
+    "p_title" => "aṅguttaranikāya",
+    "abbr" => "aṃ."
+  ],
+  [
+    "id" => 97,
+    "book" => 91,
+    "name" => "Aṭṭhakanipātapāḷi",
+    "term" => "aṅguttaranikāya",
+    "v_title" => "aṅguttaranikāya-1",
+    "m_title" => "aṅguttaranikāya",
+    "p_title" => "aṅguttaranikāya",
+    "abbr" => "aṃ."
+  ],
+  [
+    "id" => 98,
+    "book" => 92,
+    "name" => "Navakanipātapāḷi",
+    "term" => "aṅguttaranikāya",
+    "v_title" => "aṅguttaranikāya-1",
+    "m_title" => "aṅguttaranikāya",
+    "p_title" => "aṅguttaranikāya",
+    "abbr" => "aṃ."
+  ],
+  [
+    "id" => 99,
+    "book" => 93,
+    "name" => "Sīlakkhandhavaggapāḷi",
+    "term" => "dīghanikāya",
+    "v_title" => "dīghanikāya",
+    "m_title" => "dīghanikāya",
+    "p_title" => "dīghanikāya",
+    "abbr" => "dī."
+  ],
+  [
+    "id" => 100,
+    "book" => 94,
+    "name" => "Mahāvaggapāḷi",
+    "term" => "dīghanikāya",
+    "v_title" => "dīghanikāya",
+    "m_title" => "dīghanikāya",
+    "p_title" => "dīghanikāya",
+    "abbr" => "dī."
+  ],
+  [
+    "id" => 101,
+    "book" => 95,
+    "name" => "Pāthikavaggapāḷi",
+    "term" => "dīghanikāya",
+    "v_title" => "dīghanikāya",
+    "m_title" => "dīghanikāya",
+    "p_title" => "dīghanikāya",
+    "abbr" => "dī."
+  ],
+  [
+    "id" => 102,
+    "book" => 96,
+    "name" => "Dhammasaṅgaṇī-aṭṭhakathā",
+    "term" => "dhammasaṅgaṇī-aṭṭhakathā",
+    "v_title" => "dhammasaṅgaṇī-aṭṭhakathā",
+    "m_title" => "dhammasaṅgaṇī-aṭṭhakathā",
+    "p_title" => "dhammasaṅgaṇī-aṭṭhakathā",
+    "abbr" => "abhi. ṭṭha. 1"
+  ],
+  [
+    "id" => 103,
+    "book" => 97,
+    "name" => "Vibhaṅga-aṭṭhakathā",
+    "term" => "vibhaṅga-aṭṭhakathā",
+    "v_title" => "vibhaṅga-aṭṭhakathā",
+    "m_title" => "vibhaṅga-aṭṭhakathā",
+    "p_title" => "vibhaṅga-aṭṭhakathā",
+    "abbr" => "abhi. ṭṭha. 2"
+  ],
+  [
+    "id" => 104,
+    "book" => 98,
+    "name" => "Pañcapakaraṇa-aṭṭhakathā",
+    "term" => "pañcapakaraṇa-aṭṭhakathā",
+    "v_title" => "pañcapakaraṇa-aṭṭhakathā",
+    "m_title" => "pañcapakaraṇa-aṭṭhakathā",
+    "p_title" => "pañcapakaraṇa-aṭṭhakathā",
+    "abbr" => "abhi. ṭṭha. 3"
+  ],
+  [
+    "id" => 105,
+    "book" => 98,
+    "name" => "puggalapaññatti-aṭṭhakathā",
+    "term" => "pañcapakaraṇa-aṭṭhakathā",
+    "v_title" => "pañcapakaraṇa-aṭṭhakathā",
+    "m_title" => "pañcapakaraṇa-aṭṭhakathā",
+    "p_title" => "pañcapakaraṇa-aṭṭhakathā",
+    "abbr" => "abhi. ṭṭha. 3"
+  ],
+  [
+    "id" => 106,
+    "book" => 98,
+    "name" => "kathāvatthu-aṭṭhakathā",
+    "term" => "pañcapakaraṇa-aṭṭhakathā",
+    "v_title" => "pañcapakaraṇa-aṭṭhakathā",
+    "m_title" => "pañcapakaraṇa-aṭṭhakathā",
+    "p_title" => "pañcapakaraṇa-aṭṭhakathā",
+    "abbr" => "abhi. ṭṭha. 3"
+  ],
+  [
+    "id" => 107,
+    "book" => 98,
+    "name" => "yamakappakaraṇa-aṭṭhakathā",
+    "term" => "pañcapakaraṇa-aṭṭhakathā",
+    "v_title" => "pañcapakaraṇa-aṭṭhakathā",
+    "m_title" => "pañcapakaraṇa-aṭṭhakathā",
+    "p_title" => "pañcapakaraṇa-aṭṭhakathā",
+    "abbr" => "abhi. ṭṭha. 3"
+  ],
+  [
+    "id" => 108,
+    "book" => 98,
+    "name" => "paṭṭhānappakaraṇa-aṭṭhakathā",
+    "term" => "pañcapakaraṇa-aṭṭhakathā",
+    "v_title" => "pañcapakaraṇa-aṭṭhakathā",
+    "m_title" => "pañcapakaraṇa-aṭṭhakathā",
+    "p_title" => "pañcapakaraṇa-aṭṭhakathā",
+    "abbr" => "abhi. ṭṭha. 3"
+  ],
+  [
+    "id" => 109,
+    "book" => 99,
+    "name" => "Ekakanipāta-aṭṭhakathā",
+    "term" => "aṅguttaranikāya-aṭṭhakathā",
+    "v_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "m_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "p_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "abbr" => "aṃ. ṭṭha."
+  ],
+  [
+    "id" => 110,
+    "book" => 100,
+    "name" => "Dukanipāta-aṭṭhakathā",
+    "term" => "aṅguttaranikāya-aṭṭhakathā",
+    "v_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "m_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "p_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "abbr" => "aṃ. ṭṭha."
+  ],
+  [
+    "id" => 111,
+    "book" => 100,
+    "name" => "manorathapūraṇī",
+    "term" => "aṅguttaranikāya-aṭṭhakathā",
+    "v_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "m_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "p_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "abbr" => "aṃ. ṭṭha."
+  ],
+  [
+    "id" => 112,
+    "book" => 100,
+    "name" => "manorathapūraṇī",
+    "term" => "aṅguttaranikāya-aṭṭhakathā",
+    "v_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "m_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "p_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "abbr" => "aṃ. ṭṭha."
+  ],
+  [
+    "id" => 113,
+    "book" => 101,
+    "name" => "Pañcakanipāta-aṭṭhakathā",
+    "term" => "aṅguttaranikāya-aṭṭhakathā",
+    "v_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "m_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "p_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "abbr" => "aṃ. ṭṭha."
+  ],
+  [
+    "id" => 114,
+    "book" => 101,
+    "name" => "manorathapūraṇī",
+    "term" => "aṅguttaranikāya-aṭṭhakathā",
+    "v_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "m_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "p_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "abbr" => "aṃ. ṭṭha."
+  ],
+  [
+    "id" => 115,
+    "book" => 101,
+    "name" => "manorathapūraṇī",
+    "term" => "aṅguttaranikāya-aṭṭhakathā",
+    "v_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "m_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "p_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "abbr" => "aṃ. ṭṭha."
+  ],
+  [
+    "id" => 116,
+    "book" => 102,
+    "name" => "Aṭṭhakanipāta-aṭṭhakathā",
+    "term" => "aṅguttaranikāya-aṭṭhakathā",
+    "v_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "m_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "p_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "abbr" => "aṃ. ṭṭha."
+  ],
+  [
+    "id" => 117,
+    "book" => 102,
+    "name" => "manorathapūraṇī",
+    "term" => "aṅguttaranikāya-aṭṭhakathā",
+    "v_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "m_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "p_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "abbr" => "aṃ. ṭṭha."
+  ],
+  [
+    "id" => 118,
+    "book" => 102,
+    "name" => "manorathapūraṇī",
+    "term" => "aṅguttaranikāya-aṭṭhakathā",
+    "v_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "m_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "p_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "abbr" => "aṃ. ṭṭha."
+  ],
+  [
+    "id" => 119,
+    "book" => 102,
+    "name" => "manorathapūraṇī",
+    "term" => "aṅguttaranikāya-aṭṭhakathā",
+    "v_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "m_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "p_title" => "aṅguttaranikāya-aṭṭhakathā",
+    "abbr" => "aṃ. ṭṭha."
+  ],
+  [
+    "id" => 120,
+    "book" => 103,
+    "name" => "Sīlakkhandhavaggaṭṭhakathā",
+    "term" => "dīghanikāya-aṭṭhakathā",
+    "v_title" => "dīghanikāya-aṭṭhakathā",
+    "m_title" => "dīghanikāya-aṭṭhakathā",
+    "p_title" => "dīghanikāya-aṭṭhakathā",
+    "abbr" => "dī. ṭṭha."
+  ],
+  [
+    "id" => 121,
+    "book" => 104,
+    "name" => "Mahāvaggaṭṭhakathā",
+    "term" => "dīghanikāya-aṭṭhakathā",
+    "v_title" => "dīghanikāya-aṭṭhakathā",
+    "m_title" => "dīghanikāya-aṭṭhakathā",
+    "p_title" => "dīghanikāya-aṭṭhakathā",
+    "abbr" => "dī. ṭṭha."
+  ],
+  [
+    "id" => 122,
+    "book" => 105,
+    "name" => "Pāthikavaggaṭṭhakathā",
+    "term" => "dīghanikāya-aṭṭhakathā",
+    "v_title" => "dīghanikāya-aṭṭhakathā",
+    "m_title" => "dīghanikāya-aṭṭhakathā",
+    "p_title" => "dīghanikāya-aṭṭhakathā",
+    "abbr" => "dī. ṭṭha."
+  ],
+  [
+    "id" => 123,
+    "book" => 106,
+    "name" => "Therīgāthā-aṭṭhakathā",
+    "term" => "therīgāthā-aṭṭhakathā",
+    "v_title" => "therīgāthā-aṭṭhakathā",
+    "m_title" => "therīgāthā-aṭṭhakathā",
+    "p_title" => "therīgāthā-aṭṭhakathā",
+    "abbr" => "therī. ṭṭha."
+  ],
+  [
+    "id" => 124,
+    "book" => 107,
+    "name" => "Apadāna-aṭṭhakathā",
+    "term" => "apadāna-aṭṭhakathā",
+    "v_title" => "apadāna-aṭṭhakathā",
+    "m_title" => "apadāna-aṭṭhakathā",
+    "p_title" => "apadāna-aṭṭhakathā",
+    "abbr" => "apa. ṭṭha."
+  ],
+  [
+    "id" => 125,
+    "book" => 108,
+    "name" => "Buddhavaṃsa-aṭṭhakathā",
+    "term" => "buddhavaṃsa-aṭṭhakathā",
+    "v_title" => "buddhavaṃsa-aṭṭhakathā",
+    "m_title" => "buddhavaṃsa-aṭṭhakathā",
+    "p_title" => "buddhavaṃsa-aṭṭhakathā",
+    "abbr" => "buddhavaṃ. ṭṭha."
+  ],
+  [
+    "id" => 126,
+    "book" => 109,
+    "name" => "Cariyāpiṭaka-aṭṭhakathā",
+    "term" => "cariyāpiṭaka-aṭṭhakathā",
+    "v_title" => "cariyāpiṭaka-aṭṭhakathā",
+    "m_title" => "cariyāpiṭaka-aṭṭhakathā",
+    "p_title" => "cariyāpiṭaka-aṭṭhakathā",
+    "abbr" => "cariyā. ṭṭha."
+  ],
+  [
+    "id" => 127,
+    "book" => 110,
+    "name" => "Jātaka-aṭṭhakathā(Paṭhamo bhāgo)",
+    "term" => "jātaka-aṭṭhakathā",
+    "v_title" => "jātaka-aṭṭhakathā",
+    "m_title" => "jātaka-aṭṭhakathā",
+    "p_title" => "jātaka-aṭṭhakathā",
+    "abbr" => "jā. ṭṭha."
+  ],
+  [
+    "id" => 128,
+    "book" => 111,
+    "name" => "Jātaka-aṭṭhakathā(Dutiyo bhāgo)",
+    "term" => "jātaka-aṭṭhakathā",
+    "v_title" => "jātaka-aṭṭhakathā",
+    "m_title" => "jātaka-aṭṭhakathā",
+    "p_title" => "jātaka-aṭṭhakathā",
+    "abbr" => "jā. ṭṭha."
+  ],
+  [
+    "id" => 129,
+    "book" => 112,
+    "name" => "Jātaka-aṭṭhakathā(Tatiyo bhāgo)",
+    "term" => "jātaka-aṭṭhakathā",
+    "v_title" => "jātaka-aṭṭhakathā",
+    "m_title" => "jātaka-aṭṭhakathā",
+    "p_title" => "jātaka-aṭṭhakathā",
+    "abbr" => "jā. ṭṭha."
+  ],
+  [
+    "id" => 130,
+    "book" => 113,
+    "name" => "Jātaka-aṭṭhakathā(Catuttho bhāgo)",
+    "term" => "jātaka-aṭṭhakathā",
+    "v_title" => "jātaka-aṭṭhakathā",
+    "m_title" => "jātaka-aṭṭhakathā",
+    "p_title" => "jātaka-aṭṭhakathā",
+    "abbr" => "jā. ṭṭha."
+  ],
+  [
+    "id" => 131,
+    "book" => 113,
+    "name" => "jātaka-aṭṭhakathā",
+    "term" => "jātaka-aṭṭhakathā",
+    "v_title" => "jātaka-aṭṭhakathā",
+    "m_title" => "jātaka-aṭṭhakathā",
+    "p_title" => "jātaka-aṭṭhakathā",
+    "abbr" => "jā. ṭṭha."
+  ],
+  [
+    "id" => 132,
+    "book" => 114,
+    "name" => "Jātaka-aṭṭhakathā(Pañcamo bhāgo)",
+    "term" => "jātaka-aṭṭhakathā",
+    "v_title" => "jātaka-aṭṭhakathā",
+    "m_title" => "jātaka-aṭṭhakathā",
+    "p_title" => "jātaka-aṭṭhakathā",
+    "abbr" => "jā. ṭṭha."
+  ],
+  [
+    "id" => 133,
+    "book" => 115,
+    "name" => "Jātaka-aṭṭhakathā(Chaṭṭho bhāgo)",
+    "term" => "jātaka-aṭṭhakathā",
+    "v_title" => "jātaka-aṭṭhakathā",
+    "m_title" => "jātaka-aṭṭhakathā",
+    "p_title" => "jātaka-aṭṭhakathā",
+    "abbr" => "jā. ṭṭha."
+  ],
+  [
+    "id" => 134,
+    "book" => 116,
+    "name" => "Khuddakapāṭha-aṭṭhakathā",
+    "term" => "khuddakapāṭha-aṭṭhakathā",
+    "v_title" => "khuddakapāṭha-aṭṭhakathā",
+    "m_title" => "khuddakapāṭha-aṭṭhakathā",
+    "p_title" => "khuddakapāṭha-aṭṭhakathā",
+    "abbr" => "khuddaka. ṭṭha."
+  ],
+  [
+    "id" => 135,
+    "book" => 117,
+    "name" => "Jātaka-aṭṭhakathā(Sattamo bhāgo)",
+    "term" => "jātaka-aṭṭhakathā",
+    "v_title" => "jātaka-aṭṭhakathā",
+    "m_title" => "jātaka-aṭṭhakathā",
+    "p_title" => "jātaka-aṭṭhakathā",
+    "abbr" => "jā. ṭṭha."
+  ],
+  [
+    "id" => 136,
+    "book" => 118,
+    "name" => "Mahāniddesa-aṭṭhakathā",
+    "term" => "mahāniddesa-aṭṭhakathā",
+    "v_title" => "mahāniddesa-aṭṭhakathā",
+    "m_title" => "mahāniddesa-aṭṭhakathā",
+    "p_title" => "mahāniddesa-aṭṭhakathā",
+    "abbr" => "mahāni. ṭṭha."
+  ],
+  [
+    "id" => 137,
+    "book" => 119,
+    "name" => "Cūḷaniddesa-aṭṭhakathā",
+    "term" => "cūḷaniddesa-aṭṭhakathā",
+    "v_title" => "cūḷaniddesa-aṭṭhakathā",
+    "m_title" => "cūḷaniddesa-aṭṭhakathā",
+    "p_title" => "cūḷaniddesa-aṭṭhakathā",
+    "abbr" => "cūḷani. ṭṭha."
+  ],
+  [
+    "id" => 138,
+    "book" => 120,
+    "name" => "Paṭisambhidāmagga-aṭṭhakathā",
+    "term" => "paṭisambhidāmagga-aṭṭhakathā",
+    "v_title" => "paṭisambhidāmagga-aṭṭhakathā",
+    "m_title" => "paṭisambhidāmagga-aṭṭhakathā",
+    "p_title" => "paṭisambhidāmagga-aṭṭhakathā",
+    "abbr" => "paṭisaṃ. ṭṭha."
+  ],
+  [
+    "id" => 139,
+    "book" => 121,
+    "name" => "Nettippakaraṇa-aṭṭhakathā",
+    "term" => "nettippakaraṇa-aṭṭhakathā",
+    "v_title" => "nettippakaraṇa-aṭṭhakathā",
+    "m_title" => "nettippakaraṇa-aṭṭhakathā",
+    "p_title" => "nettippakaraṇa-aṭṭhakathā",
+    "abbr" => "netti. ṭṭha."
+  ],
+  [
+    "id" => 140,
+    "book" => 122,
+    "name" => "Dhammapada-aṭṭhakathā",
+    "term" => "dhammapada-aṭṭhakathā",
+    "v_title" => "dhammapada-aṭṭhakathā",
+    "m_title" => "dhammapada-aṭṭhakathā",
+    "p_title" => "dhammapada-aṭṭhakathā",
+    "abbr" => "dhamma. ṭṭha."
+  ],
+  [
+    "id" => 141,
+    "book" => 123,
+    "name" => "Udāna-aṭṭhakathā",
+    "term" => "udāna-aṭṭhakathā",
+    "v_title" => "udāna-aṭṭhakathā",
+    "m_title" => "udāna-aṭṭhakathā",
+    "p_title" => "udāna-aṭṭhakathā",
+    "abbr" => "udāna. ṭṭha."
+  ],
+  [
+    "id" => 142,
+    "book" => 124,
+    "name" => "Itivuttaka-aṭṭhakathā",
+    "term" => "itivuttaka-aṭṭhakathā",
+    "v_title" => "itivuttaka-aṭṭhakathā",
+    "m_title" => "itivuttaka-aṭṭhakathā",
+    "p_title" => "itivuttaka-aṭṭhakathā",
+    "abbr" => "itivutta. ṭṭha."
+  ],
+  [
+    "id" => 143,
+    "book" => 125,
+    "name" => "Suttanipāta-aṭṭhakathā",
+    "term" => "suttanipāta-aṭṭhakathā",
+    "v_title" => "suttanipāta-aṭṭhakathā",
+    "m_title" => "suttanipāta-aṭṭhakathā",
+    "p_title" => "suttanipāta-aṭṭhakathā",
+    "abbr" => "suttani. ṭṭha."
+  ],
+  [
+    "id" => 144,
+    "book" => 126,
+    "name" => "Vimānavatthu-aṭṭhakathā",
+    "term" => "vimānavatthu-aṭṭhakathā",
+    "v_title" => "vimānavatthu-aṭṭhakathā",
+    "m_title" => "vimānavatthu-aṭṭhakathā",
+    "p_title" => "vimānavatthu-aṭṭhakathā",
+    "abbr" => "vimāna. ṭṭha."
+  ],
+  [
+    "id" => 145,
+    "book" => 127,
+    "name" => "Petavatthu-aṭṭhakathā",
+    "term" => "petavatthu-aṭṭhakathā",
+    "v_title" => "petavatthu-aṭṭhakathā",
+    "m_title" => "petavatthu-aṭṭhakathā",
+    "p_title" => "petavatthu-aṭṭhakathā",
+    "abbr" => "peta. ṭṭha."
+  ],
+  [
+    "id" => 146,
+    "book" => 128,
+    "name" => "Theragāthā-aṭṭhakathā(Paṭhamo bhāgo)",
+    "term" => "Theragāthā-aṭṭhakathā",
+    "v_title" => "Theragāthā-aṭṭhakathā",
+    "m_title" => "theragāthā-aṭṭhakathā",
+    "p_title" => "Theragāthā-aṭṭhakathā",
+    "abbr" => "thera. ṭṭha."
+  ],
+  [
+    "id" => 147,
+    "book" => 129,
+    "name" => "Theragāthā-aṭṭhakathā(Dutiyo bhāgo)",
+    "term" => "Theragāthā-aṭṭhakathā",
+    "v_title" => "Theragāthā-aṭṭhakathā",
+    "m_title" => "theragāthā-aṭṭhakathā",
+    "p_title" => "Theragāthā-aṭṭhakathā",
+    "abbr" => "thera. ṭṭha."
+  ],
+  [
+    "id" => 148,
+    "book" => 130,
+    "name" => "Mūlapaṇṇāsa-aṭṭhakathā",
+    "term" => "majimanikaya-aṭṭhakathā",
+    "v_title" => "majimanikaya-aṭṭhakathā",
+    "m_title" => "majimanikaya-aṭṭhakathā",
+    "p_title" => "majimanikaya-aṭṭhakathā",
+    "abbr" => "ma. ṭṭha."
+  ],
+  [
+    "id" => 149,
+    "book" => 131,
+    "name" => "Majjhimapaṇṇāsa-aṭṭhakathā",
+    "term" => "majimanikaya-aṭṭhakathā",
+    "v_title" => "majimanikaya-aṭṭhakathā",
+    "m_title" => "majimanikaya-aṭṭhakathā",
+    "p_title" => "majimanikaya-aṭṭhakathā",
+    "abbr" => "ma. ṭṭha."
+  ],
+  [
+    "id" => 150,
+    "book" => 132,
+    "name" => "Uparipaṇṇāsa-aṭṭhakathā",
+    "term" => "majimanikaya-aṭṭhakathā",
+    "v_title" => "majimanikaya-aṭṭhakathā",
+    "m_title" => "majimanikaya-aṭṭhakathā",
+    "p_title" => "majimanikaya-aṭṭhakathā",
+    "abbr" => "ma. ṭṭha."
+  ],
+  [
+    "id" => 151,
+    "book" => 133,
+    "name" => "Sagāthāvagga-aṭṭhakathā",
+    "term" => "saṃyuttanikāya-aṭṭhakathā",
+    "v_title" => "saṃyuttanikāya-aṭṭhakathā",
+    "m_title" => "saṃyuttanikāya-aṭṭhakathā",
+    "p_title" => "saṃyuttanikāya-aṭṭhakathā",
+    "abbr" => "saṃ. ṭṭha."
+  ],
+  [
+    "id" => 152,
+    "book" => 134,
+    "name" => "Nidānavagga-aṭṭhakathā",
+    "term" => "saṃyuttanikāya-aṭṭhakathā",
+    "v_title" => "saṃyuttanikāya-aṭṭhakathā",
+    "m_title" => "saṃyuttanikāya-aṭṭhakathā",
+    "p_title" => "saṃyuttanikāya-aṭṭhakathā",
+    "abbr" => "saṃ. ṭṭha."
+  ],
+  [
+    "id" => 153,
+    "book" => 135,
+    "name" => "Khandhavagga-aṭṭhakathā",
+    "term" => "saṃyuttanikāya-aṭṭhakathā",
+    "v_title" => "saṃyuttanikāya-aṭṭhakathā",
+    "m_title" => "saṃyuttanikāya-aṭṭhakathā",
+    "p_title" => "saṃyuttanikāya-aṭṭhakathā",
+    "abbr" => "saṃ. ṭṭha."
+  ],
+  [
+    "id" => 154,
+    "book" => 136,
+    "name" => "Saḷāyatanavagga-aṭṭhakathā",
+    "term" => "saṃyuttanikāya-aṭṭhakathā",
+    "v_title" => "saṃyuttanikāya-aṭṭhakathā",
+    "m_title" => "saṃyuttanikāya-aṭṭhakathā",
+    "p_title" => "saṃyuttanikāya-aṭṭhakathā",
+    "abbr" => "saṃ. ṭṭha."
+  ],
+  [
+    "id" => 155,
+    "book" => 137,
+    "name" => "Mahāvagga-aṭṭhakathā",
+    "term" => "saṃyuttanikāya-aṭṭhakathā",
+    "v_title" => "saṃyuttanikāya-aṭṭhakathā",
+    "m_title" => "saṃyuttanikāya-aṭṭhakathā",
+    "p_title" => "saṃyuttanikāya-aṭṭhakathā",
+    "abbr" => "saṃ. ṭṭha."
+  ],
+  [
+    "id" => 156,
+    "book" => 138,
+    "name" => "Pārājikakaṇḍa-aṭṭhakathā",
+    "term" => "vinaya-aṭṭhakathā",
+    "v_title" => "vinaya-aṭṭhakathā",
+    "m_title" => "pārājikakaṇḍa-aṭṭhakathā",
+    "p_title" => "vinaya-aṭṭhakathā",
+    "abbr" => "vi. ṭṭha."
+  ],
+  [
+    "id" => 157,
+    "book" => 139,
+    "name" => "Pācittiya-aṭṭhakathā",
+    "term" => "vinaya-aṭṭhakathā",
+    "v_title" => "vinaya-aṭṭhakathā",
+    "m_title" => "pācittiyādi-aṭṭhakathā",
+    "p_title" => "vinaya-aṭṭhakathā",
+    "abbr" => "vi. ṭṭha."
+  ],
+  [
+    "id" => 158,
+    "book" => 140,
+    "name" => "Mahāvagga-aṭṭhakathā",
+    "term" => "vinaya-aṭṭhakathā",
+    "v_title" => "vinaya-aṭṭhakathā",
+    "m_title" => "pācittiyādi-aṭṭhakathā",
+    "p_title" => "vinaya-aṭṭhakathā",
+    "abbr" => "vi. ṭṭha."
+  ],
+  [
+    "id" => 159,
+    "book" => 141,
+    "name" => "Cūḷavagga-aṭṭhakathā",
+    "term" => "vinaya-aṭṭhakathā",
+    "v_title" => "vinaya-aṭṭhakathā",
+    "m_title" => "cūḷavaggādi-aṭṭhakathā",
+    "p_title" => "vinaya-aṭṭhakathā",
+    "abbr" => "vi. ṭṭha."
+  ],
+  [
+    "id" => 160,
+    "book" => 142,
+    "name" => "Parivāra-aṭṭhakathā",
+    "term" => "vinaya-aṭṭhakathā",
+    "v_title" => "vinaya-aṭṭhakathā",
+    "m_title" => "cūḷavaggādi-aṭṭhakathā",
+    "p_title" => "vinaya-aṭṭhakathā",
+    "abbr" => "vi. ṭṭha."
+  ],
+  [
+    "id" => 161,
+    "book" => 143,
+    "name" => "Therāpadānapāḷi(Paṭhamo bhāgo)",
+    "term" => "therāpadānapāḷi",
+    "v_title" => "therāpadānapāḷi",
+    "m_title" => "therāpadānapāḷi",
+    "p_title" => "therāpadānapāḷi",
+    "abbr" => "apa."
+  ],
+  [
+    "id" => 162,
+    "book" => 144,
+    "name" => "Therāpadānapāḷi(Dutiyo bhāgo)",
+    "term" => "therāpadānapāḷi",
+    "v_title" => "therāpadānapāḷi",
+    "m_title" => "therāpadānapāḷi",
+    "p_title" => "therāpadānapāḷi",
+    "abbr" => "apa."
+  ],
+  [
+    "id" => 163,
+    "book" => 144,
+    "name" => "therīapadānapāḷi",
+    "term" => "therīapadānapāḷi",
+    "v_title" => "therīapadānapāḷi",
+    "m_title" => "therīapadānapāḷi",
+    "p_title" => "therīapadānapāḷi",
+    "abbr" => "therīapadānapāḷi"
+  ],
+  [
+    "id" => 164,
+    "book" => 145,
+    "name" => "Buddhavaṃsapāḷi",
+    "term" => "buddhavaṃsapāḷi",
+    "v_title" => "buddhavaṃsapāḷi",
+    "m_title" => "buddhavaṃsapāḷi",
+    "p_title" => "buddhavaṃsapāḷi",
+    "abbr" => "buddhavaṃ."
+  ],
+  [
+    "id" => 165,
+    "book" => 146,
+    "name" => "Cariyāpiṭakapāḷi",
+    "term" => "cariyāpiṭakapāḷi",
+    "v_title" => "cariyāpiṭakapāḷi",
+    "m_title" => "cariyāpiṭakapāḷi",
+    "p_title" => "cariyāpiṭakapāḷi",
+    "abbr" => "cariyā."
+  ],
+  [
+    "id" => 166,
+    "book" => 147,
+    "name" => "Jātakapāḷi(Dutiyo bhāgo)",
+    "term" => "jātakapāḷi",
+    "v_title" => "jātakapāḷi",
+    "m_title" => "jātakapāḷi",
+    "p_title" => "jātakapāḷi",
+    "abbr" => "jā."
+  ],
+  [
+    "id" => 167,
+    "book" => 148,
+    "name" => "Jātakapāḷi(Paṭhamo bhāgo)",
+    "term" => "jātakapāḷi",
+    "v_title" => "jātakapāḷi",
+    "m_title" => "jātakapāḷi",
+    "p_title" => "jātakapāḷi",
+    "abbr" => "jā."
+  ],
+  [
+    "id" => 168,
+    "book" => 149,
+    "name" => "Mahāniddesapāḷi",
+    "term" => "mahāniddesapāḷi",
+    "v_title" => "mahāniddesapāḷi",
+    "m_title" => "mahāniddesapāḷi",
+    "p_title" => "mahāniddesapāḷi",
+    "abbr" => "mahāni."
+  ],
+  [
+    "id" => 169,
+    "book" => 150,
+    "name" => "Cūḷaniddesapāḷi",
+    "term" => "cūḷaniddesapāḷi",
+    "v_title" => "cūḷaniddesapāḷi",
+    "m_title" => "cūḷaniddesapāḷi",
+    "p_title" => "cūḷaniddesapāḷi",
+    "abbr" => "cūḷani."
+  ],
+  [
+    "id" => 170,
+    "book" => 151,
+    "name" => "Paṭisambhidāmaggapāḷi",
+    "term" => "paṭisambhidāmaggapāḷi",
+    "v_title" => "paṭisambhidāmaggapāḷi",
+    "m_title" => "paṭisambhidāmaggapāḷi",
+    "p_title" => "paṭisambhidāmaggapāḷi",
+    "abbr" => "paṭisaṃ."
+  ],
+  [
+    "id" => 171,
+    "book" => 152,
+    "name" => "Milindapañhapāḷi",
+    "term" => "milindapañhapāḷi",
+    "v_title" => "milindapañhapāḷi",
+    "m_title" => "milindapañhapāḷi",
+    "p_title" => "milindapañhapāḷi",
+    "abbr" => "milinda."
+  ],
+  [
+    "id" => 172,
+    "book" => 153,
+    "name" => "Nettippakaraṇapāḷi",
+    "term" => "nettippakaraṇapāḷi",
+    "v_title" => "nettippakaraṇapāḷi",
+    "m_title" => "nettippakaraṇapāḷi",
+    "p_title" => "nettippakaraṇapāḷi",
+    "abbr" => "netti."
+  ],
+  [
+    "id" => 173,
+    "book" => 154,
+    "name" => "Khuddakapāṭhapāḷi",
+    "term" => "khuddakapāṭhapāḷi",
+    "v_title" => "khuddakapāṭhapāḷi",
+    "m_title" => "khuddakapāṭhapāḷi",
+    "p_title" => "khuddakapāṭhapāḷi",
+    "abbr" => "khuddaka."
+  ],
+  [
+    "id" => 174,
+    "book" => 155,
+    "name" => "Peṭakopadesapāḷi",
+    "term" => "peṭakopadesapāḷi",
+    "v_title" => "peṭakopadesapāḷi",
+    "m_title" => "peṭakopadesapāḷi",
+    "p_title" => "peṭakopadesapāḷi",
+    "abbr" => "peṭako."
+  ],
+  [
+    "id" => 175,
+    "book" => 156,
+    "name" => "Dhammapadapāḷi",
+    "term" => "dhammapadapāḷi",
+    "v_title" => "dhammapadapāḷi",
+    "m_title" => "dhammapadapāḷi",
+    "p_title" => "dhammapadapāḷi",
+    "abbr" => "dhamma."
+  ],
+  [
+    "id" => 176,
+    "book" => 157,
+    "name" => "Udānapāḷi",
+    "term" => "udānapāḷi",
+    "v_title" => "udānapāḷi",
+    "m_title" => "udānapāḷi",
+    "p_title" => "udānapāḷi",
+    "abbr" => "udāna."
+  ],
+  [
+    "id" => 177,
+    "book" => 158,
+    "name" => "Itivuttakapāḷi",
+    "term" => "itivuttakapāḷi",
+    "v_title" => "itivuttakapāḷi",
+    "m_title" => "itivuttakapāḷi",
+    "p_title" => "itivuttakapāḷi",
+    "abbr" => "itivutta."
+  ],
+  [
+    "id" => 178,
+    "book" => 159,
+    "name" => "Suttanipātapāḷi",
+    "term" => "suttanipātapāḷi",
+    "v_title" => "suttanipātapāḷi",
+    "m_title" => "suttanipātapāḷi",
+    "p_title" => "suttanipātapāḷi",
+    "abbr" => "suttani."
+  ],
+  [
+    "id" => 179,
+    "book" => 160,
+    "name" => "Vimānavatthupāḷi",
+    "term" => "vimānavatthupāḷi",
+    "v_title" => "vimānavatthupāḷi",
+    "m_title" => "vimānavatthupāḷi",
+    "p_title" => "vimānavatthupāḷi",
+    "abbr" => "vimāna."
+  ],
+  [
+    "id" => 180,
+    "book" => 161,
+    "name" => "Petavatthupāḷi",
+    "term" => "petavatthupāḷi",
+    "v_title" => "petavatthupāḷi",
+    "m_title" => "petavatthupāḷi",
+    "p_title" => "petavatthupāḷi",
+    "abbr" => "peta."
+  ],
+  [
+    "id" => 181,
+    "book" => 162,
+    "name" => "Theragāthāpāḷi",
+    "term" => "theragāthāpāḷi",
+    "v_title" => "theragāthāpāḷi",
+    "m_title" => "theragāthāpāḷi",
+    "p_title" => "theragāthāpāḷi",
+    "abbr" => "theragāthā."
+  ],
+  [
+    "id" => 182,
+    "book" => 163,
+    "name" => "Therīgāthāpāḷi",
+    "term" => "therīgāthāpāḷi",
+    "v_title" => "therīgāthāpāḷi",
+    "m_title" => "therīgāthāpāḷi",
+    "p_title" => "therīgāthāpāḷi",
+    "abbr" => "therī."
+  ],
+  [
+    "id" => 183,
+    "book" => 164,
+    "name" => "Mūlapaṇṇāsapāḷi",
+    "term" => "majjhimanikaya",
+    "v_title" => "majjhimanikaya",
+    "m_title" => "majjhimanikaya",
+    "p_title" => "majjhimanikaya",
+    "abbr" => "ma."
+  ],
+  [
+    "id" => 184,
+    "book" => 165,
+    "name" => "Majjhimapaṇṇāsapāḷi",
+    "term" => "majjhimanikaya",
+    "v_title" => "majjhimanikaya",
+    "m_title" => "majjhimanikaya",
+    "p_title" => "majjhimanikaya",
+    "abbr" => "ma."
+  ],
+  [
+    "id" => 185,
+    "book" => 166,
+    "name" => "Uparipaṇṇāsapāḷi",
+    "term" => "majjhimanikaya",
+    "v_title" => "majjhimanikaya",
+    "m_title" => "majjhimanikaya",
+    "p_title" => "majjhimanikaya",
+    "abbr" => "ma."
+  ],
+  [
+    "id" => 186,
+    "book" => 167,
+    "name" => "Sagāthāvaggo",
+    "term" => "saṃyuttanikāya",
+    "v_title" => "saṃyuttanikāya",
+    "m_title" => "saṃyuttanikāya",
+    "p_title" => "saṃyuttanikāya",
+    "abbr" => "saṃ."
+  ],
+  [
+    "id" => 187,
+    "book" => 168,
+    "name" => "Nidānavaggo",
+    "term" => "saṃyuttanikāya",
+    "v_title" => "saṃyuttanikāya",
+    "m_title" => "saṃyuttanikāya",
+    "p_title" => "saṃyuttanikāya",
+    "abbr" => "saṃ."
+  ],
+  [
+    "id" => 188,
+    "book" => 169,
+    "name" => "Khandhavaggo",
+    "term" => "saṃyuttanikāya",
+    "v_title" => "saṃyuttanikāya",
+    "m_title" => "saṃyuttanikāya",
+    "p_title" => "saṃyuttanikāya",
+    "abbr" => "saṃ."
+  ],
+  [
+    "id" => 189,
+    "book" => 170,
+    "name" => "Saḷāyatanavaggo",
+    "term" => "saṃyuttanikāya",
+    "v_title" => "saṃyuttanikāya",
+    "m_title" => "saṃyuttanikāya",
+    "p_title" => "saṃyuttanikāya",
+    "abbr" => "saṃ."
+  ],
+  [
+    "id" => 190,
+    "book" => 171,
+    "name" => "Mahāvaggo",
+    "term" => "saṃyuttanikāya",
+    "v_title" => "saṃyuttanikāya",
+    "m_title" => "saṃyuttanikāya",
+    "p_title" => "saṃyuttanikāya",
+    "abbr" => "saṃ."
+  ],
+  [
+    "id" => 191,
+    "book" => 172,
+    "name" => "dhammasaṅgaṇī-mūlaṭīkā",
+    "term" => "dhammasaṅgaṇī-mūlaṭīkā",
+    "v_title" => "dhammasaṅgaṇī-mūlaṭīkā",
+    "m_title" => "dhammasaṅgaṇī-mūlaṭīkā",
+    "p_title" => "dhammasaṅgaṇī-mūlaṭīkā",
+    "abbr" => "mūlaṭī. 1"
+  ],
+  [
+    "id" => 192,
+    "book" => 173,
+    "name" => "vibhaṅga-mūlaṭīkā",
+    "term" => "vibhaṅga-mūlaṭīkā",
+    "v_title" => "vibhaṅga-mūlaṭīkā",
+    "m_title" => "vibhaṅga-mūlaṭīkā",
+    "p_title" => "vibhaṅga-mūlaṭīkā",
+    "abbr" => "mūlaṭī. 2"
+  ],
+  [
+    "id" => 193,
+    "book" => 173,
+    "name" => "Vibhaṅga-anuṭīkā",
+    "term" => "vibhaṅga-anuṭīkā",
+    "v_title" => "vibhaṅga-anuṭīkā",
+    "m_title" => "vibhaṅga-anuṭīkā",
+    "p_title" => "vibhaṅga-anuṭīkā",
+    "abbr" => "anuṭī. 2"
+  ],
+  [
+    "id" => 194,
+    "book" => 174,
+    "name" => "pañcapakaraṇa-mūlaṭīkā",
+    "term" => "pañcapakaraṇa-mūlaṭīkā",
+    "v_title" => "pañcapakaraṇa-mūlaṭīkā",
+    "m_title" => "pañcapakaraṇa-mūlaṭīkā",
+    "p_title" => "pañcapakaraṇa-mūlaṭīkā",
+    "abbr" => "mūlaṭī. 3"
+  ],
+  [
+    "id" => 195,
+    "book" => 174,
+    "name" => "mūlaṭīkā",
+    "term" => "pañcapakaraṇa-mūlaṭīkā",
+    "v_title" => "pañcapakaraṇa-mūlaṭīkā",
+    "m_title" => "pañcapakaraṇa-mūlaṭīkā",
+    "p_title" => "pañcapakaraṇa-mūlaṭīkā",
+    "abbr" => "mūlaṭī. 3"
+  ],
+  [
+    "id" => 196,
+    "book" => 174,
+    "name" => "mūlaṭīkā",
+    "term" => "pañcapakaraṇa-mūlaṭīkā",
+    "v_title" => "pañcapakaraṇa-mūlaṭīkā",
+    "m_title" => "pañcapakaraṇa-mūlaṭīkā",
+    "p_title" => "pañcapakaraṇa-mūlaṭīkā",
+    "abbr" => "mūlaṭī. 3"
+  ],
+  [
+    "id" => 197,
+    "book" => 174,
+    "name" => "mūlaṭīkā",
+    "term" => "pañcapakaraṇa-mūlaṭīkā",
+    "v_title" => "pañcapakaraṇa-mūlaṭīkā",
+    "m_title" => "pañcapakaraṇa-mūlaṭīkā",
+    "p_title" => "pañcapakaraṇa-mūlaṭīkā",
+    "abbr" => "mūlaṭī. 3"
+  ],
+  [
+    "id" => 198,
+    "book" => 174,
+    "name" => "mūlaṭīkā",
+    "term" => "pañcapakaraṇa-mūlaṭīkā",
+    "v_title" => "pañcapakaraṇa-mūlaṭīkā",
+    "m_title" => "pañcapakaraṇa-mūlaṭīkā",
+    "p_title" => "pañcapakaraṇa-mūlaṭīkā",
+    "abbr" => "mūlaṭī. 3"
+  ],
+  [
+    "id" => 199,
+    "book" => 175,
+    "name" => "dhammasaṅgaṇī-anuṭīkā",
+    "term" => "dhammasaṅgaṇī-anuṭīkā",
+    "v_title" => "dhammasaṅgaṇī-anuṭīkā",
+    "m_title" => "dhammasaṅgaṇī-anuṭīkā",
+    "p_title" => "dhammasaṅgaṇī-anuṭīkā",
+    "abbr" => "anuṭī. 1"
+  ],
+  [
+    "id" => 200,
+    "book" => 176,
+    "name" => "dhātukathāpakaraṇa-anuṭīkā",
+    "term" => "pañcapakaraṇa-anuṭīkā",
+    "v_title" => "pañcapakaraṇa-anuṭīkā",
+    "m_title" => "pañcapakaraṇa-anuṭīkā",
+    "p_title" => "pañcapakaraṇa-anuṭīkā",
+    "abbr" => "anuṭī. 3"
+  ],
+  [
+    "id" => 201,
+    "book" => 176,
+    "name" => "puggalapaññattipakaraṇa-anuṭīkā",
+    "term" => "pañcapakaraṇa-anuṭīkā",
+    "v_title" => "pañcapakaraṇa-anuṭīkā",
+    "m_title" => "pañcapakaraṇa-anuṭīkā",
+    "p_title" => "pañcapakaraṇa-anuṭīkā",
+    "abbr" => "anuṭī. 3"
+  ],
+  [
+    "id" => 202,
+    "book" => 176,
+    "name" => "kathāvatthupakaraṇa-anuṭīkā",
+    "term" => "pañcapakaraṇa-anuṭīkā",
+    "v_title" => "pañcapakaraṇa-anuṭīkā",
+    "m_title" => "pañcapakaraṇa-anuṭīkā",
+    "p_title" => "pañcapakaraṇa-anuṭīkā",
+    "abbr" => "anuṭī. 3"
+  ],
+  [
+    "id" => 203,
+    "book" => 176,
+    "name" => "anuṭīkā",
+    "term" => "pañcapakaraṇa-anuṭīkā",
+    "v_title" => "pañcapakaraṇa-anuṭīkā",
+    "m_title" => "pañcapakaraṇa-anuṭīkā",
+    "p_title" => "pañcapakaraṇa-anuṭīkā",
+    "abbr" => "anuṭī. 3"
+  ],
+  [
+    "id" => 204,
+    "book" => 176,
+    "name" => "anuṭīkā",
+    "term" => "pañcapakaraṇa-anuṭīkā",
+    "v_title" => "pañcapakaraṇa-anuṭīkā",
+    "m_title" => "pañcapakaraṇa-anuṭīkā",
+    "p_title" => "pañcapakaraṇa-anuṭīkā",
+    "abbr" => "anuṭī. 3"
+  ],
+  [
+    "id" => 205,
+    "book" => 177,
+    "name" => "Ganthārambhakathā",
+    "term" => "abhidhammāvatāro",
+    "v_title" => "abhidhammāvatāro",
+    "m_title" => "abhidhammāvatāro",
+    "p_title" => "abhidhammāvatāro",
+    "abbr" => "abhidhammāvatāro"
+  ],
+  [
+    "id" => 206,
+    "book" => 177,
+    "name" => "nāmarūpaparicchedo",
+    "term" => "nāmarūpaparicchedo",
+    "v_title" => "nāmarūpaparicchedo",
+    "m_title" => "nāmarūpaparicchedo",
+    "p_title" => "nāmarūpaparicchedo",
+    "abbr" => "nāmarūpaparicchedo"
+  ],
+  [
+    "id" => 207,
+    "book" => 177,
+    "name" => "paramatthavinicchayo",
+    "term" => "paramatthavinicchayo",
+    "v_title" => "paramatthavinicchayo",
+    "m_title" => "paramatthavinicchayo",
+    "p_title" => "paramatthavinicchayo",
+    "abbr" => "paramatthavinicchayo"
+  ],
+  [
+    "id" => 208,
+    "book" => 177,
+    "name" => "saccasaṅkhepo",
+    "term" => "saccasaṅkhepo",
+    "v_title" => "saccasaṅkhepo",
+    "m_title" => "saccasaṅkhepo",
+    "p_title" => "saccasaṅkhepo",
+    "abbr" => "saccasaṅkhepo"
+  ],
+  [
+    "id" => 209,
+    "book" => 178,
+    "name" => "Abhidhammatthasaṅgaho",
+    "term" => "abhidhammatthasaṅgaho",
+    "v_title" => "abhidhammatthasaṅgaho",
+    "m_title" => "abhidhammatthasaṅgaho",
+    "p_title" => "abhidhammatthasaṅgaho",
+    "abbr" => "abhidhammatthasaṅgaho"
+  ],
+  [
+    "id" => 210,
+    "book" => 178,
+    "name" => "abhidhammatthavibhāvinīṭīkā",
+    "term" => "abhidhammatthavibhāvinīṭīkā",
+    "v_title" => "abhidhammatthavibhāvinīṭīkā",
+    "m_title" => "abhidhammatthavibhāvinīṭīkā",
+    "p_title" => "abhidhammatthavibhāvinīṭīkā",
+    "abbr" => "abhidhammatthavibhāvinīṭīkā"
+  ],
+  [
+    "id" => 211,
+    "book" => 179,
+    "name" => "Paṭhamo paricchedo",
+    "term" => "abhidhammāvatāra-purāṇaṭīkā",
+    "v_title" => "abhidhammāvatāra-purāṇaṭīkā",
+    "m_title" => "abhidhammāvatāra-purāṇaṭīkā",
+    "p_title" => "abhidhammāvatāra-purāṇaṭīkā",
+    "abbr" => "abhidhammāvatāra-purāṇaṭīkā"
+  ],
+  [
+    "id" => 212,
+    "book" => 179,
+    "name" => "abhidhammāvatāra-abhinavaṭīkā",
+    "term" => "abhidhammāvatāra-abhinavaṭīkā",
+    "v_title" => "abhidhammāvatāra-abhinavaṭīkā",
+    "m_title" => "abhidhammāvatāra-abhinavaṭīkā",
+    "p_title" => "abhidhammāvatāra-abhinavaṭīkā",
+    "abbr" => "abhidhammāvatāra-abhinavaṭīkā"
+  ],
+  [
+    "id" => 213,
+    "book" => 180,
+    "name" => "Abhidhammamātikāpāḷi",
+    "term" => "abhidhammamātikāpāḷi",
+    "v_title" => "abhidhammamātikāpāḷi",
+    "m_title" => "abhidhammamātikāpāḷi",
+    "p_title" => "abhidhammamātikāpāḷi",
+    "abbr" => "abhidhammamātikāpāḷi"
+  ],
+  [
+    "id" => 214,
+    "book" => 180,
+    "name" => "mohavicchedanī",
+    "term" => "mohavicchedanī",
+    "v_title" => "mohavicchedanī",
+    "m_title" => "mohavicchedanī",
+    "p_title" => "mohavicchedanī",
+    "abbr" => "mohavicchedanī"
+  ],
+  [
+    "id" => 215,
+    "book" => 181,
+    "name" => "Ekakanipāta-ṭīkā",
+    "term" => "aṅguttaranikāya-ṭīkā",
+    "v_title" => "aṅguttaranikāya-ṭīkā",
+    "m_title" => "aṅguttaranikāya-ṭīkā",
+    "p_title" => "aṅguttaranikāya-ṭīkā",
+    "abbr" => "aṃ. ṭī."
+  ],
+  [
+    "id" => 216,
+    "book" => 182,
+    "name" => "Dukanipāta-ṭīkā",
+    "term" => "aṅguttaranikāya-ṭīkā",
+    "v_title" => "aṅguttaranikāya-ṭīkā",
+    "m_title" => "aṅguttaranikāya-ṭīkā",
+    "p_title" => "aṅguttaranikāya-ṭīkā",
+    "abbr" => "aṃ. ṭī."
+  ],
+  [
+    "id" => 217,
+    "book" => 182,
+    "name" => "tikanipāta-ṭīkā",
+    "term" => "tikanipāta-ṭīkā",
+    "v_title" => "tikanipāta-ṭīkā",
+    "m_title" => "aṅguttaranikāya-ṭīkā",
+    "p_title" => "tikanipāta-ṭīkā",
+    "abbr" => "aṃ. ṭī."
+  ],
+  [
+    "id" => 218,
+    "book" => 182,
+    "name" => "catukkanipāta-ṭīkā",
+    "term" => "catukkanipāta-ṭīkā",
+    "v_title" => "catukkanipāta-ṭīkā",
+    "m_title" => "aṅguttaranikāya-ṭīkā",
+    "p_title" => "catukkanipāta-ṭīkā",
+    "abbr" => "aṃ. ṭī."
+  ],
+  [
+    "id" => 219,
+    "book" => 183,
+    "name" => "Pañcakanipāta-ṭīkā",
+    "term" => "aṅguttaranikāya-ṭīkā",
+    "v_title" => "aṅguttaranikāya-ṭīkā",
+    "m_title" => "aṅguttaranikāya-ṭīkā",
+    "p_title" => "aṅguttaranikāya-ṭīkā",
+    "abbr" => "aṃ. ṭī."
+  ],
+  [
+    "id" => 220,
+    "book" => 183,
+    "name" => "chakkanipāta-ṭīkā",
+    "term" => "chakkanipāta-ṭīkā",
+    "v_title" => "chakkanipāta-ṭīkā",
+    "m_title" => "aṅguttaranikāya-ṭīkā",
+    "p_title" => "chakkanipāta-ṭīkā",
+    "abbr" => "aṃ. ṭī."
+  ],
+  [
+    "id" => 221,
+    "book" => 183,
+    "name" => "sattakanipāta-ṭīkā",
+    "term" => "sattakanipāta-ṭīkā",
+    "v_title" => "sattakanipāta-ṭīkā",
+    "m_title" => "aṅguttaranikāya-ṭīkā",
+    "p_title" => "sattakanipāta-ṭīkā",
+    "abbr" => "aṃ. ṭī."
+  ],
+  [
+    "id" => 222,
+    "book" => 184,
+    "name" => "Aṭṭhakanipāta-ṭīkā",
+    "term" => "aṅguttaranikāya-ṭīkā",
+    "v_title" => "aṅguttaranikāya-ṭīkā",
+    "m_title" => "aṅguttaranikāya-ṭīkā",
+    "p_title" => "aṅguttaranikāya-ṭīkā",
+    "abbr" => "aṃ. ṭī."
+  ],
+  [
+    "id" => 223,
+    "book" => 184,
+    "name" => "navakanipāta-ṭīkā",
+    "term" => "navakanipāta-ṭīkā",
+    "v_title" => "navakanipāta-ṭīkā",
+    "m_title" => "aṅguttaranikāya-ṭīkā",
+    "p_title" => "navakanipāta-ṭīkā",
+    "abbr" => "aṃ. ṭī."
+  ],
+  [
+    "id" => 224,
+    "book" => 184,
+    "name" => "dasakanipāta-ṭīkā",
+    "term" => "dasakanipāta-ṭīkā",
+    "v_title" => "dasakanipāta-ṭīkā",
+    "m_title" => "aṅguttaranikāya-ṭīkā",
+    "p_title" => "dasakanipāta-ṭīkā",
+    "abbr" => "aṃ. ṭī."
+  ],
+  [
+    "id" => 225,
+    "book" => 184,
+    "name" => "ekādasakanipāta-ṭīkā",
+    "term" => "ekādasakanipāta-ṭīkā",
+    "v_title" => "ekādasakanipāta-ṭīkā",
+    "m_title" => "aṅguttaranikāya-ṭīkā",
+    "p_title" => "ekādasakanipāta-ṭīkā",
+    "abbr" => "aṃ. ṭī."
+  ],
+  [
+    "id" => 226,
+    "book" => 185,
+    "name" => "Sīlakkhandhavaggaṭīkā",
+    "term" => "dīghanikāya-ṭīkā",
+    "v_title" => "dīghanikāya-ṭīkā",
+    "m_title" => "dīghanikāya-ṭīkā",
+    "p_title" => "dīghanikāya-ṭīkā",
+    "abbr" => "dī. ṭī."
+  ],
+  [
+    "id" => 227,
+    "book" => 186,
+    "name" => "Mahāvaggaṭīkā",
+    "term" => "dīghanikāya-ṭīkā",
+    "v_title" => "dīghanikāya-ṭīkā",
+    "m_title" => "dīghanikāya-ṭīkā",
+    "p_title" => "dīghanikāya-ṭīkā",
+    "abbr" => "dī. ṭī."
+  ],
+  [
+    "id" => 228,
+    "book" => 187,
+    "name" => "Pāthikavaggaṭīkā",
+    "term" => "dīghanikāya-ṭīkā",
+    "v_title" => "dīghanikāya-ṭīkā",
+    "m_title" => "dīghanikāya-ṭīkā",
+    "p_title" => "dīghanikāya-ṭīkā",
+    "abbr" => "dī. ṭī."
+  ],
+  [
+    "id" => 229,
+    "book" => 188,
+    "name" => "Sīlakkhandhavaggaabhinavaṭīkā",
+    "term" => "dīghanikāya-abhinavaṭīkā",
+    "v_title" => "dīghanikāya-abhinavaṭīkā",
+    "m_title" => "dīghanikāya-abhinavaṭīkā",
+    "p_title" => "dīghanikāya-abhinavaṭīkā",
+    "abbr" => "dī. abhi. ṭī."
+  ],
+  [
+    "id" => 230,
+    "book" => 189,
+    "name" => "Sīlakkhandhavaggaabhinavaṭīkā",
+    "term" => "dīghanikāya-abhinavaṭīkā",
+    "v_title" => "dīghanikāya-abhinavaṭīkā",
+    "m_title" => "dīghanikāya-abhinavaṭīkā",
+    "p_title" => "dīghanikāya-abhinavaṭīkā",
+    "abbr" => "dī. abhi. ṭī."
+  ],
+  [
+    "id" => 231,
+    "book" => 190,
+    "name" => "Nettippakaraṇa-ṭīkā",
+    "term" => "nettippakaraṇa-ṭīkā",
+    "v_title" => "nettippakaraṇa-ṭīkā",
+    "m_title" => "nettippakaraṇa-ṭīkā",
+    "p_title" => "nettippakaraṇa-ṭīkā",
+    "abbr" => "netti. ṭī."
+  ],
+  [
+    "id" => 232,
+    "book" => 191,
+    "name" => "Nettivibhāvinī",
+    "term" => "nettivibhāvinī",
+    "v_title" => "nettivibhāvinī",
+    "m_title" => "nettivibhāvinī",
+    "p_title" => "nettivibhāvinī",
+    "abbr" => "netti. vibhā."
+  ],
+  [
+    "id" => 233,
+    "book" => 192,
+    "name" => "Mūlapaṇṇāsa-ṭīkā",
+    "term" => "majjhimanikaya-ṭīkā",
+    "v_title" => "majjhimanikaya-ṭīkā",
+    "m_title" => "majjhimanikaya-ṭīkā",
+    "p_title" => "majjhimanikaya-ṭīkā",
+    "abbr" => "ma. ṭī."
+  ],
+  [
+    "id" => 234,
+    "book" => 193,
+    "name" => "Majjhimapaṇṇāsaṭīkā",
+    "term" => "majjhimanikaya-ṭīkā",
+    "v_title" => "majjhimanikaya-ṭīkā",
+    "m_title" => "majjhimanikaya-ṭīkā",
+    "p_title" => "majjhimanikaya-ṭīkā",
+    "abbr" => "ma. ṭī."
+  ],
+  [
+    "id" => 235,
+    "book" => 194,
+    "name" => "Uparipaṇṇāsa-ṭīkā",
+    "term" => "majjhimanikaya-ṭīkā",
+    "v_title" => "majjhimanikaya-ṭīkā",
+    "m_title" => "majjhimanikaya-ṭīkā",
+    "p_title" => "majjhimanikaya-ṭīkā",
+    "abbr" => "ma. ṭī."
+  ],
+  [
+    "id" => 236,
+    "book" => 195,
+    "name" => "Sagāthāvaggaṭīkā",
+    "term" => "saṃyuttanikāya-ṭīkā",
+    "v_title" => "saṃyuttanikāya-ṭīkā",
+    "m_title" => "saṃyuttanikāya-ṭīkā",
+    "p_title" => "saṃyuttanikāya-ṭīkā",
+    "abbr" => "saṃ. ṭī."
+  ],
+  [
+    "id" => 237,
+    "book" => 196,
+    "name" => "Nidānavaggaṭīkā",
+    "term" => "saṃyuttanikāya-ṭīkā",
+    "v_title" => "saṃyuttanikāya-ṭīkā",
+    "m_title" => "saṃyuttanikāya-ṭīkā",
+    "p_title" => "saṃyuttanikāya-ṭīkā",
+    "abbr" => "saṃ. ṭī."
+  ],
+  [
+    "id" => 238,
+    "book" => 197,
+    "name" => "Khandhavaggaṭīkā",
+    "term" => "saṃyuttanikāya-ṭīkā",
+    "v_title" => "saṃyuttanikāya-ṭīkā",
+    "m_title" => "saṃyuttanikāya-ṭīkā",
+    "p_title" => "saṃyuttanikāya-ṭīkā",
+    "abbr" => "saṃ. ṭī."
+  ],
+  [
+    "id" => 239,
+    "book" => 198,
+    "name" => "Saḷāyatanavaggaṭīkā",
+    "term" => "saṃyuttanikāya-ṭīkā",
+    "v_title" => "saṃyuttanikāya-ṭīkā",
+    "m_title" => "saṃyuttanikāya-ṭīkā",
+    "p_title" => "saṃyuttanikāya-ṭīkā",
+    "abbr" => "saṃ. ṭī."
+  ],
+  [
+    "id" => 240,
+    "book" => 199,
+    "name" => "Mahāvaggaṭīkā",
+    "term" => "saṃyuttanikāya-ṭīkā",
+    "v_title" => "saṃyuttanikāya-ṭīkā",
+    "m_title" => "saṃyuttanikāya-ṭīkā",
+    "p_title" => "saṃyuttanikāya-ṭīkā",
+    "abbr" => "saṃ. ṭī."
+  ],
+  [
+    "id" => 241,
+    "book" => 200,
+    "name" => "Vinayavinicchayo",
+    "term" => "vinayavinicchayo",
+    "v_title" => "vinayavinicchayo",
+    "m_title" => "vinayavinicchayo",
+    "p_title" => "vinayavinicchayo",
+    "abbr" => "vinayavinicchayo"
+  ],
+  [
+    "id" => 242,
+    "book" => 200,
+    "name" => "uttaravinicchayo",
+    "term" => "uttaravinicchayo",
+    "v_title" => "uttaravinicchayo",
+    "m_title" => "uttaravinicchayo",
+    "p_title" => "uttaravinicchayo",
+    "abbr" => "uttaravinicchayo"
+  ],
+  [
+    "id" => 243,
+    "book" => 201,
+    "name" => "Vinayavinicchayaṭīkā(Paṭhamo bhāgo)",
+    "term" => "vinayavinicchaya-ṭīkā",
+    "v_title" => "vinayavinicchaya-ṭīkā",
+    "m_title" => "vinayavinicchaya-ṭīkā",
+    "p_title" => "vinayavinicchaya-ṭīkā",
+    "abbr" => "vinayavinicchaya-ṭīkā"
+  ],
+  [
+    "id" => 244,
+    "book" => 201,
+    "name" => "uttaravinicchaya-ṭīkā",
+    "term" => "uttaravinicchaya-ṭīkā",
+    "v_title" => "uttaravinicchaya-ṭīkā",
+    "m_title" => "uttaravinicchaya-ṭīkā",
+    "p_title" => "uttaravinicchaya-ṭīkā",
+    "abbr" => "uttaravinicchaya-ṭīkā"
+  ],
+  [
+    "id" => 245,
+    "book" => 202,
+    "name" => "Pācityādiyojanā",
+    "term" => "pācityādiyojanā",
+    "v_title" => "pācityādiyojanā",
+    "m_title" => "pācityādiyojanā",
+    "p_title" => "pācityādiyojanā",
+    "abbr" => "pācityādiyojanā"
+  ],
+  [
+    "id" => 246,
+    "book" => 203,
+    "name" => "Khuddasikkhā-mūlasikkhā",
+    "term" => "khuddasikkhā",
+    "v_title" => "khuddasikkhā",
+    "m_title" => "khuddasikkhā",
+    "p_title" => "khuddasikkhā",
+    "abbr" => "khuddasikkhā"
+  ],
+  [
+    "id" => 247,
+    "book" => 203,
+    "name" => "khuddasikkhā",
+    "term" => "khuddasikkhā",
+    "v_title" => "khuddasikkhā",
+    "m_title" => "khuddasikkhā",
+    "p_title" => "khuddasikkhā",
+    "abbr" => "khuddasikkhā"
+  ],
+  [
+    "id" => 248,
+    "book" => 203,
+    "name" => "khuddasikkhā",
+    "term" => "khuddasikkhā",
+    "v_title" => "khuddasikkhā",
+    "m_title" => "khuddasikkhā",
+    "p_title" => "khuddasikkhā",
+    "abbr" => "khuddasikkhā"
+  ],
+  [
+    "id" => 249,
+    "book" => 203,
+    "name" => "mūlasikkhā",
+    "term" => "mūlasikkhā",
+    "v_title" => "mūlasikkhā",
+    "m_title" => "mūlasikkhā",
+    "p_title" => "mūlasikkhā",
+    "abbr" => "mūlasikkhā"
+  ],
+  [
+    "id" => 250,
+    "book" => 203,
+    "name" => "mūlasikkhā",
+    "term" => "mūlasikkhā",
+    "v_title" => "mūlasikkhā",
+    "m_title" => "mūlasikkhā",
+    "p_title" => "mūlasikkhā",
+    "abbr" => "mūlasikkhā"
+  ],
+  [
+    "id" => 251,
+    "book" => 204,
+    "name" => "sāratthadīpanī-ṭīkā (paṭhamo bhāgo)",
+    "term" => "sāratthadīpanī-ṭīkā",
+    "v_title" => "sāratthadīpanī-ṭīkā",
+    "m_title" => "sāratthadīpanī-ṭīkā",
+    "p_title" => "sāratthadīpanī-ṭīkā",
+    "abbr" => "sārattha. ṭī."
+  ],
+  [
+    "id" => 252,
+    "book" => 205,
+    "name" => "sāratthadīpanī-ṭīkā (dutiyo bhāgo)",
+    "term" => "sāratthadīpanī-ṭīkā",
+    "v_title" => "sāratthadīpanī-ṭīkā",
+    "m_title" => "sāratthadīpanī-ṭīkā",
+    "p_title" => "sāratthadīpanī-ṭīkā",
+    "abbr" => "sārattha. ṭī."
+  ],
+  [
+    "id" => 253,
+    "book" => 206,
+    "name" => "sāratthadīpanī-ṭīkā (tatiyo bhāgo)",
+    "term" => "sāratthadīpanī-ṭīkā",
+    "v_title" => "sāratthadīpanī-ṭīkā",
+    "m_title" => "sāratthadīpanī-ṭīkā",
+    "p_title" => "sāratthadīpanī-ṭīkā",
+    "abbr" => "sārattha. ṭī."
+  ],
+  [
+    "id" => 254,
+    "book" => 206,
+    "name" => "sāratthadīpanī-ṭīkā",
+    "term" => "sāratthadīpanī-ṭīkā",
+    "v_title" => "sāratthadīpanī-ṭīkā",
+    "m_title" => "sāratthadīpanī-ṭīkā",
+    "p_title" => "sāratthadīpanī-ṭīkā",
+    "abbr" => "sārattha. ṭī."
+  ],
+  [
+    "id" => 255,
+    "book" => 206,
+    "name" => "sāratthadīpanī-ṭīkā",
+    "term" => "sāratthadīpanī-ṭīkā",
+    "v_title" => "sāratthadīpanī-ṭīkā",
+    "m_title" => "sāratthadīpanī-ṭīkā",
+    "p_title" => "sāratthadīpanī-ṭīkā",
+    "abbr" => "sārattha. ṭī."
+  ],
+  [
+    "id" => 256,
+    "book" => 206,
+    "name" => "sāratthadīpanī-ṭīkā",
+    "term" => "sāratthadīpanī-ṭīkā",
+    "v_title" => "sāratthadīpanī-ṭīkā",
+    "m_title" => "sāratthadīpanī-ṭīkā",
+    "p_title" => "sāratthadīpanī-ṭīkā",
+    "abbr" => "sārattha. ṭī."
+  ],
+  [
+    "id" => 257,
+    "book" => 207,
+    "name" => "Bhikkhupātimokkhapāḷi",
+    "term" => "pātimokkhapāḷi",
+    "v_title" => "pātimokkhapāḷi",
+    "m_title" => "pātimokkhapāḷi",
+    "p_title" => "pātimokkhapāḷi",
+    "abbr" => "pātimokkha"
+  ],
+  [
+    "id" => 258,
+    "book" => 207,
+    "name" => "pātimokkhapāḷi",
+    "term" => "pātimokkhapāḷi",
+    "v_title" => "pātimokkhapāḷi",
+    "m_title" => "pātimokkhapāḷi",
+    "p_title" => "pātimokkhapāḷi",
+    "abbr" => "pātimokkha"
+  ],
+  [
+    "id" => 259,
+    "book" => 207,
+    "name" => "kaṅkhāvitaraṇī",
+    "term" => "kaṅkhāvitaraṇī",
+    "v_title" => "kaṅkhāvitaraṇī",
+    "m_title" => "kaṅkhāvitaraṇī",
+    "p_title" => "kaṅkhāvitaraṇī",
+    "abbr" => "kaṅkhā."
+  ],
+  [
+    "id" => 260,
+    "book" => 208,
+    "name" => "Vinayasaṅgaha-aṭṭhakathā",
+    "term" => "vinayasaṅgaha-aṭṭhakathā",
+    "v_title" => "vinayasaṅgaha-aṭṭhakathā",
+    "m_title" => "vinayasaṅgaha-aṭṭhakathā",
+    "p_title" => "vinayasaṅgaha-aṭṭhakathā",
+    "abbr" => "vinayasaṅgaha-aṭṭhakathā"
+  ],
+  [
+    "id" => 261,
+    "book" => 209,
+    "name" => "Vajirabuddhi-ṭīkā",
+    "term" => "vajirabuddhi-ṭīkā",
+    "v_title" => "vajirabuddhi-ṭīkā",
+    "m_title" => "vajirabuddhi-ṭīkā",
+    "p_title" => "vajirabuddhi-ṭīkā",
+    "abbr" => "vajīra. ṭī."
+  ],
+  [
+    "id" => 262,
+    "book" => 209,
+    "name" => "vajirabuddhi-ṭīkā",
+    "term" => "vajirabuddhi-ṭīkā",
+    "v_title" => "vajirabuddhi-ṭīkā",
+    "m_title" => "vajirabuddhi-ṭīkā",
+    "p_title" => "vajirabuddhi-ṭīkā",
+    "abbr" => "vajīra. ṭī."
+  ],
+  [
+    "id" => 263,
+    "book" => 209,
+    "name" => "vajirabuddhi-ṭīkā",
+    "term" => "vajirabuddhi-ṭīkā",
+    "v_title" => "vajirabuddhi-ṭīkā",
+    "m_title" => "vajirabuddhi-ṭīkā",
+    "p_title" => "vajirabuddhi-ṭīkā",
+    "abbr" => "vajīra. ṭī."
+  ],
+  [
+    "id" => 264,
+    "book" => 209,
+    "name" => "vajirabuddhi-ṭīkā",
+    "term" => "vajirabuddhi-ṭīkā",
+    "v_title" => "vajirabuddhi-ṭīkā",
+    "m_title" => "vajirabuddhi-ṭīkā",
+    "p_title" => "vajirabuddhi-ṭīkā",
+    "abbr" => "vajīra. ṭī."
+  ],
+  [
+    "id" => 265,
+    "book" => 209,
+    "name" => "vajirabuddhi-ṭīkā",
+    "term" => "vajirabuddhi-ṭīkā",
+    "v_title" => "vajirabuddhi-ṭīkā",
+    "m_title" => "vajirabuddhi-ṭīkā",
+    "p_title" => "vajirabuddhi-ṭīkā",
+    "abbr" => "vajīra. ṭī."
+  ],
+  [
+    "id" => 266,
+    "book" => 209,
+    "name" => "vajirabuddhi-ṭīkā",
+    "term" => "vajirabuddhi-ṭīkā",
+    "v_title" => "vajirabuddhi-ṭīkā",
+    "m_title" => "vajirabuddhi-ṭīkā",
+    "p_title" => "vajirabuddhi-ṭīkā",
+    "abbr" => "vajīra. ṭī."
+  ],
+  [
+    "id" => 267,
+    "book" => 210,
+    "name" => "Vimativinodanī-ṭīkā",
+    "term" => "vimativinodanī-ṭīkā",
+    "v_title" => "vimativinodanī-ṭīkā",
+    "m_title" => "vimativinodanī-ṭīkā",
+    "p_title" => "vimativinodanī-ṭīkā",
+    "abbr" => "vimati. ṭī."
+  ],
+  [
+    "id" => 268,
+    "book" => 210,
+    "name" => "Vimativinodanī-ṭīkā",
+    "term" => "Vimativinodanī-ṭīkā",
+    "v_title" => "vimativinodanī-ṭīkā",
+    "m_title" => "vimativinodanī-ṭīkā",
+    "p_title" => "Vimativinodanī-ṭīkā",
+    "abbr" => "vimati. ṭī."
+  ],
+  [
+    "id" => 269,
+    "book" => 210,
+    "name" => "Vimativinodanī-ṭīkā",
+    "term" => "Vimativinodanī-ṭīkā",
+    "v_title" => "vimativinodanī-ṭīkā",
+    "m_title" => "vimativinodanī-ṭīkā",
+    "p_title" => "Vimativinodanī-ṭīkā",
+    "abbr" => "vimati. ṭī."
+  ],
+  [
+    "id" => 270,
+    "book" => 210,
+    "name" => "Vimativinodanī-ṭīkā",
+    "term" => "Vimativinodanī-ṭīkā",
+    "v_title" => "vimativinodanī-ṭīkā",
+    "m_title" => "vimativinodanī-ṭīkā",
+    "p_title" => "Vimativinodanī-ṭīkā",
+    "abbr" => "vimati. ṭī."
+  ],
+  [
+    "id" => 271,
+    "book" => 210,
+    "name" => "Vimativinodanī-ṭīkā",
+    "term" => "Vimativinodanī-ṭīkā",
+    "v_title" => "vimativinodanī-ṭīkā",
+    "m_title" => "vimativinodanī-ṭīkā",
+    "p_title" => "Vimativinodanī-ṭīkā",
+    "abbr" => "vimati. ṭī."
+  ],
+  [
+    "id" => 272,
+    "book" => 211,
+    "name" => "Vinayālaṅkāra-ṭīkā",
+    "term" => "vinayālaṅkāra-ṭīkā",
+    "v_title" => "vinayālaṅkāra-ṭīkā",
+    "m_title" => "vinayālaṅkāra-ṭīkā",
+    "p_title" => "vinayālaṅkāra-ṭīkā",
+    "abbr" => "ālaṅkāra. ṭī."
+  ],
+  [
+    "id" => 273,
+    "book" => 212,
+    "name" => "Kaṅkhāvitaraṇīpurāṇa-ṭīkā",
+    "term" => "kaṅkhāvitaraṇīpurāṇa-ṭīkā",
+    "v_title" => "kaṅkhāvitaraṇīpurāṇa-ṭīkā",
+    "m_title" => "kaṅkhāvitaraṇīpurāṇa-ṭīkā",
+    "p_title" => "kaṅkhāvitaraṇīpurāṇa-ṭīkā",
+    "abbr" => "kaṅkhā. ṭī."
+  ],
+  [
+    "id" => 274,
+    "book" => 212,
+    "name" => "Kaṅkhāvitaraṇī-abhinavaṭīkā",
+    "term" => "kaṅkhāvitaraṇī-abhinavaṭīkā",
+    "v_title" => "kaṅkhāvitaraṇī-abhinavaṭīkā",
+    "m_title" => "kaṅkhāvitaraṇī-abhinavaṭīkā",
+    "p_title" => "kaṅkhāvitaraṇī-abhinavaṭīkā",
+    "abbr" => "kaṅkhā."
+  ],
+  [
+    "id" => 275,
+    "book" => 213,
+    "name" => "Pārājikapāḷi",
+    "term" => "pārājikapāḷi",
+    "v_title" => "pārājikapāḷi",
+    "m_title" => "pārājikapāḷi",
+    "p_title" => "vinayapiṭaka",
+    "abbr" => "vi."
+  ],
+  [
+    "id" => 276,
+    "book" => 214,
+    "name" => "Pācittiyapāḷi",
+    "term" => "pācittiyapāḷi",
+    "v_title" => "pācittiyapāḷi",
+    "m_title" => "pācittiyapāḷi",
+    "p_title" => "vinayapiṭaka",
+    "abbr" => "vi."
+  ],
+  [
+    "id" => 277,
+    "book" => 215,
+    "name" => "Mahāvaggapāḷi",
+    "term" => "mahāvaggapāḷi",
+    "v_title" => "mahāvaggapāḷi",
+    "m_title" => "mahāvaggapāḷi",
+    "p_title" => "vinayapiṭaka",
+    "abbr" => "vi."
+  ],
+  [
+    "id" => 278,
+    "book" => 216,
+    "name" => "Cūḷavaggapāḷi",
+    "term" => "cūḷavaggapāḷi",
+    "v_title" => "cūḷavaggapāḷi",
+    "m_title" => "cūḷavaggapāḷi",
+    "p_title" => "vinayapiṭaka",
+    "abbr" => "vi."
+  ],
+  [
+    "id" => 279,
+    "book" => 217,
+    "name" => "Parivārapāḷi",
+    "term" => "parivārapāḷi",
+    "v_title" => "parivārapāḷi",
+    "m_title" => "parivārapāḷi",
+    "p_title" => "vinayapiṭaka",
+    "abbr" => "vi."
+  ],
+  [
+    "id" => 280,
+    "book" => 139,
+    "name" => "samantapāsādikā",
+    "term" => "vinaya-aṭṭhakathā",
+    "v_title" => "vinaya-aṭṭhakathā",
+    "m_title" => "vinaya-aṭṭhakathā",
+    "p_title" => "vinaya-aṭṭhakathā",
+    "abbr" => "vi. ṭṭha."
+  ],
+  [
+    "id" => 281,
+    "book" => 214,
+    "name" => "(VN)Bhikkhunīvibhaṅgo",
+    "term" => "pācittiyapāḷi",
+    "v_title" => "pācittiyapāḷi",
+    "m_title" => "pācittiyapāḷi",
+    "p_title" => "vinayapiṭaka",
+    "abbr" => "vi."
+  ]
+  ];
+
+
+
+    }
+}

+ 172 - 0
api-v12/app/Http/Api/ChannelApi.php

@@ -0,0 +1,172 @@
+<?php
+
+namespace App\Http\Api;
+
+use App\Models\Channel;
+use Illuminate\Support\Str;
+use Illuminate\Support\Facades\Log;
+
+class ChannelApi
+{
+    public static function getById($id)
+    {
+        if (!Str::isUuid($id)) {
+            return false;
+        }
+        $channel = Channel::where("uid", $id)->first();
+        if ($channel) {
+            return [
+                'id' => $id,
+                'name' => $channel['name'],
+                'type' => $channel['type'],
+                'lang' => $channel['lang'],
+                'studio_id' => $channel['owner_uid'],
+                'status' => $channel['status'],
+            ];
+        } else {
+            return false;
+        }
+    }
+    public static function getCanReadByUser($userUuid = null)
+    {
+        #获取 user 在某章节 所有有权限的 channel 列表
+        $channelId = [];
+        //我自己的
+
+        if ($userUuid) {
+            $my = Channel::select('uid')->where('owner_uid', $userUuid)->get();
+            foreach ($my as $key => $value) {
+                $channelId[$value->uid] = $value->uid;
+            }
+
+            //获取共享channel
+
+            $allSharedChannels = ShareApi::getResList($userUuid, 2);
+            foreach ($allSharedChannels as $key => $value) {
+                $channelId[$value['res_id']] = $value['res_id'];
+            }
+        }
+        //获取全网公开的channel
+        $my = Channel::select('uid')->where('status', 30)->get();
+        foreach ($my as $key => $value) {
+            $channelId[$value->uid] = $value->uid;
+        }
+        $output = array();
+        foreach ($channelId as $key => $value) {
+            $output[] = $key;
+        }
+        return $output;
+    }
+
+    public static function getCanEditByUser($userUuid = null)
+    {
+        #获取 user 在某章节 所有有权限的 channel 列表
+        $channelId = [];
+        //我自己的
+
+        if ($userUuid) {
+            $my = Channel::select('uid')->where('owner_uid', $userUuid)->get();
+            foreach ($my as $key => $value) {
+                $channelId[$value->uid] = $value->uid;
+            }
+
+            //获取共享channel
+
+            $allSharedChannels = ShareApi::getResList($userUuid, 2);
+            foreach ($allSharedChannels as $key => $value) {
+                if ($value['power'] >= 20) {
+                    $channelId[$value['res_id']] = $value['res_id'];
+                }
+            }
+        }
+
+        $output = array();
+        foreach ($channelId as $key => $value) {
+            $output[] = $key;
+        }
+        return $output;
+    }
+    public static function userCanRead($userUid, $channelUid)
+    {
+        $channels = ChannelApi::getCanReadByUser($userUid);
+        return in_array($channelUid, $channels);
+    }
+    public static function userCanEdit($userUid, $channelUid)
+    {
+        $channels = ChannelApi::getCanEditByUser($userUid);
+        return in_array($channelUid, $channels);
+    }
+
+    public static function canManageByUser($channelId, $userUuid)
+    {
+        $isOwner = Channel::where('owner_uid', $userUuid)
+            ->where('uid', $channelId)->exists();
+        return $isOwner;
+    }
+    public static function getSysChannel($channel_name, $fallback = "")
+    {
+        $channel = Channel::where('name', $channel_name)
+            ->where('owner_uid', config("mint.admin.root_uuid"))
+            ->first();
+        if (!$channel) {
+            if (!empty($fallback)) {
+                $channel = Channel::where('name', $fallback)
+                    ->where('owner_uid', config("mint.admin.root_uuid"))
+                    ->first();
+                if (!$channel) {
+                    return false;
+                } else {
+                    return $channel->uid;
+                }
+            }
+            return false;
+        } else {
+            return $channel->uid;
+        }
+    }
+
+    public static function getChannelByName($name)
+    {
+        $channel = Channel::where('name', $name)
+            ->first();
+        if (!$channel) {
+            throw new \Exception('channel is invalid');
+        }
+        return $channel;
+    }
+
+    /**
+     * 获取某个studio 的某个语言的自定义书的channel
+     * 如果没有,建立
+     */
+    public static function userBookGetOrCreate($studioId, $lang, $status)
+    {
+        $channelName = '_user_book_' . $lang;
+        $channel = Channel::where('owner_uid', $studioId)
+            ->where('name', $channelName)->first();
+        if ($channel) {
+            return $channel->uid;
+        }
+        $channelUuid = Str::uuid();
+        $channel = new Channel;
+        $channel->id = app('snowflake')->id();
+        $channel->uid = $channelUuid;
+        $channel->owner_uid = $studioId;
+        $channel->name = $channelName;
+        $channel->type = 'original';
+        $channel->lang = $lang;
+        $channel->editor_id = 0;
+        $channel->is_system = true;
+        $channel->create_time = time() * 1000;
+        $channel->modify_time = time() * 1000;
+        $channel->status = $status;
+        $saveOk = $channel->save();
+        if ($saveOk) {
+            Log::debug('copy user book : create channel success name=' . $channelName);
+            return $channel->uid;
+        } else {
+            Log::error('copy user book : create channel fail.', ['channel' => $channelName, 'studioId' => $studioId]);
+            return false;
+        }
+    }
+}

+ 30 - 0
api-v12/app/Http/Api/CourseApi.php

@@ -0,0 +1,30 @@
+<?php
+namespace App\Http\Api;
+
+use App\Models\CourseMember;
+use Illuminate\Support\Facades\Log;
+
+class CourseApi{
+    public static function getStudentChannels($courseId){
+        $channels = [];
+        $studentsChannel = CourseMember::where('course_id',$courseId)
+                ->whereNotNull('channel_id')
+                ->where('role','student')
+                ->whereIn('status',['joined','accepted','agreed'])
+                ->select('channel_id')
+                ->orderBy('created_at')
+                ->get();
+        foreach ($studentsChannel as $key => $channel) {
+            $channels[] = $channel->channel_id;
+        }
+        return $channels;
+    }
+
+    public static function role($courseId,$userUid){
+        $role = CourseMember::where('course_id',$courseId)
+                            ->where('user_id',$userUid)
+                            ->where('is_current',true)
+                            ->value('role');
+        return $role;
+    }
+}

+ 81 - 0
api-v12/app/Http/Api/DictApi.php

@@ -0,0 +1,81 @@
+<?php
+namespace App\Http\Api;
+use App\Models\DictInfo;
+
+class DictApi{
+    public static function langOrder(string $lang){
+        switch ($lang) {
+            case 'zh':
+                $output = ["zh","jp","en","my"];
+                break;
+            case 'en':
+                $output = ["en","my"];
+                break;
+            case 'my':
+                $output = ["my","en"];
+                break;
+            default:
+                $output = [$lang,"en","my"];
+                break;
+        }
+        $output[] = "others";
+        return $output;
+    }
+
+    public static function dictOrder($lang){
+        $output = [];
+        switch ($lang) {
+            case 'zh':
+                $output = [
+                "0d79e8e8-1430-4c99-a0f1-b74f2b4b26d8",	/*《巴汉词典》增订*/
+                "f364d3dc-b611-471b-9a4f-531286b8c2c3",	/*《巴汉词典》Mahāñāṇo Bhikkhu编著*/
+                "0e4dc5c8-a228-4693-92ba-7d42918d8a91",	/*汉译パーリ语辞典-黃秉榮*/
+                "6aa9ec8b-bba4-4bcd-abd2-9eae015bad2b",	/*汉译パーリ语辞典-李瑩*/
+                "eb99f8b4-c3e5-43af-9102-6a93fcb97db6",	/*パーリ语辞典--勘误表*/
+                ];
+                break;
+            case 'jp':
+                $output = [
+                "91d3ec93-3811-4973-8d84-ced99179a0aa",	/*パーリ语辞典*/
+                "6d6c6812-75e7-457d-874f-5b049ad4b6de",	/*パーリ语辞典-增补*/
+                ];
+                break;
+            case 'en':
+                $output = [
+                "c6e70507-4a14-4687-8b70-2d0c7eb0cf21",	/*	Concise P-E Dict*/
+                "6afb8c05-5cbe-422e-b691-0d4507450cb7",	/*	PTS P-E dictionary*/
+                ];
+                break;
+            case 'my':
+                $output =[
+                "e740ef40-26d7-416e-96c2-925d6650ac6b",	/*	Tipiṭaka Pāḷi-Myanmar*/
+                "beb45062-7c20-4047-bcd4-1f636ba443d1",	/*	U Hau Sein’s Pāḷi-Myanmar Dictionary*/
+                "1e299ccb-4fc4-487d-8d72-08f63d84c809",	/*	Pali Roots Dictionary*/
+                "6f9caea1-17fa-41f1-92e5-bd8e6e70e1d7",	/*	U Hau Sein’s Pāḷi-Myanmar*/
+                ];
+                break;
+            case 'vi':
+                $output = [
+                "23f67523-fa03-48d9-9dda-ede80d578dd2",	/*	Pali Viet Dictionary*/
+                "4ac8a0d5-9c6f-4b9f-983d-84288d47f993",	/*	Pali Viet Abhi-Terms*/
+                "7c7ee287-35ba-4cf3-b87b-30f1fa6e57c9",	/*	Pali Viet Vinaya Terms*/
+                ];
+                break;
+            default:
+                $output = [];
+                break;
+        };
+        $output[] = "others";
+        return $output;
+    }
+    public static function getSysDict($name){
+        $dict_info=  DictInfo::where('name',$name)
+                    ->where('owner_id',config("mint.admin.root_uuid"))
+                    ->first();
+        if(!$dict_info){
+            return false;
+        }else{
+            return $dict_info->id;
+        }
+    }
+}

+ 24 - 0
api-v12/app/Http/Api/GroupApi.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace App\Http\Api;
+
+use App\Models\GroupInfo;
+
+class GroupApi
+{
+    public static function getById($id)
+    {
+        $group = GroupInfo::where("uid", $id)->first();
+        if ($group) {
+            return [
+                'id' => $id,
+                'name' => $group->name,
+                'realName' => $group->uid,
+                'studioName' => $group->name,
+                'nickName' => $group->name,
+            ];
+        } else {
+            return null;
+        }
+    }
+}

+ 683 - 0
api-v12/app/Http/Api/MdRender.php

@@ -0,0 +1,683 @@
+<?php
+
+namespace App\Http\Api;
+
+use Illuminate\Support\Str;
+use App\Models\Channel;
+use Illuminate\Support\Facades\Log;
+use App\Tools\Markdown;
+//use App\Services\TemplateRender;
+
+define("STACK_DEEP", 8);
+
+class MdRender
+{
+    /**
+     * 文字渲染模式
+     * read 阅读模式
+     * edit 编辑模式
+     */
+    protected $options = [
+        'mode' => 'read',
+        'channelType' => 'translation',
+        'contentType' => "markdown",
+        'format' => 'react',
+        'debug' => [],
+        'studioId' => null,
+        'lang' => 'zh-Hans',
+        'footnote' => false,
+        'paragraph' => false,
+    ];
+
+    public function __construct($options = [])
+    {
+        foreach ($options as $key => $value) {
+            $this->options[$key] = $value;
+        }
+    }
+
+    /**
+     * 将句子模版组成的段落复制一份,为了实现巴汉逐段对读
+     */
+    private function preprocessingForParagraph($input)
+    {
+        if (!$this->options['paragraph']) {
+            return $input;
+        }
+        $paragraphs = explode("\n\n", $input);
+        $output = [];
+        foreach ($paragraphs as $key => $paragraph) {
+            # 判断是否是纯粹的句子模版
+            $pattern = "/\{\{sent\|id=([0-9].+?)\}\}/";
+            $replacement = '';
+            $space = preg_replace($pattern, $replacement, $paragraph);
+            $space = str_replace('>', '', $space);
+            if (empty(trim($space))) {
+                $output[] = str_replace('}}', '|text=origin}}', $paragraph);
+                $output[] = str_replace('}}', '|text=translation}}', $paragraph);
+            } else {
+                $output[] = $paragraph;
+            }
+        }
+
+        return implode("\n\n", $output);
+    }
+
+    /**
+     * 按照{{}}把字符串切分成三个部分。模版之前的,模版,和模版之后的
+     */
+    private function tplSplit($tpl)
+    {
+        $before = strpos($tpl, '{{');
+        if ($before === FALSE) {
+            //未找到
+            return ['data' => [$tpl, '', ''], 'error' => 0];
+        } else {
+            $pointer = $before;
+            $stack = array();
+            $stack[] = $pointer;
+            $after = substr($tpl, $pointer + 2);
+            while (!empty($after) && count($stack) > 0 && count($stack) < STACK_DEEP) {
+                $nextBegin = strpos($after, "{{");
+                $nextEnd = strpos($after, "}}");
+                if ($nextBegin !== FALSE) {
+                    if ($nextBegin < $nextEnd) {
+                        //有嵌套找到最后一个}}
+                        $pointer = $pointer + 2 + $nextBegin;
+                        $stack[] = $pointer;
+                        $after = substr($tpl, $pointer + 2);
+                    } else if ($nextEnd !== FALSE) {
+                        //无嵌套有结束
+                        $pointer = $pointer + 2 + $nextEnd;
+                        array_pop($stack);
+                        $after = substr($tpl, $pointer + 2);
+                    } else {
+                        //无结束符 没找到
+                        break;
+                    }
+                } else if ($nextEnd !== FALSE) {
+                    $pointer = $pointer + 2 + $nextEnd;
+                    array_pop($stack);
+                    $after = substr($tpl, $pointer + 2);
+                } else {
+                    //没找到
+                    break;
+                }
+            }
+            if (count($stack) > 0) {
+                if (count($stack) === STACK_DEEP) {
+                    return ['data' => [$tpl, '', ''], 'error' => 2];
+                } else {
+                    //未关闭
+                    return ['data' => [$tpl, '', ''], 'error' => 1];
+                }
+            } else {
+                return [
+                    'data' =>
+                    [
+                        substr($tpl, 0, $before),
+                        substr($tpl, $before, $pointer - $before + 2),
+                        substr($tpl, $pointer + 2)
+                    ],
+                    'error' => 0
+                ];
+            }
+        }
+    }
+
+    private function wiki2xml(string $wiki, $channelId = []): string
+    {
+        /**
+         * 渲染markdown里面的模版
+         */
+        $remain = $wiki;
+        $buffer = array();
+        do {
+            $arrWiki = $this->tplSplit($remain);
+            $buffer[] = $arrWiki['data'][0];
+            $tpl = $arrWiki['data'][1];
+            if (!empty($tpl)) {
+                /**
+                 * 处理模版 提取参数
+                 */
+                $tpl = str_replace("|\n", "|", $tpl);
+                $pattern = "/\{\{(.+?)\|/";
+                $replacement = '<MdTpl class="tpl" name="$1"><param>';
+                $tpl = preg_replace($pattern, $replacement, $tpl);
+                $tpl = str_replace("}}", "</param></MdTpl>", $tpl);
+                $tpl = str_replace("|", "</param><param>", $tpl);
+                /**
+                 * 替换变量名
+                 */
+                $pattern = "/<param>([a-z]+?)=/";
+                $replacement = '<param name="$1">';
+                $tpl = preg_replace($pattern, $replacement, $tpl);
+                //tpl to react
+                $tpl = str_replace('<param', '<span class="param"', $tpl);
+                $tpl = str_replace('</param>', '</span>', $tpl);
+                $tpl = $this->xml2tpl($tpl, $channelId);
+                $buffer[] = $tpl;
+            }
+            $remain = $arrWiki['data'][2];
+        } while (!empty($remain));
+
+        $html = implode('', $buffer);
+
+        return $html;
+    }
+    private function xmlQueryId(string $xml, string $id): string
+    {
+        try {
+            $dom = simplexml_load_string($xml);
+        } catch (\Exception $e) {
+            Log::error($e);
+            return "<div></div>";
+        }
+        $tpl_list = $dom->xpath('//MdTpl');
+        foreach ($tpl_list as $key => $tpl) {
+            foreach ($tpl->children() as  $param) {
+                # 处理每个参数
+                if ($param->getName() === "param") {
+                    foreach ($param->attributes() as $pa => $pa_value) {
+                        $pValue = $pa_value->__toString();
+                        if ($pa === "name" && $pValue === "id") {
+                            if ($param->__toString() === $id) {
+                                return $tpl->asXML();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return "<div></div>";
+    }
+    public static function take_sentence(string $xml): array
+    {
+        $output = [];
+        try {
+            $dom = simplexml_load_string($xml);
+        } catch (\Exception $e) {
+            Log::error($e);
+            return $output;
+        }
+        $tpl_list = $dom->xpath('//MdTpl');
+        foreach ($tpl_list as $key => $tpl) {
+            foreach ($tpl->attributes() as $a => $a_value) {
+                if ($a === "name") {
+                    if ($a_value->__toString() === "sent") {
+                        foreach ($tpl->children() as  $param) {
+                            # 处理每个参数
+                            if ($param->getName() === "param") {
+                                $sent = $param->__toString();
+                                if (!empty($sent)) {
+                                    $output[] = $sent;
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return $output;
+    }
+    private function xml2tpl(string $xml, $channelId = []): string
+    {
+        /**
+         * 解析xml
+         * 获取模版参数
+         * 生成react 组件参数
+         */
+        try {
+            //$dom = simplexml_load_string($xml);
+            $doc = new \DOMDocument();
+            $xml = str_replace('MdTpl', 'dfn', $xml);
+            $xml = mb_convert_encoding($xml, 'HTML-ENTITIES', "UTF-8");
+            $ok = $doc->loadHTML($xml, LIBXML_NOERROR  | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
+        } catch (\Exception $e) {
+            Log::error($e);
+            Log::error($xml);
+            return "<span>xml解析错误{$e}</span>";
+        }
+
+        if (!$ok) {
+            return "<span>xml解析错误</span>";
+        }
+        /*
+        if(!$dom){
+            Log::error($xml);
+            return "<span>xml解析错误</span>";
+        }
+        */
+
+        $tpl_list = $doc->getElementsByTagName('dfn');
+
+        foreach ($tpl_list as $key => $tpl) {
+            /**
+             * 遍历 MdTpl 处理参数
+             */
+            $props = [];
+            $tpl_name = '';
+            foreach ($tpl->attributes as $a => $a_value) {
+                if ($a_value->nodeName === "name") {
+                    $tpl_name = $a_value->nodeValue;
+                    break;
+                }
+            }
+            $param_id = 0;
+            $child = $tpl->firstChild;
+            while ($child) {
+                # 处理每个参数
+                if ($child->nodeName === "span") {
+                    $param_id++;
+                    $paramName = "";
+                    foreach ($child->attributes as $pa => $pa_value) {
+                        if ($pa_value->nodeName === "name") {
+                            $nodeText = $pa_value->nodeValue;
+                            $props["{$nodeText}"] = $child->nodeValue;
+                            $paramName = $pa_value;
+                        }
+                    }
+                    if (empty($paramName)) {
+                        foreach ($child->childNodes as $param_child) {
+                            # code...
+                            if ($param_child->nodeType === 3) {
+                                $props["{$param_id}"] = $param_child->nodeValue;
+                            }
+                        }
+                    }
+                }
+                $child = $child->nextSibling;
+            }
+            /**
+             * 生成模版参数
+             *
+             */
+            //TODO 判断$channelId里面的是否都是uuid
+            $channelInfo = [];
+            foreach ($channelId as $key => $id) {
+                $channelInfo[] = Channel::where('uid', $id)->first();
+            }
+
+            $tplRender = new TemplateRender(
+                $props,
+                $channelInfo,
+                $this->options['mode'],
+                $this->options['format'],
+                $this->options['studioId'],
+                $this->options['debug'],
+                $this->options['lang'],
+            );
+            $tplRender->options($this->options);
+            $tplProps = $tplRender->render($tpl_name);
+
+            /*
+            $tplProps = TemplateRender::name($tpl_name)
+                ->options($this->options)
+                ->param($props)
+                ->render();
+                */
+            if ($this->options['format'] === 'react' && $tplProps) {
+                $props = $doc->createAttribute("props");
+                $props->nodeValue = $tplProps['props'];
+                $tpl->appendChild($props);
+                $attTpl = $doc->createAttribute("tpl");
+                $attTpl->nodeValue = $tplProps['tpl'];
+                $tpl->appendChild($attTpl);
+                $htmlElement = $doc->createElement($tplProps['tag']);
+                $htmlElement->nodeValue = $tplProps['html'];
+                $tpl->appendChild($htmlElement);
+            }
+        }
+        $html = $doc->saveHTML();
+        $html = str_replace(['<dfn', '</dfn>'], ['<MdTpl', '</MdTpl>'], $html);
+        switch ($this->options['format']) {
+            case 'react':
+                return trim($html);
+                break;
+            case 'unity':
+                if ($tplProps) {
+                    return "{{" . "{$tplProps['tpl']}|{$tplProps['props']}" . "}}";
+                } else {
+                    return '';
+                }
+                break;
+            default:
+                /**html tex text simple markdown */
+                if (isset($tplProps)) {
+                    if (is_array($tplProps)) {
+                        return $tplProps[0];
+                    } else {
+                        return $tplProps;
+                    }
+                } else {
+                    Log::error('tplProps undefine');
+                    return '';
+                }
+                break;
+        }
+    }
+
+    /**
+     * 将markdown文件中的模版转换为标准的wiki模版
+     */
+    private function markdown2wiki(string $markdown): string
+    {
+        //$markdown = mb_convert_encoding($markdown,'UTF-8','UTF-8');
+        $markdown = iconv('UTF-8', 'UTF-8//IGNORE', $markdown);
+        /**
+         * nissaya
+         * aaa=bbb\n
+         * {{nissaya|aaa|bbb}}
+         */
+        if ($this->options['channelType'] === 'nissaya') {
+            if ($this->options['contentType'] === "json") {
+                $json = json_decode($markdown);
+                $nissayaWord = [];
+                if (is_array($json)) {
+                    foreach ($json as $word) {
+                        if (count($word->sn) === 1) {
+                            //只输出第一层级
+                            $str = "{{nissaya|";
+                            if (isset($word->word->value)) {
+                                $str .= $word->word->value;
+                            }
+                            $str .= "|";
+                            if (isset($word->meaning->value)) {
+                                $str .= $word->meaning->value;
+                            }
+                            $str .= "}}";
+                            $nissayaWord[] = $str;
+                        }
+                    }
+                } else {
+                    Log::error('json data is not array', ['data' => $markdown]);
+                }
+
+                $markdown = implode('', $nissayaWord);
+            } else if ($this->options['contentType'] === "markdown") {
+                $lines = explode("\n", $markdown);
+                $newLines = array();
+                foreach ($lines as  $line) {
+                    if (
+                        strstr($line, '=') === FALSE &&
+                        strstr($line, '$') === FALSE
+                    ) {
+                        $newLines[] = $line;
+                    } else {
+                        $line = str_replace('$', '=', $line);
+                        $nissaya = explode('=', $line);
+                        $meaning = array_slice($nissaya, 1);
+                        $meaning = implode('=', $meaning);
+                        $newLines[] = "{{nissaya|{$nissaya[0]}|{$meaning}}}";
+                    }
+                }
+                $markdown = implode("\n", $newLines);
+            }
+        }
+        //$markdown = preg_replace("/\n\n/","<div></div>",$markdown);
+
+        /**
+         * 处理 mermaid
+         */
+        if (strpos($markdown, "```mermaid") !== false) {
+            $lines = explode("\n", $markdown);
+            $newLines = array();
+            $mermaidBegin = false;
+            $mermaidString = array();
+            foreach ($lines as  $line) {
+                if ($line === "```mermaid") {
+                    $mermaidBegin = true;
+                    $mermaidString = [];
+                    continue;
+                }
+                if ($mermaidBegin) {
+                    if ($line === "```") {
+                        $newLines[] = "{{mermaid|" . base64_encode(\json_encode($mermaidString)) . "}}";
+                        $mermaidBegin = false;
+                    } else {
+                        $mermaidString[] = $line;
+                    }
+                } else {
+                    $newLines[] = $line;
+                }
+            }
+            $markdown = implode("\n", $newLines);
+        }
+
+        /**
+         * 替换换行符
+         * react 无法处理 <br> 替换为<div></div>代替换行符作用
+         */
+        //$markdown = str_replace('<br>','<div></div>',$markdown);
+
+        /**
+         * markdown -> html
+         */
+        /*
+
+        $html = MdRender::fixHtml($html);
+        */
+
+        #替换术语
+        $pattern = "/\[\[(.+?)\]\]/";
+        $replacement = '{{term|$1}}';
+        $markdown = preg_replace($pattern, $replacement, $markdown);
+
+        #替换句子模版
+        $pattern = "/\{\{([0-9].+?)\}\}/";
+        $replacement = '{{sent|id=$1}}';
+        $markdown = preg_replace($pattern, $replacement, $markdown);
+
+        /**
+         * 替换多行注释
+         * ```
+         * bla
+         * bla
+         * ```
+         * {{note|
+         * bla
+         * bla
+         * }}
+         */
+        if (strpos($markdown, "```\n") !== false) {
+            $lines = explode("\n", $markdown);
+            $newLines = array();
+            $noteBegin = false;
+            $noteString = array();
+            foreach ($lines as  $line) {
+
+                if ($noteBegin) {
+                    if ($line === "```") {
+                        $newLines[] = "}}";
+                        $noteBegin = false;
+                    } else {
+                        $newLines[] = $line;
+                    }
+                } else {
+                    if ($line === "```") {
+                        $noteBegin = true;
+                        $newLines[] = "{{note|";
+                        continue;
+                    } else {
+                        $newLines[] = $line;
+                    }
+                }
+            }
+            if ($noteBegin) {
+                $newLines[] = "}}";
+            }
+            $markdown = implode("\n", $newLines);
+        }
+
+        /**
+         * 替换单行注释
+         * `bla bla`
+         * {{note|bla}}
+         */
+        $pattern = "/`(.+?)`/";
+        $replacement = '{{note|$1}}';
+        $markdown = preg_replace($pattern, $replacement, $markdown);
+
+        return $markdown;
+    }
+
+    private function markdownToHtml($markdown)
+    {
+        $markdown = str_replace('MdTpl', 'mdtpl', $markdown);
+        $markdown = str_replace(['<param', '</param>'], ['<span', '</span>'], $markdown);
+
+        $html = Markdown::render($markdown);
+        if ($this->options['format'] === 'react') {
+            $html = $this->fixHtml($html);
+        }
+        $html = str_replace('<hr>', '<hr />', $html);
+        //给H1-6 添加uuid
+        for ($i = 1; $i < 7; $i++) {
+            if (strpos($html, "<h{$i}>") === false) {
+                continue;
+            }
+            $output = array();
+            $input = $html;
+            $hPos = strpos($input, "<h{$i}>");
+            while ($hPos !== false) {
+                $output[] = substr($input, 0, $hPos);
+                $output[] = "<h{$i} id='" . Str::uuid() . "'>";
+                $input = substr($input, $hPos + 4);
+                $hPos = strpos($input, "<h{$i}>");
+            }
+            $output[] = $input;
+            $html = implode('', $output);
+        }
+        $html = str_replace('mdtpl', 'MdTpl', $html);
+        return $html;
+    }
+    private function  fixHtml($html)
+    {
+        $doc = new \DOMDocument();
+        libxml_use_internal_errors(true);
+        $html = mb_convert_encoding($html, 'HTML-ENTITIES', "UTF-8");
+        $doc->loadHTML('<span>' . $html . '</span>', LIBXML_NOERROR  | LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
+        $fixed = $doc->saveHTML();
+        $fixed = mb_convert_encoding($fixed, "UTF-8", 'HTML-ENTITIES');
+        return $fixed;
+    }
+    public static function init()
+    {
+        $GLOBALS["MdRenderStack"] = 0;
+    }
+    public function convert($markdown, $channelId = [], $queryId = null)
+    {
+        if (isset($GLOBALS["MdRenderStack"]) && is_numeric($GLOBALS["MdRenderStack"])) {
+            $GLOBALS["MdRenderStack"]++;
+        } else {
+            $GLOBALS["MdRenderStack"] = 1;
+        }
+        if ($GLOBALS["MdRenderStack"] < 3) {
+            $output  =  $this->_convert($markdown, $channelId, $queryId);
+        } else {
+            $output  = $markdown;
+        }
+        $GLOBALS["MdRenderStack"]--;
+        return $output;
+    }
+    private function _convert($markdown, $channelId = [], $queryId = null)
+    {
+        if (empty($markdown)) {
+            switch ($this->options['format']) {
+                case 'react':
+                    return "<span></span>";
+                    break;
+                default:
+                    return "";
+                    break;
+            }
+        }
+        $wiki = $this->markdown2wiki($markdown);
+        $wiki = $this->preprocessingForParagraph($wiki);
+        $markdownWithTpl = $this->wiki2xml($wiki, $channelId);
+        if (!is_null($queryId)) {
+            $html = $this->xmlQueryId($markdownWithTpl, $queryId);
+        }
+        $html = $this->markdownToHtml($markdownWithTpl);
+
+        //后期处理
+        $output = '';
+        switch ($this->options['format']) {
+            case 'react':
+                //生成可展开组件
+                $html = str_replace("<div/>", "<div></div>", $html);
+                $pattern = '/<li><div>(.+?)<\/div><\/li>/';
+                $replacement = '<li><MdTpl name="toggle" tpl="toggle" props=""><div>$1</div></MdTpl></li>';
+                $output = preg_replace($pattern, $replacement, $html);
+                break;
+            case 'text':
+            case 'simple':
+            case 'prompt':
+                $html = strip_tags($html);
+                $output = htmlspecialchars_decode($html, ENT_QUOTES);
+                //$output = html_entity_decode($html);
+                break;
+            case 'tex':
+                $html = strip_tags($html);
+                $output = htmlspecialchars_decode($html, ENT_QUOTES);
+                //$output = html_entity_decode($html);
+                break;
+            case 'unity':
+                $html = str_replace(['<strong>', '</strong>', '<em>', '</em>'], ['[%b%]', '[%/b%]', '[%i%]', '[%/i%]'], $html);
+                $html = strip_tags($html);
+                $html = str_replace(['[%b%]', '[%/b%]', '[%i%]', '[%/i%]'], ['<b>', '</b>', '<i>', '</i>'], $html);
+                $output = htmlspecialchars_decode($html, ENT_QUOTES);
+                break;
+            case 'html':
+                $output = htmlspecialchars_decode($html, ENT_QUOTES);
+                //处理脚注
+                if ($this->options['footnote'] && isset($GLOBALS['note']) && count($GLOBALS['note']) > 0) {
+                    $output .= '<div><h1>endnote</h1>';
+                    foreach ($GLOBALS['note'] as $footnote) {
+                        $output .= '<p><a name="footnote-' . $footnote['sn'] . '">[' . $footnote['sn'] . ']</a> ' . $footnote['content'] . '</p>';
+                    }
+                    $output .= '</div>';
+                    unset($GLOBALS['note']);
+                }
+                //处理图片链接
+                $output = str_replace('<img src="', '<img src="' . config('app.url'), $output);
+                break;
+            case 'markdown':
+                //处理脚注
+                $footnotes = array();
+                if ($this->options['footnote'] && isset($GLOBALS['note']) && count($GLOBALS['note']) > 0) {
+                    foreach ($GLOBALS['note'] as $footnote) {
+                        $footnotes[] = '[^' . $footnote['sn'] . ']: ' . $footnote['content'];
+                    }
+                    unset($GLOBALS['note']);
+                }
+                //处理图片链接
+                $output = str_replace('/attachments/', config('app.url') . "/attachments/", $markdownWithTpl);
+                $output = $output . "\n\n" . implode("\n\n", $footnotes);
+                break;
+        }
+        return $output;
+    }
+
+
+    /**
+     * string[] $channelId
+     */
+    public static function render($markdown, $channelId, $queryId = null, $mode = 'read', $channelType = 'translation', $contentType = "markdown", $format = 'react')
+    {
+
+        $mdRender = new MdRender(
+            [
+                'mode' => $mode,
+                'channelType' => $channelType,
+                'contentType' => $contentType,
+                'format' => $format
+            ]
+        );
+
+        $output  = $mdRender->convert($markdown, $channelId, $queryId);
+
+        return $output;
+    }
+}

+ 206 - 0
api-v12/app/Http/Api/Mq.php

@@ -0,0 +1,206 @@
+<?php
+
+namespace App\Http\Api;
+
+use PhpAmqpLib\Connection\AMQPStreamConnection;
+use PhpAmqpLib\Message\AMQPMessage;
+use PhpAmqpLib\Exchange\AMQPExchangeType;
+use PhpAmqpLib\Exception\AMQPTimeoutException;
+use PhpAmqpLib\Exception\AMQPProtocolChannelException;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Str;
+
+
+class Mq
+{
+
+    private static function connection()
+    {
+        $host = config("queue.connections.rabbitmq.host");
+        $port = config("queue.connections.rabbitmq.port");
+        $user = config("queue.connections.rabbitmq.user");
+        $password = config("queue.connections.rabbitmq.password");
+        $vhost = config("queue.connections.rabbitmq.password");
+        if (empty($host) || empty($port) || empty($user) || empty($password) || empty($vhost)) {
+            Log::error('rabbitmq set error');
+            return;
+        }
+        $connection = new AMQPStreamConnection($host, $port, $user, $password, $vhost);
+        return $connection;
+    }
+
+    public static function publish(string $queue, $message)
+    {
+        //一对一
+        try {
+            Log::debug('mq publish', ['queue' => $queue, 'message' => $message]);
+            $host = config("queue.connections.rabbitmq.host");
+            $port = config("queue.connections.rabbitmq.port");
+            $user = config("queue.connections.rabbitmq.user");
+            $password = config("queue.connections.rabbitmq.password");
+            $vhost = config("queue.connections.rabbitmq.virtual_host");
+            if (empty($host) || empty($port) || empty($user) || empty($password) || empty($vhost)) {
+                Log::error('rabbitmq set error');
+                return;
+            }
+            $connection = new AMQPStreamConnection($host, $port, $user, $password, $vhost);
+            $channel = $connection->channel();
+            $channel->queue_declare($queue, false, true, false, false);
+
+            $msgId = Str::uuid();
+            Log::info("mq push message queue={$queue} id={$msgId}");
+            $msg = new AMQPMessage(
+                json_encode($message, JSON_UNESCAPED_UNICODE),
+                [
+                    "message_id" => $msgId,
+                    "content_type" => 'application/json; charset=utf-8'
+                ]
+            );
+            $channel->basic_publish($msg, '', $queue);
+
+            $channel->close();
+            $connection->close();
+        } catch (\Exception $e) {
+            Log::error($e);
+            return;
+        }
+    }
+
+    /**
+     * @param string $exchange
+     * @param string $queue
+     * @param callable|null $callback
+     */
+    public static function worker($exchange, $queue, $callback = null)
+    {
+
+        $consumerTag = 'consumer';
+
+
+        $host = config("queue.connections.rabbitmq.host");
+        $port = config("queue.connections.rabbitmq.port");
+        $user = config("queue.connections.rabbitmq.user");
+        $password = config("queue.connections.rabbitmq.password");
+        $vhost = config("queue.connections.rabbitmq.virtual_host");
+        $connection = new AMQPStreamConnection($host, $port, $user, $password, $vhost);
+
+        $channel = $connection->channel();
+
+        /*
+     The following code is the same both in the consumer and the producer.
+     In this way we are sure we always have a queue to consume from and an
+         exchange where to publish messages.
+ */
+
+        /*
+     name: $queue
+     passive: false
+     durable: true // the queue will survive server restarts
+     exclusive: false // the queue can be accessed in other channels
+     auto_delete: false //the queue won't be deleted once the channel is closed.
+ */
+        $channel->queue_declare($queue, false, true, false, false);
+
+        /*
+            name: $exchange
+            type: direct
+            passive: false
+            durable: true // the exchange will survive server restarts
+            auto_delete: false //the exchange won't be deleted once the channel is closed.
+        */
+
+        $channel->exchange_declare($exchange, AMQPExchangeType::DIRECT, false, true, false);
+        $channel->queue_bind($queue, $exchange);
+
+        /**
+         * @param \PhpAmqpLib\Message\AMQPMessage $message
+         */
+        $process_message = function (AMQPMessage $message) use ($callback, $queue) {
+            Log::debug('received message', [
+                'message_id' => $message->get('message_id'),
+                'content_type' => $message->get('content_type')
+            ]);
+            if ($callback !== null) {
+                try {
+                    $result = $callback(json_decode($message->getBody()), $message->get('message_id'));
+                    Log::debug(
+                        'mq done',
+                        [
+                            'message_id' => $message->get('message_id')
+                        ]
+                    );
+                    if ($result !== 0) {
+                        throw new \Exception('task error');
+                    }
+                } catch (\Exception $e) {
+                    Log::error("mq worker {$queue} exception", [
+                        'queue' => $queue,
+                        'message_id' => $message->get('message_id'),
+                        'exception' => $e
+                    ]);
+                }
+
+                if (\App\Tools\Tools::isStop()) {
+                    Log::info('mq worker: .stop file exist. cancel the consumer.');
+                    $message->getChannel()->basic_cancel($message->getConsumerTag());
+                }
+            }
+
+
+            //exit
+            foreach (config('mint.mq.loop_limit') as $key => $value) {
+                if ($queue === $key) {
+                    if ($value > 0) {
+                        if (isset($GLOBALS[$key])) {
+                            $GLOBALS[$key]++;
+                        } else {
+                            $GLOBALS[$key] = 1;
+                        }
+                        if ($GLOBALS[$key] >= $value) {
+                            Log::info("mq exit queue={$queue} loop=" . $GLOBALS[$key]);
+                            $message->getChannel()->basic_cancel($message->getConsumerTag());
+                        }
+                    }
+                }
+            }
+            // Send a message with the string "quit" to cancel the consumer.
+            /*
+            if ($message->body === 'quit') {
+                $message->getChannel()->basic_cancel($message->getConsumerTag());
+            }
+            */
+        };
+
+        /*
+            queue: Queue from where to get the messages
+            consumer_tag: Consumer identifier
+            no_local: Don't receive messages published by this consumer.
+            no_ack: If set to true, automatic acknowledgement mode will be used by this consumer. See https://www.rabbitmq.com/confirms.html for details.
+            exclusive: Request exclusive consumer access, meaning only this consumer can access the queue
+            nowait:
+            callback: A PHP Callback
+        */
+        $channel->basic_consume($queue, $consumerTag, false, true, false, false, $process_message);
+
+        /**
+         * @param \PhpAmqpLib\Channel\AMQPChannel $channel
+         * @param \PhpAmqpLib\Connection\AbstractConnection $connection
+         */
+        $shutdown = function ($channel, $connection) {
+            $channel->close();
+            $connection->close();
+        };
+
+        register_shutdown_function($shutdown, $channel, $connection);
+
+        $timeout = 15;
+        // Loop as long as the channel has callbacks registered
+        while ($channel->is_consuming()) {
+            try {
+                $channel->wait(null, false, $timeout);
+            } catch (AMQPTimeoutException $e) {
+                // ignore it
+            }
+        }
+    }
+}

+ 26 - 0
api-v12/app/Http/Api/NotificationApi.php

@@ -0,0 +1,26 @@
+<?php
+namespace App\Http\Api;
+
+use App\Models\Notification;
+use Illuminate\Support\Str;
+
+class NotificationApi{
+    public static function send($data){
+        $insertData = [];
+        foreach ($data as $key => $row) {
+            $insertData[] = [
+                'id' => Str::uuid(),
+                'from' => $row['from'],
+                'to' => $row['to'],
+                'url' => $row['url'],
+                'content' => $row['content'],
+                'res_type' => $row['res_type'],
+                'res_id' => $row['res_id'],
+                'updated_at' => now(),
+                'created_at' => now(),
+            ];
+        }
+        $insert = Notification::insert($insertData);
+        return $insert;
+    }
+}

+ 25 - 0
api-v12/app/Http/Api/PaliTextApi.php

@@ -0,0 +1,25 @@
+<?php
+namespace App\Http\Api;
+
+use App\Models\PaliText;
+
+class PaliTextApi{
+    public static function getChapterStartEnd($book,$para){
+        $chapter = PaliText::where('book',$book)
+                        ->where('paragraph',$para)
+                        ->first();
+        if(!$chapter){
+            return false;
+        }
+        $start = $para;
+        $end = $para + $chapter->chapter_len -1;
+        return [$start,$end];
+    }
+
+    public static function getChapterPath($book,$para){
+        $path = PaliText::where('book',$book)
+                        ->where('paragraph',$para)
+                        ->value('path');
+        return $path;
+    }
+}

+ 55 - 0
api-v12/app/Http/Api/ProjectApi.php

@@ -0,0 +1,55 @@
+<?php
+
+namespace App\Http\Api;
+
+use App\Models\Project;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\App;
+
+class ProjectApi
+{
+    public static function getById($id)
+    {
+        if (!$id) {
+            return null;
+        };
+        $project = Project::find($id);
+        if ($project) {
+            return [
+                'id' => $id,
+                'sn' => $project->id,
+                'title' => $project->title,
+                'type' => $project->type,
+                'weight' => $project->weight,
+                'description' => $project->description,
+            ];
+        } else {
+            return null;
+        }
+    }
+
+    public static function getListByIds($ids)
+    {
+        if (!$ids) {
+            return null;
+        };
+        $projects = Project::whereIn('uid', $ids)->get();
+        $output = array();
+        foreach ($ids as $key => $id) {
+            foreach ($projects as $project) {
+                if ($project->uid === $id) {
+                    $output[] = [
+                        'id' => $id,
+                        'sn' => $project->id,
+                        'title' => $project->title,
+                        'type' => $project->type,
+                        'weight' => $project->weight,
+                        'description' => $project->description,
+                    ];
+                    continue;
+                };
+            }
+        }
+        return $output;
+    }
+}

+ 90 - 0
api-v12/app/Http/Api/SentenceApi.php

@@ -0,0 +1,90 @@
+<?php
+
+namespace App\Http\Api;
+
+use Illuminate\Support\Str;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Redis;
+
+use App\Models\Sentence;
+use App\Models\SentHistory;
+use App\Models\Channel;
+
+use App\Http\Api\ShareApi;
+
+class SentenceApi{
+    protected $auth = false;
+    protected $channel = null;
+
+    public function auth($channelId,$userId){
+        $channel = Channel::where('uid',$channelId)->first();
+        if(!$channel){
+            return false;
+        }
+        if($channel->owner_uid !== $userId){
+            //判断是否为协作
+            $power = ShareApi::getResPower($userId,$channel->uid,2);
+            if($power < 20){
+                return false;
+            }
+        }
+        $this->channel = $channel;
+        $this->auth = true;
+        return true;
+    }
+    public function store($sent,$user,$copy=false){
+        $row = Sentence::firstOrNew([
+            "book_id"=>$sent['book_id'],
+            "paragraph"=>$sent['paragraph'],
+            "word_start"=>$sent['word_start'],
+            "word_end"=>$sent['word_end'],
+            "channel_uid"=>$this->channel->uid,
+        ],[
+            "id"=>app('snowflake')->id(),
+            "uid"=>Str::uuid(),
+        ]);
+        $row->content = $sent['content'];
+        $row->strlen = mb_strlen($sent['content'],"UTF-8");
+        $row->language = $this->channel->lang;
+        $row->status = $this->channel->status;
+        if($copy){
+            //复制句子,保留原作者信息
+            $row->editor_uid = $sent["editor_uid"];
+            $row->acceptor_uid = $user["user_uid"];
+            $row->pr_edit_at = $sent["updated_at"];
+        }else{
+            $row->editor_uid = $user["user_uid"];
+            $row->acceptor_uid = null;
+            $row->pr_edit_at = null;
+        }
+        $row->create_time = time()*1000;
+        $row->modify_time = time()*1000;
+        $row->save();
+
+        //保存历史记录
+        if($copy){
+            $this->saveHistory($row->uid,$sent["editor_uid"],$sent['content']);
+        }else{
+            $this->saveHistory($row->uid,$user["user_uid"],$sent['content']);
+        }
+        //清除缓存
+        $sentId = "{$sent['book_id']}-{$sent['paragraph']}-{$sent['word_start']}-{$sent['word_end']}";
+        $hKey = "/sentence/res-count/{$sentId}/";
+        Redis::del($hKey);
+    }
+
+    private function saveHistory($uid,$editor,$content){
+        $newHis = new SentHistory;
+        $newHis->id = app('snowflake')->id();
+        $newHis->sent_uid = $uid;
+        $newHis->user_uid = $editor;
+        if(empty($content)){
+            $newHis->content = "";
+        }else{
+            $newHis->content = $content;
+        }
+
+        $newHis->create_time = time()*1000;
+        $newHis->save();
+    }
+}

+ 192 - 0
api-v12/app/Http/Api/ShareApi.php

@@ -0,0 +1,192 @@
+<?php
+
+namespace App\Http\Api;
+
+use App\Models\GroupMember;
+use App\Models\Share;
+use App\Models\Article;
+use App\Models\Channel;
+use App\Models\Collection;
+use App\Models\Project;
+
+use App\Http\Api\ChannelApi;
+
+class ShareApi
+{
+
+    /**
+     * 获取某用户的可见的协作资源
+     * $res_type 见readme.md#资源类型 -1全部类型资源
+     * ## 资源类型
+     *  1 PCS 文档
+     *  2 Channel 版本
+     *  3 Article 文章
+     *  4 Collection 文集
+     *  5 版本片段
+     * power 权限 10: 只读  20:编辑 30: 拥有者
+     */
+
+    public static function getResList($user_uid, $res_type = -1)
+    {
+        # 找我加入的群
+        $my_group = GroupMember::where("user_id", $user_uid)->select('group_id')->get();
+        $userList[] = $user_uid;
+        foreach ($my_group as $key => $value) {
+            # code...
+            $userList[] = $value["group_id"];
+        }
+
+        if ($res_type == -1) {
+            #所有类型资源
+            $Fetch = Share::whereIn("cooperator_id", $userList)->select(['res_id', 'res_type', 'power'])->get();
+        } else {
+            #指定类型资源
+            $Fetch = Share::whereIn("cooperator_id", $userList)
+                ->where('res_type', $res_type)
+                ->select(['res_id', 'res_type', 'power'])->get();
+        }
+
+        $resOutput = array();
+        foreach ($Fetch as $key => $value) {
+            # 查重
+            if (isset($resOutput[$value["res_id"]])) {
+                if ($value["power"] > $resOutput[$value["res_id"]]["power"]) {
+                    $resOutput[$value["res_id"]]["power"] = $value["power"];
+                }
+            } else {
+                $resOutput[$value["res_id"]] = array("power" => $value["power"], "type" => $value["res_type"]);
+            }
+        }
+        $resList = array();
+        foreach ($resOutput as $key => $value) {
+            # code...
+            $resList[] = array("res_id" => $key, "res_type" => (int)$value["type"], "power" => (int)$value["power"]);
+        }
+
+        foreach ($resList as $key => $res) {
+            # 获取资源标题 和所有者
+            $resList[$key]["res_title"] = "_unknown_";
+            $resList[$key]["res_owner_id"] = "_unknown_";
+            $resList[$key]["type"] = "_unknown_";
+            $resList[$key]["status"] = "0";
+            $resList[$key]["lang"] = "_unknown_";
+
+            switch ($res["res_type"]) {
+                case 1:
+                    # pcs 文档
+                    $resList[$key]["res_title"] = "title";
+                    break;
+                case 2:
+                    # channel
+                    $channelInfo = Channel::where('uid', $res["res_id"])->first();
+                    if ($channelInfo) {
+                        $resList[$key]["res_title"] = $channelInfo["name"];
+                        $resList[$key]["res_owner_id"] = $channelInfo["owner_uid"];
+                        $resList[$key]["type"] = $channelInfo["type"];
+                        $resList[$key]["status"] = $channelInfo["status"];
+                        $resList[$key]["lang"] = $channelInfo["lang"];
+                    }
+                    break;
+                case 3:
+                    # 3 Article 文章
+                    $aInfo = Article::where('uid', $res["res_id"])->first();
+                    if ($aInfo) {
+                        $resList[$key]["res_title"] = $aInfo["title"];
+                        $resList[$key]["res_owner_id"] = $aInfo["owner"];
+                        $resList[$key]["status"] = $aInfo["status"];
+                        $resList[$key]["lang"] = '';
+                    }
+                    break;
+                case 4:
+                    # 4 Collection 文集
+                    $aInfo = Collection::where('uid', $res["res_id"])->first();
+                    if ($aInfo) {
+                        $resList[$key]["res_title"] = $aInfo["title"];
+                        $resList[$key]["res_owner_id"] = $aInfo["owner"];
+                        $resList[$key]["status"] = $aInfo["status"];
+                        $resList[$key]["lang"] = $aInfo["lang"];
+                    }
+                    break;
+                case 5:
+                    # code...
+                    break;
+                case 6:
+                    $aInfo = Project::where('uid', $res["res_id"])->first();
+                    if ($aInfo) {
+                        $resList[$key]["res_title"] = $aInfo["title"];
+                        $resList[$key]["res_owner_id"] = $aInfo["owner_id"];
+                        $resList[$key]["status"] = $aInfo["status"];
+                        $resList[$key]["lang"] = '';
+                    }
+                    break;
+                default:
+                    # code...
+                    break;
+            }
+        }
+
+        return $resList;
+    }
+
+    /**
+     * 获取对某个共享资源的权限
+     */
+    public static function getResPower($user_uid, $res_id, $res_type = 0)
+    {
+        if (empty($user_uid)) {
+            #未登录用户 没有共享资源
+            return 0;
+        }
+        //查看是否为资源拥有者
+        if ($res_type != 0) {
+            switch ($res_type) {
+                case 2:
+                    # channel
+                    $channel = ChannelApi::getById($res_id);
+                    if ($channel) {
+                        if ($channel['studio_id'] === $user_uid) {
+                            return 30;
+                        }
+                    }
+                    break;
+                case 3:
+                    //Article
+                    $owner = Article::where('uid', $res_id)->value('owner');
+                    if ($owner === $user_uid) {
+                        return 30;
+                    }
+                    break;
+                case 4:
+                    $owner = Collection::where('uid', $res_id)->value('owner');
+                    if ($owner === $user_uid) {
+                        return 30;
+                    }
+                    //文集
+                    break;
+                case 6: //workflow
+                    $owner = Project::where('uid', $res_id)->value('owner_id');
+                    if ($owner === $user_uid) {
+                        return 30;
+                    }
+                    break;
+            }
+        }
+        # 找我加入的群
+        $my_group = GroupMember::where("user_id", $user_uid)->select('group_id')->get();
+        $userList[] = $user_uid;
+        foreach ($my_group as $key => $value) {
+            $userList[] = $value["group_id"];
+        }
+        $Fetch = Share::whereIn("cooperator_id", $userList)
+            ->where('res_id', $res_id)
+            ->select(['power'])->get();
+        $power = 0;
+        foreach ($Fetch as $key => $value) {
+            # code...
+            if ((int)$value["power"] > $power) {
+                $power = $value["power"];
+            }
+        }
+        return $power;
+    }
+}

+ 123 - 0
api-v12/app/Http/Api/StudioApi.php

@@ -0,0 +1,123 @@
+<?php
+
+namespace App\Http\Api;
+
+use App\Models\UserInfo;
+use App\Models\GroupInfo;
+use App\Models\GroupMember;
+
+use Illuminate\Support\Facades\Storage;
+use Illuminate\Support\Facades\App;
+
+class StudioApi
+{
+    public static function getIdByName($name)
+    {
+        /**
+         * 获取 uuid
+         */
+        //TODO 改为studio table
+        if (empty($name)) {
+            return false;
+        }
+        $userInfo = UserInfo::where('username', $name)->first();
+        if ($userInfo) {
+            //group
+            return $userInfo->userid;
+        } else {
+            $group = GroupInfo::where('uid', $name)->first();
+            if ($group) {
+                return $group->uid;
+            } else {
+                return false;
+            }
+        }
+    }
+    public static function getById($id)
+    {
+        //TODO 改为studio table
+        if (empty($id)) {
+            return false;
+        }
+        $userInfo = UserInfo::where('userid', $id)->first();
+        if ($userInfo) {
+            $data = [
+                'id' => $id,
+                'nickName' => $userInfo->nickname,
+                'realName' => $userInfo->username,
+                'studioName' => $userInfo->username,
+            ];
+            if (!empty($userInfo->role)) {
+                $data['roles'] = json_decode($userInfo->role);
+            }
+            if ($userInfo->avatar) {
+                $img = str_replace('.jpg', '_s.jpg', $userInfo->avatar);
+
+                if (App::environment(['local', 'testing'])) {
+                    $data['avatar'] = Storage::url($img);
+                } else {
+                    $data['avatar'] = Storage::temporaryUrl($img, now()->addDays(6));
+                }
+            }
+            return $data;
+        } else {
+            $group = GroupInfo::where('uid', $id)->first();
+            if ($group) {
+                $data = [
+                    'id' => $id,
+                    'nickName' => $group->name,
+                    'realName' => $group->uid,
+                    'studioName' => $group->uid,
+                ];
+            } else {
+                return false;
+            }
+        }
+    }
+
+    public static function getByIntId($id)
+    {
+        //TODO 改为studio table
+        if (empty($id)) {
+            return false;
+        }
+        $userInfo = UserInfo::where('id', $id)->first();
+        if (!$userInfo) {
+            return false;
+        }
+        return [
+            'id' => $userInfo['userid'],
+            'nickName' => $userInfo['nickname'],
+            'realName' => $userInfo['username'],
+            'studioName' => $userInfo['username'],
+            'avatar' => '',
+        ];
+    }
+
+    public static function userCanManage($userId, $studioId)
+    {
+        if ($userId === $studioId) {
+            return true;
+        }
+        $group = GroupMember::where('user_id', $userId)
+            ->where('group_id', $studioId)
+            ->first();
+        if ($group && $group->power <= 1) {
+            return true;
+        }
+        return false;
+    }
+    public static function userCanList($userId, $studioId)
+    {
+        if ($userId === $studioId) {
+            return true;
+        }
+        $group = GroupMember::where('user_id', $userId)
+            ->where('group_id', $studioId)
+            ->first();
+        if ($group && $group->power <= 2) {
+            return true;
+        }
+        return false;
+    }
+}

+ 31 - 0
api-v12/app/Http/Api/SuggestionApi.php

@@ -0,0 +1,31 @@
+<?php
+namespace App\Http\Api;
+
+use App\Models\SentPr;
+use App\Models\Discussion;
+use App\Models\Sentence;
+use App\Http\Api\PaliTextApi;
+
+class SuggestionApi{
+    public static function getCountBySent($book,$para,$start,$end,$channel,$type="suggestion"){
+        $count['suggestion'] = SentPr::where('book_id',$book)
+                                    ->where('paragraph',$para)
+                                    ->where('word_start',$start)
+                                    ->where('word_end',$end)
+                                    ->where('channel_uid',$channel)
+                                    ->count();
+        $sentId = Sentence::where('book_id',$book)
+                            ->where('paragraph',$para)
+                            ->where('word_start',$start)
+                            ->where('word_end',$end)
+                            ->where('channel_uid',$channel)
+                            ->value('uid');
+        if($sentId){
+            $count['discussion'] = Discussion::where('res_id',$sentId)
+                                            ->whereNull('parent')
+                                            ->count();
+        }
+
+        return $count;
+    }
+}

+ 139 - 0
api-v12/app/Http/Api/TaskApi.php

@@ -0,0 +1,139 @@
+<?php
+
+namespace App\Http\Api;
+
+use App\Models\Task;
+use App\Models\TaskRelation;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\App;
+use App\Tools\RedisClusters;
+use Illuminate\Support\Str;
+
+class TaskApi
+{
+    public static function getById($id)
+    {
+        if (!$id) {
+            return null;
+        };
+        $task = Task::where('id', $id)->first();
+        if ($task) {
+            return [
+                'id' => $id,
+                'title' => $task->title,
+                'description' => $task->description,
+            ];
+        } else {
+            return null;
+        }
+    }
+
+    public static function getListByIds($ids)
+    {
+        if (!$ids) {
+            return null;
+        };
+        $tasks = Task::whereIn('id', $ids)->get();
+        $output = array();
+        foreach ($ids as $key => $id) {
+            foreach ($tasks as $task) {
+                if ($task->id === $id) {
+                    $output[] = [
+                        'id' => $id,
+                        'title' => $task->title,
+                        'description' => $task->description,
+                        "executor" => UserApi::getByUuid($task->executor_id)
+                    ];
+                    continue;
+                };
+            }
+        }
+        return $output;
+    }
+
+    public static function setRelationTasks($taskId, $relationTasksId, $editor_id, $relation = 'pre')
+    {
+        if ($relation === 'pre') {
+            $where = 'next_task_id';
+            $task1 = 'task_id';
+            $task2 = 'next_task_id';
+        } else {
+            $where = 'task_id';
+            $task1 = 'next_task_id';
+            $task2 = 'task_id';
+        }
+        TaskApi::removeTaskRelationRedisKey($taskId, $relation);
+        $delete = TaskRelation::where($where, $taskId)
+            ->delete();
+        foreach ($relationTasksId as $key => $id) {
+            if (Str::isUuid($taskId) && Str::isUuid($id)) {
+                $data[] = [
+                    $task1 => $id,
+                    $task2 => $taskId,
+                    'editor_id' => $editor_id,
+                    'created_at' => now(),
+                    'updated_at' => now(),
+                ];
+            }
+        }
+        if (isset($data)) {
+            TaskRelation::insert($data);
+        }
+        TaskApi::removeTaskRelationRedisKey($taskId, $relation);
+    }
+    public static function getRelationTasks($taskId, $relation = 'pre')
+    {
+        $key = TaskApi::taskRelationRedisKey($taskId, $relation);
+        //Log::debug('task redis key=' . $key . ' has=' . RedisClusters::has($key));
+        $data = RedisClusters::remember($key,  24 * 3600, function () use ($taskId, $relation) {
+            Log::debug('getRelationTasks task=' . $taskId . ' relation=' . $relation);
+            if ($relation === 'pre') {
+                $where = 'next_task_id';
+                $select = 'task_id';
+            } else {
+                $where = 'task_id';
+                $select = 'next_task_id';
+            }
+            $tasks = TaskRelation::where($where, $taskId)
+                ->select($select)->get();
+            $tasksId = [];
+            foreach ($tasks as $key => $task) {
+                $tasksId[] = $task[$select];
+            }
+            return TaskApi::getListByIds($tasksId);
+        });
+        return $data;
+    }
+
+    public static function getNextTasks($taskId)
+    {
+        return TaskApi::getRelationTasks($taskId, 'next');
+    }
+    public static function getPreTasks($taskId)
+    {
+        return TaskApi::getRelationTasks($taskId, 'pre');
+    }
+    public static function removeTaskRelationRedisKey($taskId, $relation = 'pre')
+    {
+        //查询相关task
+        $relations = TaskRelation::where('task_id', $taskId)
+            ->orWhere('next_task_id', $taskId)
+            ->select('task_id', 'next_task_id')->get();
+        $relationsId = [];
+        $relationsId[$taskId] = 1;
+        foreach ($relations as $key => $value) {
+            $relationsId[$value->task_id] = 1;
+            $relationsId[$value->next_task_id] = 1;
+        }
+        foreach ($relationsId as $taskId => $value) {
+            $key = TaskApi::taskRelationRedisKey($taskId, 'pre');
+            RedisClusters::forget($key);
+            $key = TaskApi::taskRelationRedisKey($taskId, 'next');
+            RedisClusters::forget($key);
+        }
+    }
+    public static function taskRelationRedisKey($taskId, $relation = 'pre')
+    {
+        return "task/relation/{$relation}/{$taskId}";
+    }
+}

+ 1440 - 0
api-v12/app/Http/Api/TemplateRender.php

@@ -0,0 +1,1440 @@
+<?php
+
+namespace App\Http\Api;
+
+use Illuminate\Support\Facades\Cache;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Str;
+use Illuminate\Support\Facades\Http;
+
+use App\Models\DhammaTerm;
+use App\Models\PaliText;
+use App\Models\Channel;
+use App\Models\PageNumber;
+use App\Models\Discussion;
+use App\Models\BookTitle as BookSeries;
+
+use App\Http\Controllers\CorpusController;
+
+use App\Tools\RedisClusters;
+use App\Http\Api\ChannelApi;
+use App\Http\Api\MdRender;
+use App\Http\Api\PaliTextApi;
+
+use App\Tools\Tools;
+
+class TemplateRender
+{
+    protected $param = [];
+    protected $mode = "read";
+    protected $channel_id = [];
+    protected $debug = [];
+    protected $format = 'react';
+    protected $studioId = null;
+    protected $lang = 'en';
+    protected $langFamily = 'en';
+    protected $glossaryKey = 'glossary';
+    protected $channelInfo = [];
+
+    protected $options = [
+        'mode' => 'read',
+        'channelType' => 'translation',
+        'contentType' => "markdown",
+        'format' => 'react',
+        'debug' => [],
+        'studioId' => null,
+        'lang' => 'zh-Hans',
+        'footnote' => false,
+        'paragraph' => false,
+        'origin' => true,
+        'translation' => true,
+    ];
+
+    /**
+     * Create a new command instance.
+     * string $mode  'read' | 'edit'
+     * string $format  'react' | 'text' | 'tex' | 'unity'
+     * @return void
+     */
+    public function __construct($param, $channelInfo, $mode, $format = 'react', $studioId = '', $debug = [], $lang = 'zh-Hans')
+    {
+        $this->param = $param;
+        foreach ($channelInfo as $value) {
+            $this->channel_id[] = $value->uid;
+        }
+        $this->channelInfo = $channelInfo;
+        $this->mode = $mode;
+        $this->format = $format;
+        $this->studioId = $studioId;
+        $this->debug = $debug;
+        $this->glossaryKey = 'glossary';
+
+        if (count($this->channel_id) > 0) {
+            $channelId = $this->channel_id[0];
+            if (Str::isUuid($channelId)) {
+                $lang = Channel::where('uid', $channelId)->value('lang');
+            }
+        }
+        if (!empty($lang)) {
+            $this->lang = $lang;
+            $this->langFamily = explode('-', $lang)[0];
+        }
+    }
+    public function options($options = [])
+    {
+        foreach ($options as $key => $value) {
+            $this->options[$key] = $value;
+        }
+    }
+    public function glossaryKey()
+    {
+        return $this->glossaryKey;
+    }
+    /**
+     * TODO 设置默认语言。在渲染某些内容的时候需要语言信息
+     */
+    public function setLang($lang)
+    {
+        $this->lang = $lang;
+        $this->langFamily = explode('-', $lang)[0];
+    }
+    private function info($message, $debug)
+    {
+        if (in_array($debug, $this->debug)) {
+            Log::info($message);
+        }
+    }
+    private function error($message, $debug)
+    {
+        if (in_array($debug, $this->debug)) {
+            Log::error($message);
+        }
+    }
+    public function render($tpl_name)
+    {
+        switch ($tpl_name) {
+            case 'term':
+                # 术语
+                $result = $this->render_term();
+                break;
+            case 'note':
+                $result = $this->render_note();
+                break;
+            case 'sent':
+                $result = $this->render_sent();
+                break;
+            case 'quote':
+                $result = $this->render_quote();
+                break;
+            case 'ql':
+                $result = $this->render_quote_link();
+                break;
+            case 'exercise':
+                $result = $this->render_exercise();
+                break;
+            case 'article':
+                $result = $this->render_article();
+                break;
+            case 'nissaya':
+                $result = $this->render_nissaya();
+                break;
+            case 'mermaid':
+                $result = $this->render_mermaid();
+                break;
+            case 'qa':
+                $result = $this->render_qa();
+                break;
+            case 'v':
+                $result = $this->render_video();
+                break;
+            case 'g':
+                $result = $this->render_grammar_lookup();
+                break;
+            case 'ref':
+                $result = $this->render_ref();
+                break;
+            case 'dict-pref':
+                $result = $this->render_dict_pref();
+                break;
+            case 'ai':
+                $result = $this->render_ai();
+                break;
+            default:
+                # code...
+                $result = [
+                    'props' => base64_encode(\json_encode([])),
+                    'html' => '',
+                    'tag' => 'span',
+                    'tpl' => 'unknown',
+                ];
+                break;
+        }
+        return $result;
+    }
+
+    public function getTermProps($word, $tag = null, $channel = null)
+    {
+        if ($channel && !empty($channel)) {
+            $channelId = $channel;
+        } else {
+            if (count($this->channel_id) > 0) {
+                $channelId = $this->channel_id[0];
+            } else {
+                $channelId = null;
+            }
+        }
+
+        if (count($this->channelInfo) === 0) {
+            if (!empty($channel)) {
+                $channelInfo = Channel::where('uid', $channel)->first();
+                if (!$channelInfo) {
+                    unset($channelInfo);
+                }
+            }
+            if (!isset($channelInfo)) {
+                Log::warning('channel is null');
+                $output = [
+                    "word" => $word,
+                    'innerHtml' => '',
+                ];
+                return $output;
+            }
+        } else {
+            $channelInfo = $this->channelInfo[0];
+        }
+
+        if (Str::isUuid($channelId)) {
+            $lang = Channel::where('uid', $channelId)->value('lang');
+            if (!empty($lang)) {
+                $langFamily = explode('-', $lang)[0];
+            } else {
+                $langFamily = 'zh';
+            }
+            $this->info("term:{$word} 先查属于这个channel 的", 'term');
+            $this->info('channel id' . $channelId, 'term');
+            $table = DhammaTerm::where("word", $word)
+                ->where('channal', $channelId);
+            if ($tag && !empty($tag)) {
+                $table = $table->where('tag', $tag);
+            }
+            $tplParam = $table->orderBy('updated_at', 'desc')
+                ->first();
+            $studioId = $channelInfo->owner_uid;
+        } else {
+            $tplParam = false;
+            $lang = '';
+            $langFamily = '';
+            $studioId = $this->studioId;
+        }
+
+        if (!$tplParam) {
+            if (Str::isUuid($studioId)) {
+                /**
+                 * 没有,再查这个studio的
+                 * 按照语言过滤
+                 * 完全匹配的优先
+                 * 语族匹配也行
+                 */
+                $this->info("没有-再查这个studio的", 'term');
+                $table = DhammaTerm::where("word", $word);
+                if (!empty($tag)) {
+                    $table = $table->where('tag', $tag);
+                }
+                $termsInStudio = $table->where('owner', $channelInfo->owner_uid)
+                    ->orderBy('updated_at', 'desc')
+                    ->get();
+                if (count($termsInStudio) > 0) {
+                    $list = array();
+                    foreach ($termsInStudio as $key => $term) {
+                        if (empty($term->channal)) {
+                            if ($term->language === $lang) {
+                                $list[$term->guid] = 2;
+                            } else if (strpos($term->language, $langFamily) !== false) {
+                                $list[$term->guid] = 1;
+                            }
+                        }
+                    }
+                    if (count($list) > 0) {
+                        arsort($list);
+                        foreach ($list as $key => $one) {
+                            foreach ($termsInStudio as $term) {
+                                if ($term->guid === $key) {
+                                    $tplParam = $term;
+                                    break;
+                                }
+                            }
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (!$tplParam) {
+            $this->info("没有,再查社区", 'term');
+            $community_channel = ChannelApi::getSysChannel("_community_term_zh-hans_");
+            $table = DhammaTerm::where("word", $word);
+            if (!empty($tag)) {
+                $table = $table->where('tag', $tag);
+            }
+            $tplParam = $table->where('channal', $community_channel)
+                ->first();
+            if ($tplParam) {
+                $isCommunity = true;
+            } else {
+                $this->info("查社区没有", 'term');
+            }
+        }
+        $output = [
+            "word" => $word,
+            "parentChannelId" => $channelId,
+            "parentStudioId" => $channelInfo->owner_uid,
+        ];
+        $innerString = $output["word"];
+        if ($tplParam) {
+            $output["id"] = $tplParam->guid;
+            $output["meaning"] = $tplParam->meaning;
+            $output["channel"] = $tplParam->channal;
+            if (!empty($tplParam->note)) {
+                $mdRender = new MdRender(['format' => $this->format]);
+                $output['note'] = $mdRender->convert($tplParam->note, $this->channel_id);
+            }
+            if (isset($isCommunity)) {
+                $output["isCommunity"] = true;
+            }
+            $innerString = "{$output["meaning"]}({$output["word"]})";
+            if (!empty($tplParam->other_meaning)) {
+                $output["meaning2"] = $tplParam->other_meaning;
+            }
+        }
+        $output['innerHtml'] = $innerString;
+        return $output;
+    }
+
+    private function render_term()
+    {
+        $word = $this->get_param($this->param, "word", 1);
+
+        $props = $this->getTermProps($word, '');
+
+        $output = $props['word'];
+        switch ($this->format) {
+            case 'react':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'html' => $props['innerHtml'],
+                    'tag' => 'span',
+                    'tpl' => 'term',
+                ];
+                break;
+            case 'unity':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'tpl' => 'term',
+                ];
+                break;
+            case 'html':
+                if (isset($props["meaning"])) {
+                    $GLOBALS[$this->glossaryKey][$props["word"]] = $props['meaning'];
+
+                    $key = 'term-' . $props["word"];
+                    $termHead = "<a href='#'>" . $props['meaning'] . "</a>";
+
+                    if (isset($GLOBALS[$key])) {
+                        $output = $termHead;
+                    } else {
+                        $GLOBALS[$key] = 1;
+                        $output = $termHead . '(<em>' . $props["word"] . '</em>)';
+                    }
+                } else {
+                    $output = $props["word"];
+                }
+                break;
+            case 'text':
+                if (isset($props["meaning"])) {
+                    $key = 'term-' . $props["word"];
+                    if (isset($GLOBALS[$key])) {
+                        $output = $props["meaning"];
+                    } else {
+                        $GLOBALS[$key] = 1;
+                        $output = $props["meaning"] . '(' . $props["word"] . ')';
+                    }
+                } else {
+                    $output = $props["word"];
+                }
+                break;
+            case 'tex':
+                if (isset($props["meaning"])) {
+                    $key = 'term-' . $props["word"];
+                    if (isset($GLOBALS[$key])) {
+                        $output = $props["meaning"];
+                    } else {
+                        $GLOBALS[$key] = 1;
+                        $output = $props["meaning"] . '(' . $props["word"] . ')';
+                    }
+                } else {
+                    $output = $props["word"];
+                }
+                break;
+            case 'simple':
+                if (isset($props["meaning"])) {
+                    $output = $props["meaning"];
+                } else {
+                    $output = $props["word"];
+                }
+                break;
+            case 'markdown':
+                if (isset($props["meaning"])) {
+                    $key = 'term-' . $props["word"];
+                    if (isset($GLOBALS[$key]) && $GLOBALS[$key] === 1) {
+                        $GLOBALS[$key]++;
+                        $output = $props["meaning"];
+                    } else {
+                        $GLOBALS[$key] = 1;
+                        $output = $props["meaning"] . '(' . $props["word"] . ')';
+                    }
+                } else {
+                    $output = $props["word"];
+                }
+                //如果有内容且第一次出现,显示为脚注
+                if (!empty($props["note"]) && $GLOBALS[$key] === 1) {
+                    if (isset($GLOBALS['note_sn'])) {
+                        $GLOBALS['note_sn']++;
+                    } else {
+                        $GLOBALS['note_sn'] = 1;
+                        $GLOBALS['note'] = array();
+                    }
+                    $content = $props["note"];
+                    $output .= '[^' . $GLOBALS['note_sn'] . ']';
+                    $GLOBALS['note'][] = [
+                        'sn' => $GLOBALS['note_sn'],
+                        'trigger' => '',
+                        'content' => $content,
+                    ];
+                }
+                break;
+            default:
+                if (isset($props["meaning"])) {
+                    $output = $props["meaning"];
+                } else {
+                    $output = $props["word"];
+                }
+                break;
+        }
+        return $output;
+    }
+
+    private  function render_note()
+    {
+        $note = $this->get_param($this->param, "text", 1);
+        $trigger = $this->get_param($this->param, "trigger", 2);
+        $props = ["note" => $note];
+        $innerString = "";
+        if (!empty($trigger)) {
+            $props["trigger"] = $trigger;
+            $innerString = $props["trigger"];
+        }
+        if ($this->format === 'unity') {
+            $props["note"] = MdRender::render(
+                $props["note"],
+                $this->channel_id,
+                null,
+                'read',
+                'translation',
+                'markdown',
+                'unity'
+            );
+        }
+        $output = $note;
+        switch ($this->format) {
+            case 'react':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'html' => $innerString,
+                    'tag' => 'span',
+                    'tpl' => 'note',
+                ];
+                break;
+            case 'unity':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'tpl' => 'note',
+                ];
+                break;
+            case 'html':
+                if (isset($GLOBALS['note_sn'])) {
+                    $GLOBALS['note_sn']++;
+                } else {
+                    $GLOBALS['note_sn'] = 1;
+                    $GLOBALS['note'] = array();
+                }
+                $GLOBALS['note'][] = [
+                    'sn' => $GLOBALS['note_sn'],
+                    'trigger' => $trigger,
+                    'content' => MdRender::render(
+                        $props["note"],
+                        $this->channel_id,
+                        null,
+                        'read',
+                        'translation',
+                        'markdown',
+                        'html'
+                    ),
+                ];
+
+                $link = "<a href='#footnote-" . $GLOBALS['note_sn'] . "' name='note-" . $GLOBALS['note_sn'] . "'>";
+                if (empty($trigger)) {
+                    $output =  $link . "<sup>[" . $GLOBALS['note_sn'] . "]</sup></a>";
+                } else {
+                    $output = $link . $trigger . "</a>";
+                }
+                break;
+            case 'text':
+                $output = $trigger;
+                break;
+            case 'tex':
+                $output = $trigger;
+                break;
+            case 'simple':
+                $output = '';
+                break;
+            case 'markdown':
+                if (isset($GLOBALS['note_sn'])) {
+                    $GLOBALS['note_sn']++;
+                } else {
+                    $GLOBALS['note_sn'] = 1;
+                    $GLOBALS['note'] = array();
+                }
+                $content = MdRender::render(
+                    $props["note"],
+                    $this->channel_id,
+                    null,
+                    'read',
+                    'translation',
+                    'markdown',
+                    'markdown'
+                );
+                $output = '[^' . $GLOBALS['note_sn'] . ']';
+                $GLOBALS['note'][] = [
+                    'sn' => $GLOBALS['note_sn'],
+                    'trigger' => $trigger,
+                    'content' => $content,
+                ];
+                //$output = '<footnote id="'.$GLOBALS['note_sn'].'">'.$content.'</footnote>';
+                break;
+            default:
+                $output = '';
+                break;
+        }
+        return $output;
+    }
+    private  function render_nissaya()
+    {
+        $pali =  $this->get_param($this->param, "pali", 1);
+        $meaning = $this->get_param($this->param, "meaning", 2);
+        $innerString = "";
+        $props = [
+            "pali" => $pali,
+            "meaning" => explode('=', $meaning),
+            "lang" => $this->lang,
+        ];
+        switch ($this->format) {
+            case 'react':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'html' => $innerString,
+                    'tag' => 'span',
+                    'tpl' => 'nissaya',
+                ];
+                break;
+            case 'unity':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'tpl' => 'nissaya',
+                ];
+                break;
+            case 'prompt':
+                $output = Tools::MyToRm($pali) . ':' . end($props["meaning"]);
+                break;
+            default:
+                $output = $pali . '၊' . $meaning;
+                break;
+        }
+        return $output;
+    }
+    private  function render_exercise()
+    {
+
+        $id = $this->get_param($this->param, "id", 1);
+        $title = $this->get_param($this->param, "title", 1);
+        $props = [
+            "id" => $id,
+            "title" => $title,
+            "channel" => $this->channel_id[0],
+        ];
+        switch ($this->format) {
+            case 'react':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'html' => "",
+                    'tag' => 'span',
+                    'tpl' => 'exercise',
+                ];
+                break;
+            case 'unity':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'tpl' => 'exercise',
+                ];
+                break;
+            case 'text':
+                $output = $title;
+                break;
+            case 'tex':
+                $output = $title;
+                break;
+            case 'simple':
+                $output = $title;
+                break;
+            default:
+                $output = '';
+                break;
+        }
+        return $output;
+    }
+    private  function render_article()
+    {
+        $type = $this->get_param($this->param, "type", 1);
+        $id = $this->get_param($this->param, "id", 2);
+        $title = $this->get_param($this->param, "title", 3);
+        $channel = $this->get_param($this->param, "channel", 4);
+        $style = $this->get_param($this->param, "style", 5);
+        $book = $this->get_param($this->param, "book", 6);
+        $paragraphs = $this->get_param($this->param, "paragraphs", 7);
+        $anthology = $this->get_param($this->param, "anthology", 8);
+        if ($type === 'chapter' && empty($id)) {
+            $book = (int)$book;
+            $paragraphs = (int)$paragraphs;
+            $id = "{$book}-{$paragraphs}";
+        }
+        $props = [
+            "type" => $type,
+            "id" => $id,
+            'style' => $style,
+        ];
+        if (!empty($channel)) {
+            $props['channel'] = $channel;
+        }
+        if (!empty($title)) {
+            $props['title'] = $title;
+        }
+        if (!empty($book)) {
+            $props['book'] = $book;
+        }
+        if (!empty($paragraphs)) {
+            $props['paragraphs'] = $paragraphs;
+        }
+        if (!empty($anthology)) {
+            $props['anthology'] = $anthology;
+        }
+        if (is_array($this->channel_id)) {
+            $props['parentChannels'] = $this->channel_id;
+        }
+        switch ($this->format) {
+            case 'react':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'html' => "",
+                    'text' => $title,
+                    'tag' => 'span',
+                    'tpl' => 'article',
+                ];
+                break;
+            case 'unity':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'tpl' => 'article',
+                ];
+                break;
+            case 'text':
+                $output = $title;
+                break;
+            case 'tex':
+                $output = $title;
+                break;
+            case 'simple':
+                $output = $title;
+                break;
+            default:
+                $output = '';
+                break;
+        }
+        return $output;
+    }
+    private  function render_quote()
+    {
+        $paraId = $this->get_param($this->param, "para", 1);
+        $channelId = $this->channel_id[0];
+        $props = RedisClusters::remember(
+            "/quote/{$channelId}/{$paraId}",
+            config('mint.cache.expire'),
+            function () use ($paraId, $channelId) {
+                $para = \explode('-', $paraId);
+                $output = [
+                    "paraId" => $paraId,
+                    "channel" => $channelId,
+                    "innerString" => $paraId,
+                ];
+                if (count($para) < 2) {
+                    return $output;
+                }
+                $PaliText = PaliText::where("book", $para[0])
+                    ->where("paragraph", $para[1])
+                    ->select(['toc', 'path'])
+                    ->first();
+
+                if ($PaliText) {
+                    $output["pali"] = $PaliText->toc;
+                    $output["paliPath"] = \json_decode($PaliText->path);
+                    $output["innerString"] = $PaliText->toc;
+                }
+                return $output;
+            }
+        );
+
+        switch ($this->format) {
+            case 'react':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'html' => $props["innerString"],
+                    'tag' => 'span',
+                    'tpl' => 'quote',
+                ];
+                break;
+            case 'unity':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'tpl' => 'quote',
+                ];
+                break;
+            case 'text':
+                $output = $props["innerString"];
+                break;
+            case 'tex':
+                $output = $props["innerString"];
+                break;
+            case 'simple':
+                $output = $props["innerString"];
+                break;
+            default:
+                $output = $props["innerString"];
+                break;
+        }
+        return $output;
+    }
+
+    private  function render_quote_link()
+    {
+        $type = $this->get_param($this->param, "type", 1);
+        $title = $this->get_param($this->param, "title", 6, '');
+        $bookName = $this->get_param($this->param, "bookname", 2, '');
+        $volume = $this->get_param($this->param, "volume", 3);
+        $page = $this->get_param($this->param, "page", 4, '');
+        $style = $this->get_param($this->param, "style", 5, 'modal');
+        $book = $this->get_param($this->param, "book", 7, false);
+        $para = $this->get_param($this->param, "para", 8, false);
+
+        $props = [
+            'type' => $type,
+            'style' => $style,
+            'found' => true,
+        ];
+
+        if (!empty($bookName) && $volume !== '' && !empty($page)) {
+            $props['bookName'] = $bookName;
+            $props['volume'] = (int)$volume;
+            $props['page'] = $page;
+            $props['found'] = true;
+        } else if ($book && $para) {
+            /**
+             * 没有指定书名,根据book para 查询
+             */
+            if ($type === 'c') {
+                //按照章节名称显示
+                $path = PaliTextApi::getChapterPath($book, $para);
+                if ($path) {
+                    $path = json_decode($path, true);
+                }
+                if ($path && is_array($path) && count($path) > 2) {
+                    $props['bookName'] = strtolower($path[0]['title']);
+                    $props['chapter'] = strtolower(end($path)['title']);
+                    $props['found'] = true;
+                } else {
+                    $props['found'] = false;
+                }
+            } else {
+                $pageInfo = $this->pageInfoByPara($type, $book, $para);
+                if ($pageInfo['found']) {
+                    $props['bookName'] = $pageInfo['bookName'];
+                    $props['volume'] = $pageInfo['volume'];
+                    $props['page'] = $pageInfo['page'];
+                    $props['found'] = true;
+                } else {
+                    $props['found'] = false;
+                }
+            }
+        } else if ($title) {
+            //没有书号用title查询
+            //$tmpTitle = explode('။',$title);
+            for ($i = mb_strlen($title, 'UTF-8'); $i > 0; $i--) {
+                $mTitle = mb_substr($title, 0, $i);
+                $has = array_search($mTitle, array_column(BookTitle::my(), 'title2'));
+                Log::debug('run', ['title' => $mTitle, 'has' => $has]);
+                if ($has !== false) {
+                    $tmpBookTitle = $mTitle;
+                    $tmpBookPage = mb_substr($title, $i);
+                    $tmpBookPage = $this->mb_trim($tmpBookPage, '၊။');
+                    break;
+                }
+            }
+
+            if (isset($tmpBookTitle)) {
+                Log::debug('book title found', ['title' => $tmpBookTitle, 'page' => $tmpBookPage]);
+                //$tmpBookTitle = $tmpTitle[0];
+                //$tmpBookPage = $tmpTitle[1];
+                $tmpBookPage = (int)str_replace(
+                    ['၁', '၂', '၃', '၄', '၅', '၆', '၇', '၈', '၉', '၀'],
+                    ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0'],
+                    $tmpBookPage
+                );
+                $found_key = array_search($tmpBookTitle, array_column(BookTitle::my(), 'title2'));
+                if ($found_key !== false) {
+                    $props['bookName'] = BookTitle::my()[$found_key]['bookname'];
+                    $props['volume'] = BookTitle::my()[$found_key]['volume'];
+                    $props['page'] = $tmpBookPage;
+                    if (!empty($props['bookName'])) {
+                        $found_title = array_search($props['bookName'], array_column(BookTitle::my(), 'bookname'));
+                        if ($found_title === false) {
+                            $props['found'] = false;
+                        }
+                    }
+                } else {
+                    //没找到,返回术语和页码
+                    $props['found'] = false;
+                    $props['bookName'] = $tmpBookTitle;
+                    $props['page'] = $tmpBookPage;
+                    $props['volume'] = 0;
+                }
+            }
+        } else {
+            Log::debug('book title not found');
+            $props['found'] = false;
+        }
+
+        if ($book && $para) {
+            $props['book'] = $book;
+            $props['para'] = $para;
+        }
+        if ($title) {
+            $props['title'] = $title;
+        }
+
+        $text = '';
+        if (isset($props['bookName'])) {
+            $searchField = '';
+            switch ($type) {
+                case 'm':
+                    $searchField = 'm_title';
+                    break;
+                case 'p':
+                    $searchField = 'p_title';
+                    break;
+            }
+            $found_title = array_search($props['bookName'], array_column(BookTitle::get(), $searchField));
+            if ($found_title === false) {
+                $props['found'] = false;
+            }
+
+            $term = $this->getTermProps($props['bookName'], ':quote:');
+            $props['term'] = $term;
+            if (isset($term['id'])) {
+                $props['bookNameLocal'] = $term['meaning'];
+                $text .= $term['meaning'];
+            } else {
+                $text .= $bookName;
+            }
+        }
+
+        if (isset($props['volume']) && isset($props['page'])) {
+            $text .= " {$volume}.{$page}";
+        }
+
+
+        switch ($this->format) {
+            case 'react':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'html' => '',
+                    'tag' => 'span',
+                    'tpl' => 'quote-link',
+                ];
+                break;
+            case 'unity':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'tpl' => 'quote-link',
+                ];
+                break;
+            default:
+                $output = $text;
+                break;
+        }
+        return $output;
+    }
+
+    private function pageInfoByPara($type, $book, $para)
+    {
+        $output = array();
+        $pageInfo = PageNumber::where('type', strtoupper($type))
+            ->where('book', $book)
+            ->where('paragraph', '<=', $para)
+            ->orderBy('paragraph', 'desc')
+            ->first();
+        if ($pageInfo) {
+            foreach (BookTitle::get() as $value) {
+                if ($value['id'] === $pageInfo->pcd_book_id) {
+                    switch (strtoupper($type)) {
+                        case 'M':
+                            $key = 'm_title';
+                            break;
+                        case 'P':
+                            $key = 'p_title';
+                            break;
+                        case 'V':
+                            $key = 'v_title';
+                            break;
+                        default:
+                            $key = 'term';
+                            break;
+                    }
+                    $output['bookName'] = $value[$key];
+                    break;
+                }
+            }
+            $output['volume'] = $pageInfo->volume;
+            $output['page'] = $pageInfo->page;
+            $output['found'] = true;
+        } else {
+            $output['found'] = false;
+        }
+        return $output;
+    }
+    private  function render_sent()
+    {
+
+        $sid = $this->get_param($this->param, "id", 1);
+        $channel = $this->get_param($this->param, "channel", 2);
+        $text = $this->get_param($this->param, "text", 2, 'both');
+
+        if (!empty($channel)) {
+            $channels = explode(',', $channel);
+        } else {
+            $channels = $this->channel_id;
+        }
+        $sentInfo = explode('@', trim($sid));
+        $sentId = $sentInfo[0];
+        if (isset($sentInfo[1])) {
+            $channels = [$sentInfo[1]];
+        }
+        $Sent = new CorpusController();
+        $props = $Sent->getSentTpl(
+            $sentId,
+            $channels,
+            $this->mode,
+            true,
+            $this->format
+        );
+        if ($props === false) {
+            $props['error'] = "句子模版渲染错误。句子参数个数不符。应该是四个。";
+        }
+        if ($this->mode === 'read') {
+            $tpl = "sentread";
+        } else {
+            $tpl = "sentedit";
+        }
+
+        //输出引用
+        $arrSid = explode('-', $sid);
+        $bookPara = array_slice($arrSid, 0, 2);
+        if (!isset($GLOBALS['ref_sent'])) {
+            $GLOBALS['ref_sent'] = array();
+        }
+        $GLOBALS['ref_sent'][] = $bookPara;
+
+        switch ($this->format) {
+            case 'react':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'html' => "",
+                    'tag' => 'span',
+                    'tpl' => $tpl,
+                ];
+                break;
+            case 'unity':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'tpl' => $tpl,
+                ];
+                break;
+            case 'text':
+                $output = '';
+                if (isset($props['origin']) && is_array($props['origin'])) {
+                    foreach ($props['origin'] as $key => $value) {
+                        $output .= $value['html'];
+                    }
+                }
+                if (isset($props['translation']) && is_array($props['translation'])) {
+                    foreach ($props['translation'] as $key => $value) {
+                        $output .= $value['html'];
+                    }
+                }
+                break;
+            case 'prompt':
+                $output = '';
+                if ($text === 'both' || $text === 'origin') {
+                    if (isset($props['origin']) && is_array($props['origin'])) {
+                        foreach ($props['origin'] as $key => $value) {
+                            $output .= $value['html'];
+                        }
+                    }
+                }
+                if ($text === 'both' || $text === 'translation') {
+                    if (isset($props['translation']) && is_array($props['translation'])) {
+                        foreach ($props['translation'] as $key => $value) {
+                            $output .= $value['html'];
+                        }
+                    }
+                }
+                break;
+            case 'html':
+                $output = '';
+                $output .= '<span class="sentence">';
+                if ($text === 'both' || $text === 'origin') {
+                    if (isset($props['origin']) && is_array($props['origin'])) {
+                        foreach ($props['origin'] as $key => $value) {
+                            $output .= '<span class="origin">' . $value['html'] . '</span>';
+                        }
+                    }
+                }
+                if ($text === 'both' || $text === 'translation') {
+                    if (isset($props['translation']) && is_array($props['translation'])) {
+                        foreach ($props['translation'] as $key => $value) {
+                            $output .= '<span class="translation">' . $value['html'] . '</span>';
+                        }
+                    }
+                }
+
+                $output .= '</span>';
+                break;
+            case 'tex':
+                $output = '';
+                if (isset($props['translation']) && is_array($props['translation'])) {
+                    foreach ($props['translation'] as $key => $value) {
+                        $output .= $value['html'];
+                    }
+                }
+                break;
+            case 'simple':
+                $output = '';
+                if ($text === 'both' || $text === 'origin') {
+                    if (empty($output)) {
+                        if (
+                            isset($props['origin']) &&
+                            is_array($props['origin']) &&
+                            count($props['origin']) > 0
+                        ) {
+                            foreach ($props['origin'] as $key => $value) {
+                                $output .= trim($value['html']);
+                            }
+                        }
+                    }
+                }
+                if ($text === 'both' || $text === 'translation') {
+                    if (
+                        isset($props['translation']) &&
+                        is_array($props['translation']) &&
+                        count($props['translation']) > 0
+                    ) {
+                        foreach ($props['translation'] as $key => $value) {
+                            $output .= trim($value['html']);
+                        }
+                    }
+                }
+                break;
+            case 'markdown':
+                $output = '';
+                if ($text === 'both' || $text === 'origin') {
+                    if (
+                        $this->options['origin'] === true ||
+                        $this->options['origin'] === 'true'
+                    ) {
+                        if (isset($props['origin']) && is_array($props['origin'])) {
+                            foreach ($props['origin'] as $key => $value) {
+                                $output .= trim($value['html']);
+                            }
+                        }
+                    }
+                }
+                if ($text === 'both' || $text === 'translation') {
+                    if (
+                        $this->options['translation']  === true ||
+                        $this->options['translation']  === 'true'
+                    ) {
+                        if (
+                            isset($props['translation']) &&
+                            is_array($props['translation']) &&
+                            count($props['translation']) > 0
+                        ) {
+                            foreach ($props['translation'] as $key => $value) {
+                                $output .= trim($value['html']);
+                            }
+                        } else {
+                            if ($text === 'translation') {
+                                //无译文用原文代替
+                                if (isset($props['origin']) && is_array($props['origin'])) {
+                                    foreach ($props['origin'] as $key => $value) {
+                                        $output .= trim($value['html']);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                break;
+            default:
+                $output = '';
+                break;
+        }
+        return $output;
+    }
+
+    private  function render_mermaid()
+    {
+        $text = json_decode(base64_decode($this->get_param($this->param, "text", 1)));
+
+        $props = ["text" => implode("\n", $text)];
+
+        switch ($this->format) {
+            case 'react':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'html' => "mermaid",
+                    'tag' => 'div',
+                    'tpl' => 'mermaid',
+                ];
+                break;
+            case 'unity':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'tpl' => 'mermaid',
+                ];
+                break;
+            case 'text':
+                $output = 'mermaid';
+                break;
+            case 'tex':
+                $output = 'mermaid';
+                break;
+            case 'simple':
+                $output = 'mermaid';
+                break;
+            default:
+                $output = 'mermaid';
+                break;
+        }
+        return $output;
+    }
+
+    private  function render_qa()
+    {
+
+        $id = $this->get_param($this->param, "id", 1);
+        $style = $this->get_param($this->param, "style", 2);
+
+        $props = [
+            "type" => 'qa',
+            "id" => $id,
+            'title' => '',
+            'style' => $style,
+        ];
+        $qa = Discussion::where('id', $id)->first();
+        if ($qa) {
+            $props['title'] = $qa->title;
+            $props['resId'] = $qa->res_id;
+            $props['resType'] = $qa->res_type;
+        }
+
+        switch ($this->format) {
+            case 'react':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'html' => "",
+                    'text' => $props['title'],
+                    'tag' => 'div',
+                    'tpl' => 'qa',
+                ];
+                break;
+            case 'unity':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'tpl' => 'qa',
+                ];
+                break;
+            default:
+                $output = $props['title'];
+                break;
+        }
+        return $output;
+    }
+
+    private function render_grammar_lookup()
+    {
+        $word = $this->get_param($this->param, "word", 1);
+        $props = ['word' => $word];
+
+        $localTermChannel = ChannelApi::getSysChannel(
+            "_System_Grammar_Term_" . strtolower($this->lang) . "_",
+            "_System_Grammar_Term_en_"
+        );
+        $term = $this->getTermProps($word, null, $localTermChannel);
+        $props['term'] = $term;
+        switch ($this->format) {
+            case 'react':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'html' => "",
+                    'text' => $props['word'],
+                    'tag' => 'span',
+                    'tpl' => 'grammar',
+                ];
+                break;
+            case 'unity':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'tpl' => 'grammar',
+                ];
+                break;
+            default:
+                $output = $props['word'];
+                break;
+        }
+        return $output;
+    }
+
+    private  function render_video()
+    {
+
+        $url = $this->get_param($this->param, "url", 1);
+        $style = $this->get_param($this->param, "style", 2, 'modal');
+        $title = $this->get_param($this->param, "title", 3);
+
+        $props = [
+            "url" => $url,
+            'title' => $title,
+            'style' => $style,
+        ];
+
+        switch ($this->format) {
+            case 'react':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'html' => "",
+                    'text' => $props['title'],
+                    'tag' => 'span',
+                    'tpl' => 'video',
+                ];
+                break;
+            case 'unity':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'tpl' => 'video',
+                ];
+                break;
+            default:
+                $output = $props['title'];
+                break;
+        }
+        return $output;
+    }
+
+    //论文后面的参考资料
+    private  function render_ref()
+    {
+        $references = array();
+        $counter = 0;
+
+        if (isset($GLOBALS['ref_sent'])) {
+            $hasBooks = array();
+            $book_titles = BookSeries::select(['book', 'paragraph', 'title', 'sn'])
+                ->orderBy('sn', 'DESC')->get();
+            $bTitles = array();
+            foreach ($book_titles as $key => $book) {
+                $bTitles[] = [
+                    'book' => $book->book,
+                    'paragraph' => $book->paragraph,
+                    'title' => $book->title
+                ];
+            }
+            foreach ($GLOBALS['ref_sent'] as $key => $ref) {
+                $books = array_filter($bTitles, function ($value) use ($ref) {
+                    return $value['book'] === (int)$ref[0];
+                });
+                if (count($books) > 0) {
+                    foreach ($books as $key => $book) {
+                        if ($book['paragraph'] < (int)$ref[1]) {
+                            if (!isset($hasBooks[$book['title']])) {
+                                $hasBooks[$book['title']] = 1;
+                                $counter++;
+                                $references[] = [
+                                    'sn' => $counter,
+                                    'title' => $book['title'],
+                                    'copyright' => 'CSCD V4 VRI 2008'
+                                ];
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        $props = [
+            "pali" => $references,
+        ];
+
+        switch ($this->format) {
+            case 'react':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'html' => '',
+                    'tag' => 'div',
+                    'tpl' => 'reference',
+                ];
+                break;
+            case 'unity':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'tpl' => 'reference',
+                ];
+                break;
+            case 'markdown':
+                $output = '';
+                foreach ($references as $key => $reference) {
+                    $output .= '[' . $reference['sn'] . '] **' . ucfirst($reference['title']) . '** ';
+                    $output .= $reference['copyright'] . "\n\n";
+                }
+                break;
+            default:
+                $output = '';
+                foreach ($references as $key => $reference) {
+                    $output .= '[' . $reference['sn'] . '] ' . ucfirst($reference['title']) . ' ';
+                    $output .= $reference['copyright'] . "\n";
+                }
+                break;
+        }
+        return $output;
+    }
+    private function render_dict_pref()
+    {
+        $currPage = $this->get_param($this->param, "page", 1, 1);
+        $pageSize = $this->get_param($this->param, "size", 2, 100);
+
+        $props = [
+            "currPage" => $currPage,
+            'pageSize' => $pageSize,
+        ];
+
+        switch ($this->format) {
+            case 'react':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'html' => "",
+                    'text' => '',
+                    'tag' => 'div',
+                    'tpl' => 'dict-pref',
+                ];
+                break;
+            case 'unity':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'tpl' => 'dict-pref',
+                ];
+                break;
+            default:
+                $output = 'dict-pref';
+                break;
+        }
+        return $output;
+    }
+
+    private function render_ai()
+    {
+        $model = $this->get_param($this->param, "model", 1, 1);
+
+        $props = [
+            "model" => $model,
+        ];
+
+        switch ($this->format) {
+            case 'react':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'html' => "",
+                    'text' => '',
+                    'tag' => 'div',
+                    'tpl' => 'ai',
+                ];
+                break;
+            case 'unity':
+                $output = [
+                    'props' => base64_encode(\json_encode($props)),
+                    'tpl' => 'ai',
+                ];
+                break;
+            case 'text':
+                $output = 'ai';
+                break;
+            case 'prompt':
+                $output = '';
+                break;
+            default:
+                $output = 'ai';
+                break;
+        }
+        return $output;
+    }
+    private  function get_param(array $param, string $name, int $id, string $default = '')
+    {
+        if (isset($param[$name])) {
+            return trim($param[$name]);
+        } else if (isset($param["{$id}"])) {
+            return trim($param["{$id}"]);
+        } else {
+            return $default;
+        }
+    }
+
+    private function mb_trim($str, string $character_mask = ' ', $charset = "UTF-8")
+    {
+        $start = 0;
+        $end = mb_strlen($str, $charset) - 1;
+        $chars = preg_split('//u', $character_mask, -1, PREG_SPLIT_NO_EMPTY);
+        while ($start <= $end && in_array(mb_substr($str, $start, 1, $charset), $chars)) {
+            $start++;
+        }
+
+        while ($end >= $start && in_array(mb_substr($str, $end, 1, $charset), $chars)) {
+            $end--;
+        }
+
+        return mb_substr($str, $start, $end - $start + 1, $charset);
+    }
+}

+ 101 - 0
api-v12/app/Http/Api/UserApi.php

@@ -0,0 +1,101 @@
+<?php
+
+namespace App\Http\Api;
+
+use App\Http\Resources\AiAssistant;
+use App\Models\AiModel;
+use App\Models\UserInfo;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Facades\Storage;
+use Illuminate\Support\Facades\App;
+
+class UserApi
+{
+    public static function getIdByName($name)
+    {
+        return UserInfo::where('username', $name)->value('userid');
+    }
+    public static function getIdByUuid($uuid)
+    {
+        return UserInfo::where('userid', $uuid)->value('id');
+    }
+    public static function getIntIdByName($name)
+    {
+        return UserInfo::where('username', $name)->value('id');
+    }
+    public static function getById($id)
+    {
+        $user = UserInfo::where('id', $id)->first();
+        return UserApi::userInfo($user);
+    }
+
+    public static function getByName($name)
+    {
+        $user = UserInfo::where('username', $name)->first();
+        return UserApi::userInfo($user);
+    }
+    public static function getByUuid($id)
+    {
+        $user = UserInfo::where('userid', $id)->first();
+        if (!$user) {
+            return AiAssistantApi::getByUuid($id);
+        }
+        return UserApi::userInfo($user);
+    }
+    public static function getListByUuid($uuid)
+    {
+        if (!$uuid || !is_array($uuid)) {
+            return null;
+        };
+        $users = UserInfo::whereIn('userid', $uuid)->get();
+        $assistants = AiModel::whereIn('uid', $uuid)->get();
+        $output = array();
+        foreach ($uuid as $key => $id) {
+            foreach ($users as $user) {
+                if ($user->userid === $id) {
+                    $output[] = UserApi::userInfo($user);
+                    continue;
+                };
+            }
+            foreach ($assistants as $assistant) {
+                if ($assistant->uid === $id) {
+                    $output[] = AiAssistantApi::userInfo($assistant);
+                    continue;
+                };
+            }
+        }
+        return $output;
+    }
+    public static function userInfo($user)
+    {
+        if (!$user) {
+            Log::warning('$user=null;');
+            return [
+                'id' => 0,
+                'nickName' => 'unknown',
+                'userName' => 'unknown',
+                'realName' => 'unknown',
+                'avatar' => '',
+            ];
+        }
+        $data = [
+            'id' => $user->userid,
+            'nickName' => $user->nickname,
+            'userName' => $user->username,
+            'realName' => $user->username,
+            'sn' => $user->id,
+        ];
+        if (!empty($user->role)) {
+            $data['roles'] = json_decode($user->role);
+        }
+        if ($user->avatar) {
+            $img = str_replace('.jpg', '_s.jpg', $user->avatar);
+            if (App::environment(['local', 'testing'])) {
+                $data['avatar'] = Storage::url($img);
+            } else {
+                $data['avatar'] = Storage::temporaryUrl($img, now()->addDays(6));
+            }
+        }
+        return $data;
+    }
+}

+ 39 - 0
api-v12/app/Http/Api/WatchApi.php

@@ -0,0 +1,39 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Http\Api;
+
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Str;
+use App\Models\Like;
+use App\Models\Notification;
+
+class WatchApi
+{
+    public static function change(array $resId, string $from, string $message)
+    {
+        //发送站内信
+        $watches = Like::where('type', 'watch')
+            ->whereIn('target_id', $resId)
+            ->get();
+        $notifications = [];
+        foreach ($watches as  $watch) {
+            $notifications[] = [
+                'id' => Str::uuid(),
+                'from' => $from,
+                'to' => $watch->user_id,
+                'url' => '',
+                'content' => $message,
+                'res_type' => $watch->target_type,
+                'res_id' => $watch->target_id,
+                'channel' => Str::uuid(),
+                'created_at' => now(),
+                'updated_at' => now(),
+            ];
+        }
+        Log::debug('notification insert', ['data' => $notifications]);
+        $new = Notification::insert($notifications);
+        return $new;
+    }
+}

+ 125 - 0
api-v12/app/Http/Api/quote-my.csv

@@ -0,0 +1,125 @@
+title1,title2,bookname,volume
+abhi,dha,အဘိ၊ဓ,dhammasaṅgaṇīpāḷi,0
+abhi,ka,အဘိ၊က,kathāvatthupāḷi,0
+abhi,pu,အဘိ၊ပု,puggalapaññattipāḷi,0
+abhi,ṭṭha,1,အဘိ၊ဋ္ဌ၊၁,dhammasaṅgaṇī-aṭṭhakathā,0
+abhi,ṭṭha,2,အဘိ၊ဋ္ဌ၊၂,vibhaṅga-aṭṭhakathā,0
+abhi,ṭṭha,3,အဘိ၊ဋ္ဌ၊၃,pañcapakaraṇa-aṭṭhakathā,0
+abhi,vi,အဘိ၊ဝိ,vibhaṅgapāḷi,0
+aṃ,1,အံ၊၁,aṅguttaranikāya,1
+aṃ,2,အံ၊၂,aṅguttaranikāya,2
+aṃ,3,အံ၊၃,aṅguttaranikāya,3
+aṃ,ṭī,1,အံ၊ဋီ၊၁,aṅguttaranikāya-ṭīkā,1
+aṃ,ṭī,2,အံ၊ဋီ၊၂,aṅguttaranikāya-ṭīkā,2
+aṃ,ṭī,3,အံ၊ဋီ၊၃,aṅguttaranikāya-ṭīkā,3
+aṃ,ṭṭha,1,အံ၊ဋ္ဌ၊၁,aṅguttaranikāya-aṭṭhakathā,1
+aṃ,ṭṭha,2,အံ၊ဋ္ဌ၊၂,aṅguttaranikāya-aṭṭhakathā,2
+aṃ,ṭṭha,3,အံ၊ဋ္ဌ၊၃,aṅguttaranikāya-aṭṭhakathā,3
+anuṭī,2,အနုဋီ၊၂,vibhaṅga-anuṭīkā,0
+anuṭī,3,အနုဋီ၊၃,pañcapakaraṇa-anuṭīkā,0
+apa,1,အပ၊၁,therāpadānapāḷi,1
+apa,2,အပ၊၂,therāpadānapāḷi,2
+apa,ṭṭha,1,အပ၊ဋ္ဌ၊၁,apadāna-aṭṭhakathā,1
+apa,ṭṭha,2,အပ၊ဋ္ဌ၊၂,apadāna-aṭṭhakathā,2
+buddhavaṃ,ṭṭha,ဗုဒ္ဓဝံ၊ဋ္ဌ,buddhavaṃsa-aṭṭhakathā,0
+cariyā,ṭṭha,စရိယာ၊ဋ္ဌ,cariyāpiṭaka-aṭṭhakathā,0
+cūḷani,စူဠနိ,cūḷaniddesapāḷi,0
+cūḷani,ṭṭha,စူဠနိ၊ဋ္ဌ,cūḷaniddesa-aṭṭhakathā,0
+dhamma,ဓမ္မ,dhammapadapāḷi,0
+dhamma,ṭṭha,1,ဓမ္မ၊ဋ္ဌ၊၁,dhammapada-aṭṭhakathā,1
+dhamma,ṭṭha,2,ဓမ္မ၊ဋ္ဌ၊၂,dhammapada-aṭṭhakathā,2
+dī,1,ဒီ၊၁,dīghanikāya,1
+dī,2,ဒီ၊၂,dīghanikāya,2
+dī,3,ဒီ၊၃,dīghanikāya,3
+dī,ṭī,1,ဒီ၊ဋီ၊၁,dīghanikāya-ṭīkā,1
+dī,ṭī,2,ဒီ၊ဋီ၊၂,dīghanikāya-ṭīkā,2
+dī,ṭī,3,ဒီ၊ဋီ၊၃,dīghanikāya-ṭīkā,3
+dī,ṭṭha,1,ဒီ၊ဋ္ဌ၊၁,dīghanikāya-aṭṭhakathā,1
+dī,ṭṭha,2,ဒီ၊ဋ္ဌ၊၂,dīghanikāya-aṭṭhakathā,2
+dī,ṭṭha,3,ဒီ၊ဋ္ဌ၊၃,dīghanikāya-aṭṭhakathā,3
+itivuta,ṭṭha,ဣတိဝုတ၊ဋ္ဌ,itivuttaka-aṭṭhakathā,0
+jā,1,ဇာ၊၁,jātakapāḷi,1
+jā,2,ဇာ၊၂,jātakapāḷi,2
+jā,ṭṭha,1,ဇာ၊ဋ္ဌ၊၁,jātaka-aṭṭhakathā,1
+jā,ṭṭha,2,ဇာ၊ဋ္ဌ၊၂,jātaka-aṭṭhakathā,2
+jā,ṭṭha,3,ဇာ၊ဋ္ဌ၊၃,jātaka-aṭṭhakathā,3
+jā,ṭṭha,4,ဇာ၊ဋ္ဌ၊၄,jātaka-aṭṭhakathā,4
+jā,ṭṭha,5,ဇာ၊ဋ္ဌ၊၅,jātaka-aṭṭhakathā,5
+jā,ṭṭha,6,ဇာ၊ဋ္ဌ၊၆,jātaka-aṭṭhakathā,6
+jā,ṭṭha,7,ဇာ၊ဋ္ဌ၊၇,jātaka-aṭṭhakathā,7
+jātaka,1,ဇာတက၊၁,jātakapāḷi,1
+jātaka,2,ဇာတက၊၂,jātakapāḷi,2
+khuddaka,ခုဒ္ဒက,khuddakapāṭhapāḷi,0
+khuddaka,ṭṭha,ခုဒ္ဒက၊ဋ္ဌ,khuddakapāṭha-aṭṭhakathā,0
+ma,1,မ၊၁,majjhimanikaya,1
+ma,2,မ၊၂,majjhimanikaya,2
+ma,3,မ၊၃,majjhimanikaya,3
+ma,ṭī,1,မ၊ဋီ၊၁,majjhimanikaya-ṭīkā,1
+ma,ṭī,2,မ၊ဋီ၊၂,majjhimanikaya-ṭīkā,2
+ma,ṭī,3,မ၊ဋီ၊၃,majjhimanikaya-ṭīkā,3
+ma,ṭṭha,1,မ၊ဋ္ဌ၊၁,majimanikaya-aṭṭhakathā,1
+ma,ṭṭha,2,မ၊ဋ္ဌ၊၂,majimanikaya-aṭṭhakathā,2
+ma,ṭṭha,3,မ၊ဋ္ဌ၊၃,majimanikaya-aṭṭhakathā,3
+ma,ṭṭha,4,မ၊ဋ္ဌ၊၄,majimanikaya-aṭṭhakathā,4
+mahāni,မဟာနိ,mahāniddesapāḷi,0
+mahāni,ṭṭha,မဟာနိ၊ဋ္ဌ,mahāniddesa-aṭṭhakathā,0
+milinda,မိလိန္ဒ,milindapañhapāḷi,0
+mūlaṭī,1,မူလဋီ၊၁,dhammasaṅgaṇī-mūlaṭīkā,1
+mūlaṭī,2,မူလဋီ၊၂,vibhaṅga-mūlaṭīkā,2
+netti,နေတ္တိ,nettippakaraṇapāḷi,0
+netti,ṭṭha,နေတ္တိ၊ဋ္ဌ,nettippakaraṇa-aṭṭhakathā,0
+netti,vibhā,နေတ္တိ၊ဝိဘာ,nettivibhāvinī,0
+paṭisaṃ,ပဋိသံ,paṭisambhidāmaggapāḷi,0
+paṭisaṃ,ṭṭha,1,ပဋိသံ၊ဋ္ဌ၊၁,paṭisambhidāmagga-aṭṭhakathā,1
+paṭisaṃ,ṭṭha,2,ပဋိသံ၊ဋ္ဌ၊၂,paṭisambhidāmagga-aṭṭhakathā,2
+paṭisambhidāmaga,pāḷi,ပဋိသမ္ဘိဒါမဂ်၊ပါဠိ,paṭisambhidāmaggapāḷi,0
+paṭṭhāna,1,ပဋ္ဌာန၊၁,paṭṭhānapāḷi,1
+paṭṭhāna,2,ပဋ္ဌာန၊၂,paṭṭhānapāḷi,2
+paṭṭhāna,3,ပဋ္ဌာန၊၃,paṭṭhānapāḷi,3
+paṭṭhāna,4,ပဋ္ဌာန၊၄,paṭṭhānapāḷi,4
+peta,ṭṭha,ပေတ၊ဋ္ဌ,petavatthupāḷi,0
+peṭako,ပေဋကော,peṭakopadesapāḷi,0
+saṃ,1,သံ၊၁,saṃyuttanikāya,1
+saṃ,2,သံ၊၂,saṃyuttanikāya,2
+saṃ,3,သံ၊၃,saṃyuttanikāya,3
+saṃ,ṭī,1,သံ၊ဋီ၊၁,saṃyuttanikāya-ṭīkā,1
+saṃ,ṭī,2,သံ၊ဋီ၊၂,saṃyuttanikāya-ṭīkā,2
+saṃ,ṭṭha,1,သံ၊ဋ္ဌ၊၁,saṃyuttanikāya-aṭṭhakathā,1
+saṃ,ṭṭha,2,သံ၊ဋ္ဌ၊၂,saṃyuttanikāya-aṭṭhakathā,2
+saṃ,ṭṭha,3,သံ၊ဋ္ဌ၊၃,saṃyuttanikāya-aṭṭhakathā,3
+sārattha,ṭī,1,သာရတ္ထ၊ဋီ၊၁,sāratthadīpanī-ṭīkā,1
+sārattha,ṭī,2,သာရတ္ထ၊ဋီ၊၂,sāratthadīpanī-ṭīkā,2
+sārattha,ṭī,3,သာရတ္ထ၊ဋီ၊၃,sāratthadīpanī-ṭīkā,3
+suttani,ṭṭha,1,သုတ္တနိ၊ဋ္ဌ၊၁,suttanipāta-aṭṭhakathā,1
+suttani,ṭṭha,2,သုတ္တနိ၊ဋ္ဌ၊၂,suttanipāta-aṭṭhakathā,2
+suttaniṭṭha,2,သုတ္တနိဋ္ဌ၊၂,suttanipāta-aṭṭhakathā,2
+thera,ṭṭha,1,ထေရ၊ဋ္ဌ၊၁,theragāthā-aṭṭhakathā,1
+thera,ṭṭha,2,ထေရ၊ဋ္ဌ၊၂,theragāthā-aṭṭhakathā,2
+theragāthā,ထေရဂါထာ,theragāthāpāḷi,0
+therī,ထေရီ,therīgāthāpāḷi,0
+therī,ṭṭha,ထေရီ၊ဋ္ဌ,therīgāthā-aṭṭhakathā,0
+udāna,ဥဒါန,udānapāḷi,0
+udāna,ṭṭha,ဥဒါန၊ဋ္ဌ,udāna-aṭṭhakathā,0
+vajira,ဝဇိရ,vajirabuddhi-ṭīkā,0
+vajira,ṭī,ဝဇိရ၊ဋီ,vajirabuddhi-ṭīkā,0
+vi,1,ဝိ၊၁,pārājikapāḷi,0
+vi,2,ဝိ၊၂,pācittiyapāḷi,0
+vi,3,ဝိ၊၃,mahāvaggapāḷi,0
+vi,4,ဝိ၊၄,cūḷavaggapāḷi,0
+vi,5,ဝိ၊၅,parivārapāḷi,0
+vi,ṭṭha,1,ဝိ၊ဋ္ဌ၊၁,pārājikakaṇḍa-aṭṭhakathā,1
+vi,ṭṭha,2,ဝိ၊ဋ္ဌ၊၂,pārājikakaṇḍa-aṭṭhakathā,2
+vi,ṭṭha,3,ဝိ၊ဋ္ဌ၊၃,pācittiyādi-aṭṭhakathā,0
+vi,ṭṭha,4,ဝိ၊ဋ္ဌ၊၄,cūḷavaggādi-aṭṭhakathā,0
+vimāna,ṭṭha,ဝိမာန၊ဋ္ဌ,vimānavatthu-aṭṭhakathā,0
+vimati,1,ဝိမတိ၊၁,vimativinodanī-ṭīkā,1
+vimati,2,ဝိမတိ၊၂,vimativinodanī-ṭīkā,2
+visuddhi,1,ဝိသုဒ္ဓိ၊၁,visuddhimagga,1
+visuddhi,2,ဝိသုဒ္ဓိ၊၂,visuddhimagga,2
+visuddhi,ṭī,1,ဝိသုဒ္ဓိ၊ဋီ၊၁,visuddhimagga-mahāṭīkā,1
+visuddhi,ṭī,2,ဝိသုဒ္ဓိ၊ဋီ၊၂,visuddhimagga-mahāṭīkā,2
+visuddhi,ṭī,1,မဟာဋီ၊၁,visuddhimagga-mahāṭīkā,1
+visuddhi,ṭī,2,မဟာဋီ၊၂,visuddhimagga-mahāṭīkā,2
+yamaka,1,ယမက၊၁,yamakapāḷi,1
+yamaka,2,ယမက၊၂,yamakapāḷi,2
+yamaka,3,ယမက၊၃,yamakapāḷi,3