技術細節與開發 - MiCat-S/Mi302 GitHub Wiki
這一頁給想了解內部運作或參與開發的人:請求流程、路徑怎麼比對、已實作的 Emby 端點、管理 API、程式結構、怎麼跑測試,以及參考過的專案。
Mi302 是一個 FastAPI 程式,用 uvicorn 執行,資料存在 SQLite(data/library.db)。
-
掃描:把媒體庫資料夾裡的
.strm、一般影片檔、nfo、圖片寫進資料庫,見 媒體庫與掃描。 -
瀏覽:播放器呼叫 Emby API(
/Users/{id}/Views、/Items…),Mi302 從資料庫組出 Emby 的BaseItemDto。 -
播放資訊:
POST /Items/{id}/PlaybackInfo。strm 項目回Protocol=Http、IsRemote=true,只允許直接播放(關閉轉碼),DirectStreamUrl指回/videos/{id}/stream.{格式}。 -
串流:播放器請求
/Videos/{id}/stream、/Videos/{id}/original.xxx或/Items/{id}/Download:- strm 項目:讀 strm 內容,套用路徑替換;帶 pickcode 就用播放器的 User-Agent 向 115 取直鏈,否則可以先跟完上游的重導向;最後回
302 Location: <真實網址>。 - 一般影片檔:直接送檔,支援 Range。
- strm 項目:讀 strm 內容,套用路徑替換;帶 pickcode 就用播放器的 User-Agent 向 115 取直鏈,否則可以先跟完上游的重導向;最後回
-
進度:
/Sessions/Playing…記續播位置和已看,也拿來學片頭片尾,見 片頭片尾跳過。
細節見 播放與外網連線。Mi302 回報自己是 Emby Server 4.8.11.0。
進來的每個 HTTP 請求先整理路徑,再比對路由:
- 路徑裡的英文字母轉小寫,其他字元不動(
/Persons/Émilie的É保留),所以路徑不分大小寫。 - 開頭的
/emby、/mediabrowser拿掉,有沒有這個前綴都可以。 - 結尾的
/拿掉。 - 查詢參數的名稱不分大小寫(
ParentId、parentid都可以)。
其他:
- 錯誤回應是純文字,和 Emby 一樣。
- CORS 全開。
- FastAPI 自動產生的 API 說明在
/api-docs。
token 依序從這些地方找:
- 標頭
X-Emby-Token、X-MediaBrowser-Token。 - 標頭
X-Emby-Authorization(或Authorization)裡的Token="…",格式是MediaBrowser Client="…", Device="…", DeviceId="…", Version="…", Token="…"。 - 查詢參數
api_key、X-Emby-Token、ApiKey、X-MediaBrowser-Token。
token 由 POST /Users/AuthenticateByName 發給。同一個使用者在同一個裝置(DeviceId)重新登入時,舊的 token 作廢。API 金鑰(設定檔的 api_keys,或在網頁上建立的)以管理員的身分執行(依名稱排序的第一個管理員),給 MoviePilot 這類程式用;它不能呼叫 /web/api/… 和 /p115/…。
路徑裡有使用者(/Users/{id}/…)或帶 UserId 參數時,讀寫的是那個人的觀看紀錄、續播點和收藏。一般使用者只能指定自己,指定別人回 403;管理員和 API 金鑰可以指定別人,找不到那個人回 404。id 不分大小寫,也接受有連字號的 GUID 寫法。沒指定就是登入的人。
路徑照 Emby 的寫法列出,實際上不分大小寫。「不用登入」以外的都要帶 token。
| 端點 | 說明 |
|---|---|
GET /System/Info/Public |
伺服器名稱、版本、id;不用登入 |
GET /System/Info |
完整伺服器資訊 |
GET, POST /System/Ping |
回 Emby Server;不用登入 |
GET /System/Endpoint |
固定回「區域網路內」;不用登入 |
GET /Branding/Configuration |
空的品牌設定;不用登入 |
GET /Users/Public |
登入畫面的使用者清單(server.public_users 關掉時是空的);不用登入 |
POST /Users/AuthenticateByName |
登入,JSON 或表單,密碼欄位是 Pw 或 Password;不用登入 |
GET /Users |
所有使用者;要管理員 |
GET /Users/Query |
同上,包成 {Items, TotalRecordCount}(MoviePilot 用它算有幾個使用者);要管理員 |
GET /Users/{id} |
使用者資料;只能查自己,管理員可以查別人 |
POST /Sessions/Logout |
登出,作廢這個 token |
POST /Sessions/Capabilities、/Sessions/Capabilities/Full
|
接受但不處理 |
GET /Sessions |
空清單 |
GET, POST /DisplayPreferences/{id} |
固定的預設值,不儲存;不用登入 |
GET /Users/{id}/GroupingOptions、/Plugins、/Localization/{kind}
|
空清單;不用登入 |
GET /Items/{id}/RemoteImages |
一律是空的(Mi302 不向外查圖,MoviePilot 會改用 /Items/{id}/Images/{類型});不用登入 |
WebSocket /embywebsocket
|
只回應 KeepAlive |
| 端點 | 說明 |
|---|---|
GET /Users/{id}/Views、/Library/MediaFolders
|
媒體庫清單 |
GET /Library/VirtualFolders、/Library/VirtualFolders/Query、/Library/SelectableMediaFolders
|
媒體庫和資料夾路徑(MoviePilot 用) |
POST /Library/Refresh |
全部重新掃描;要管理員 |
POST /Library/Media/Updated |
只掃通知裡的路徑;要管理員 |
POST /Items/{id}/Refresh |
只重掃這一項所在的劇或電影;要管理員 |
GET /Users/{id}/Items、/Items
|
查詢項目,見下面的參數 |
GET /Users/{id}/Items/{itemId}、/Items/{id}
|
單一項目的完整資料,含 MediaSources、People、Chapters;id 是人物 id 時回人物 |
GET /Users/{id}/Items/Latest |
最近新增(Limit、ParentId、IncludeItemTypes;要集時以劇為單位) |
GET /Users/{id}/Items/Resume |
繼續觀看 |
GET /Items/{id}/Ancestors |
上層項目 |
GET /Items/Counts |
電影、劇集、集的數量 |
GET /Shows/{id}/Seasons |
季 |
GET /Shows/{id}/Episodes |
集(SeasonId、Season、StartIndex、Limit、Fields) |
GET /Shows/NextUp |
接著看(SeriesId、Limit) |
GET /Genres |
所有類型 |
GET /Persons、/Persons/{name}
|
搜尋人物、依名稱找人物(原名、中文名都認) |
GET /Persons/{name}/Images/{type} |
人物照片,302 到 nfo 裡的網址;不用登入 |
POST /Users/{id}/PlayedItems/{itemId};DELETE(或 POST …/Delete) |
標記已看、未看;對劇或季就是底下所有集 |
POST /Users/{id}/FavoriteItems/{itemId};DELETE(或 POST …/Delete) |
加入、移除收藏 |
/Items 的查詢參數:ParentId、Recursive、IncludeItemTypes(含 Person 時一併搜尋人物)、ExcludeItemTypes、Ids、PersonIds、Person、IsFolder、SearchTerm、NameStartsWith、Years、Genres、Filters(IsPlayed、IsUnplayed、IsFavorite、IsResumable)、IsPlayed、IsFavorite、SortBy、SortOrder、StartIndex、Limit、Fields(MediaSources、People、Chapters)、UserId。
StartIndex、Limit 是負數或看不懂時當成沒給。/Items 和 /Shows/{id}/Episodes 沒給 Limit 就是全部,和 Emby 一樣;最新、繼續觀看、接著看預設 20 項,人物搜尋預設 50 項,這幾個最多 500 項。Limit=0 只回總數。
SortBy 認得:SortName、Name、DateCreated、DateLastContentAdded、PremiereDate、ProductionYear、CommunityRating、CriticRating、Runtime、Random、DatePlayed、PlayCount、IndexNumber、ParentIndexNumber、AiredEpisodeOrder。
回空結果的端點(讓播放器不報錯):/Users/{id}/Items/{id}/Intros、/Items/{id}/Similar、/Movies/{id}/Similar、/Shows/{id}/Similar、/Videos/{id}/AdditionalParts、/Items/{id}/CriticReviews、/Users/{id}/Items/{id}/LocalTrailers、/Users/{id}/Items/{id}/SpecialFeatures、/Items/{id}/SpecialFeatures、/Items/{id}/ThemeMedia。
| 端點 | 說明 |
|---|---|
GET /Items/{id}/Images/{type}[/{index}] |
讀圖片,type 是 Primary、Backdrop、Thumb、Logo、Art;不用登入 |
POST /Items/{id}/Images/{type}[/{index}] |
上傳圖片(base64 文字或圖片檔本身,最大 30 MB),存在 data/images/;要管理員 |
DELETE /Items/{id}/Images/{type}[/{index}] |
刪掉上傳的圖片,改回資料夾裡的圖;要管理員 |
GET /Users/{id}/Images/{type} |
使用者頭像,一律 404 |
| 端點 | 說明 |
|---|---|
GET, POST /Items/{id}/PlaybackInfo |
媒體來源 |
GET, HEAD /Videos/{id}/{name} |
串流(stream、stream.mkv、original.mkv…);redirect.require_auth 開著時要登入 |
GET, HEAD /Items/{id}/Download、/Items/{id}/File
|
下載;一律要登入,server.allow_download 關掉時回 403 |
POST /Sessions/Playing、/Sessions/Playing/Progress、/Sessions/Playing/Stopped
|
播放進度 |
POST /Sessions/Playing/Ping |
接受但不處理 |
POST /Users/{id}/PlayingItems/{itemId}、…/Progress;DELETE(或 POST …/Delete) |
舊版的播放進度回報 |
| 端點 | 說明 |
|---|---|
GET /Episode/{id}/IntroTimestamps(也認 /v1)、GET /Episode/{id}/Timestamps
|
Jellyfin Intro Skipper 外掛格式的片頭片尾 |
GET /MediaSegments/{id} |
Jellyfin 10.10 的媒體片段 |
GET, HEAD /d/{pickcode}[.副檔名][/檔名] |
Mi302 產生的 strm 用的短連結,302 到 115 直鏈;不用登入 |
GET, HEAD /p115/redirect?pickcode=…、/api/v1/plugin/p115strmhelper/redirect_url?pickcode=…
|
相容其他工具的 strm;不用登入 |
OPTIONS, PROPFIND, GET, HEAD /dav/{115 路徑} |
只能讀的 WebDAV(webdav.enabled):PROPFIND 列資料夾(Depth 最多 1),GET 檔案 302 到 115 直鏈,HEAD 只回大小;要 HTTP Basic 登入,寫入的方法回 405 |
管理網頁 /web 用的 API。115 的檔案、資料夾 id 有 19 位,超過 JavaScript 能精確表示的整數,回傳時一律是字串;送進來時字串、數字都接受。除了 /web/api/setup,都要用管理員帳號登入後的 token(放在 X-Emby-Token 標頭),API 金鑰不行。請求和回應都是 JSON。
| 路徑 | 用途 |
|---|---|
GET, POST /web/api/setup |
是否需要建立第一個管理員;建立它(只在還沒有任何帳號時可用) |
GET, PUT /web/api/settings |
讀取、儲存網頁上的設定(寫進設定檔) |
GET, POST /web/api/scan |
掃描進度和各媒體庫的項目數;開始掃描:{"library": 名稱}、{"path": 路徑},都沒有就全部;沒在掃描時 last 是上一次掃描(範圍、處理了幾個、是否按了停止) |
GET /web/api/libraries/userdata |
這個媒體庫(name)有幾筆觀看紀錄、是幾個使用者的(刪除媒體庫的確認框用) |
GET, POST /web/api/users;PUT, DELETE /web/api/users/{id}
|
使用者 |
GET /web/api/browse、/web/api/115/browse
|
選伺服器上的資料夾、選 115 目錄 |
GET /web/api/115/offline、POST /web/api/115/offline、POST /web/api/115/offline/delete、POST /web/api/115/offline/clear
|
115 雲下載:任務清單(page,附這個月還能加幾個);加任務(urls 一行一個、folder 存到的 115 路徑,空的用 115 預設);刪任務(hashes,files 連下載好的檔案一起刪);清除已完成或已失敗的紀錄(what:done/failed) |
POST /web/api/115/offline/retry |
重新加入失敗的雲下載任務(hash、url、folder_id):先刪那一筆失敗的紀錄(不刪檔案),再加回原來的資料夾 |
POST /web/api/115/share/read、POST /web/api/115/share/start、GET /web/api/115/share/status、POST /web/api/115/share/stop
|
轉存 115 分享(要用 cookie 登入):讀分享(text 一行一個連結;回傳每個分享的標題、大小、最上層的項目,讀不到的在 error 寫 115 的原文);在背景轉存(shares:[{code, receive_code, title, ids}],ids 是字串;folder 存到的 115 資料夾;target:path 整理到 target_path,空的先不整理);進度;停止(做完手上這一個分享) |
POST /web/api/115/rapid/read、POST /web/api/115/rapid/start、GET /web/api/115/rapid/status、POST /web/api/115/rapid/stop
|
從阿里雲盤秒傳到 115(要用 cookie 登入 115、登入阿里雲盤):讀取(source 阿里雲盤路徑、media_only 只要影片和字幕;回傳幾支影片、字幕、總大小、沒有 SHA1 的、超過 115 單檔上限的、前 200 支);在背景秒傳(source、folder 存到的 115 資料夾、target:path 整理到 target_path,空的先不整理、media_only);進度和每一支的結果;停止(做完手上這一支) |
GET /web/api/aliyun/status、POST /web/api/aliyun/token、POST /web/api/aliyun/logout、GET /web/api/aliyun/dirs
|
阿里雲盤:帳號狀態;登入(refresh_token,失敗時換回原本的);登出;選資料夾(path 這一層的子資料夾和檔案數,空的是「資源庫」「備份盤」) |
GET /web/api/115/recyclebin、POST /web/api/115/recyclebin/clean
|
115 回收站的一頁(offset、limit);清空(永久刪除,要 {"confirm": "清空"},cookie 通道再帶 password 安全密鑰) |
GET /web/api/deleted |
Mi302 送進 115 回收站的紀錄,新的在前面(source 只看一種來源、offset、limit);回傳 items、total、sources
|
GET /web/api/115/files |
瀏覽 115:資料夾(cid、path,或只給 path)的子資料夾和檔案,影片附上在媒體庫裡的樣子(lib);檔案一次最多 1000 個,offset 要下一批 |
POST /web/api/115/delete |
瀏覽 115 裡勾的刪掉(parent 現在看的資料夾 id、ids):先列一次那個資料夾,只刪真的在裡面的,移到 115 回收站;同步目錄裡的本機 strm 和媒體庫跟著拿掉 |
GET /web/api/115/organize、POST /web/api/115/organize/check、GET /web/api/115/organize/job
|
整理 115 網盤:要整理的資料夾(問過 MoviePilot 名稱不一樣的、集號不對的;q、kind 是 series/movie/episodes/held(先不整理的)、offset、limit;瀏覽 115 加進來的在 pinned;每一個的 held 是有沒有標先不整理),附背景檢查進度 job、還沒問過的 unchecked;在背景問 MoviePilot(refresh 連問過的也重問);整理工作的進度 |
POST /web/api/115/organize/folder、DELETE /web/api/115/organize/folder/{id}
|
瀏覽 115 裡挑的資料夾(cid、path)釘在清單最上面;拿掉 |
POST /web/api/115/organize/preview、POST /web/api/115/organize/recommend、POST /web/api/115/organize/execute
|
預覽一個資料夾(id;每一部分可以指定 parts: {部分: {type, tmdbid, season, format}};target 是 parent/auto/path 加 target_path),回傳預覽代碼 token;一部分的集數定位(id、part,先請 MoviePilot 推薦);照預覽代碼執行(tokens,cleanup 整理完把沒有影片留下的來源資料夾移到回收站) |
POST /web/api/115/organize/all、POST /web/api/115/organize/all/stop、GET /web/api/115/organize/all
|
全部整理:清單上符合 q、kind 的(加上瀏覽 115 釘上來的)在背景一個一個預覽,沒問題(預覽的 review 是空的)就照預覽整理,其他跳過並記原因(target、target_path、cleanup;標了先不整理的、一次要送超過 max_videos 支影片的不做,預設 300,0 = 不限;MoviePilot 連不上時等它恢復,最多 15 分鐘);做完手上這一個就停;進度和跳過、失敗的明細(明細最多留 2000 個,results_total 是全部的數量,truncated 表示有沒有留不下的;進度也在 GET /web/api/115/organize/job 的 batch) |
POST /web/api/115/organize/hold |
先不整理(id、hold):全部整理時跳過、排到清單後面;回傳那一個 |
GET /web/api/115/organize/episodes、POST /web/api/115/organize/delete
|
刪除:一部劇的每一集和劇集資料夾(series,集號不對的標 problem);刪勾選的集(series_id、file_ids、remove_folder),或清單上的一個整個刪(id),都移到 115 回收站,本機 strm 和媒體庫跟著拿掉;{"ids": [...]} 勾選的在背景一個一個刪,進度看 organize/job 的 deleting
|
GET /web/api/server、POST /web/api/server/check、POST /web/api/server/update、POST /web/api/server/restart
|
版本和上次檢查更新的結果(boot 每次啟動都不同)、現在檢查、更新到最新版(背景執行,進度看 job)、重新啟動 |
GET /web/api/libraries/suggest?path= |
批量新增媒體庫:列出子資料夾,附上建議的類型和理由、影片數(complete 表示數完了)、是否已在某個媒體庫裡(used)、要不要預設勾選;partial 表示有資料夾沒看完 |
POST /web/api/moviepilot/test、GET /web/api/moviepilot/status、POST /web/api/moviepilot/scrape
|
MoviePilot 測試連線、狀態、刮削缺少資料的項目 |
GET /web/api/moviepilot/library-dirs |
MoviePilot 目錄設定裡存儲是 115 的媒體庫目錄(dirs 每一項有 name、path、type;enabled 是設定了 MoviePilot 沒有),給轉存分享的「整理到」;沒設定、讀不到時 dirs 是空的、error 寫原因 |
GET /web/api/series、POST /web/api/moviepilot/fill
|
劇集和集號空洞清單;補全缺集 |
GET /web/api/moviepilot/subscriptions、POST /web/api/moviepilot/subscriptions/clear、GET /web/api/moviepilot/subscriptions/unsubscribed
|
MoviePilot 現在有幾個訂閱;取消所有訂閱({"confirm": "取消訂閱"});最近 5 次取消前的訂閱清單(backups) |
GET /web/api/intro/status、POST /web/api/intro/clear
|
片頭片尾({"season_id": id} 只清一季學到的紀錄) |
GET /web/api/intro/seasons?q=、PUT /web/api/intro/seasons/{季 id}
|
片頭片尾的季清單和搜尋;手動設定一季(all_seasons 套用到整部劇) |
GET /web/api/people/status、POST /web/api/people/resolve
|
演職人員中文名 |
GET, POST /web/api/backups;GET /web/api/backups/{name}
|
備份清單、立即備份、下載備份 |
GET /web/api/mediainfo/status、GET /web/api/mediainfo/titles、POST /web/api/mediainfo/probe
|
媒體資訊:狀態;還缺媒體資訊的片名(可篩選、排序);提取(可帶篩選、limit、ids) |
POST /web/api/mediainfo/probe/limit、POST /web/api/mediainfo/stop
|
提取中改這一批最多幾支({"limit": N});停止這一批 |
GET /web/api/logs、/web/api/logs/download
|
日誌、下載日誌檔 |
GET, POST /web/api/apikeys;DELETE /web/api/apikeys/{key}
|
API 金鑰 |
GET /p115/status |
115 帳號狀態 |
POST /p115/qrcode、GET /p115/qrcode/status、POST /p115/cookies、POST /p115/logout
|
掃碼登入、貼 cookie、登出 |
POST /p115/open/qrcode、GET /p115/open/qrcode/status、POST /p115/open/logout
|
115 開放平台授權 |
POST /p115/strm/sync?mode=incremental(不帶就是全量)、GET /p115/strm/status、PUT /p115/strm/tasks
|
同步、同步狀態、同步任務 |
POST /p115/strm/rewrite、POST /p115/strm/rewrite/stop
|
把現有的 strm 改成目前的伺服器網址(只動本機檔案);停止(改好的留著) |
GET /web/api/dupes、POST /web/api/dupes/scan、GET /web/api/dupes/groups、GET /web/api/dupes/shows、POST /web/api/dupes/delete、GET /web/api/dupes/log
|
115 重複檔案:狀態、找重複(paths)、分組清單、刪除(overrides、sha1+size 只刪一組、dry_run 只試算);kind=versions 是不同版本(grp 只刪一組、show 只刪一部、use_suggestions 照建議;網頁的清單用 GET /web/api/dupes/shows?kind=versions,同一部劇合成一列,展開時 groups?kind=versions&show=…;完全相同一樣用 shows?kind=exact、groups?show=…,刪除帶 show 只刪那一部)、刪除紀錄;kind=big 是大檔案(min_size 位元組、type 是 movie/episode/none 篩選,刪除時 use_suggestions 是符合條件的全部,不必留一份);POST /web/api/dupes/prefer 換不同版本建議保留的解析度(prefer 是 1080、2160 或 highest) |
POST /web/api/scan/stop、POST /p115/strm/stop、POST /web/api/dupes/stop、POST /web/api/moviepilot/stop、POST /web/api/115/organize/check/stop、POST /web/api/115/organize/execute/stop、POST /web/api/115/organize/delete/stop
|
停止掃描、115 同步、找重複/刪重複、MoviePilot 刮削、補全缺集或取消訂閱(what 是 scrape、fill 或 unsubscribe)、整理 115 網盤的檢查、執行、刪除勾選的:都在安全的地方停下,沒做完的下次再做;回傳 stopped(沒在跑是 false) |
GET /web 是管理網頁本身,/web/115 轉到 /web#115。
embyserver/ 底下每個模組:
| 模組 | 內容 |
|---|---|
__main__.py |
命令列入口:python -m embyserver [-c config.yaml] [--scan] [--sync-115 [incremental]] [--reset-password 帳號 新密碼]
|
app.py |
組裝 FastAPI 程式、整理路徑、啟動背景工作 |
auth.py |
使用者、token、Emby 認證標頭 |
backup.py |
每天備份資料庫和設定檔 |
browse115.py |
瀏覽 115:列資料夾,影片對照媒體庫 |
config.py |
設定檔的資料結構和讀取 |
config_file.py |
把設定寫回 config.yaml
|
settings.py |
網頁上可改的設定:驗證、套用、和設定檔同步 |
db.py |
SQLite 資料表和存取 |
dupes.py |
115 上的重複檔案:完全相同、不同版本、建議保留哪一份;大檔案 |
dto.py |
資料庫列轉成 Emby 的 BaseItemDto、UserDto、MediaSourceInfo
|
filetypes.py |
共用的副檔名清單 |
organize115.py |
整理 115 網盤:問 MoviePilot 每個資料夾整理後叫什麼(結果記在 organize_checks),整個資料夾交給它預覽 |
http_util.py |
呼叫外部服務的 httpx Client,把連線錯誤轉成看得懂的原因 |
intro.py |
從播放行為學片頭片尾 |
library_suggest.py |
批量新增媒體庫:猜子資料夾是電影還是劇集 |
logs.py |
日誌:終端、日誌檔、記憶體,遮掉憑證 |
mediainfo.py |
媒體資訊:ffprobe 結果和 X-mediainfo.json 轉成 Emby 格式 |
prober.py |
用 ffprobe 探測 strm 指向的影片 |
moviepilot.py |
刮削、補全缺集交給 MoviePilot |
moviepilot-plugin/ |
MoviePilot V3 外掛「Mi302 整理助手」(package.v3.json、plugins.v3/mi302organizer/):照 MoviePilot 的規則算名字(naming.py),用 MoviePilot 的 115 授權照 Mi302 送來的清單批次改名,見 MoviePilot 整合
|
p115.py |
115:掃碼登入、取直鏈、列目錄、生活事件、熔斷 |
p115_open.py |
115 開放平台通道 |
probe_select.py |
媒體資訊:挑要提取的片名(篩選、排序) |
people.py |
演職人員、中文名、類型中文化 |
redirect.py |
解析 strm 真實網址、路徑替換、重導向快取 |
reorganize.py |
整理的執行(照預覽代碼送 MoviePilot、清掉搬空的舊資料夾、之後的增量同步)和刪除;集數定位模板(MoviePilot 推薦不出來時的備用) |
scanner.py |
媒體庫掃描 |
share115.py |
轉存 115 分享:讀分享、轉存到暫存子資料夾、等 115 做完,加進「整理 115 網盤」交給 MoviePilot 整理 |
rapid115.py |
從阿里雲盤秒傳到 115:115 的上傳初始化(加密、二次驗證),整批秒傳,完成的資料夾加進「整理 115 網盤」 |
aliyun.py |
阿里雲盤開放平台:登入和換 token、列目錄、取下載網址、讀一段 |
strm_sync.py |
從 115 產生 strm、下載中繼資料 |
textutil.py |
拼音排序、拼音搜尋、繁簡轉換 |
updater.py |
網頁上的檢查更新、更新、重新啟動 |
routes/system.py |
System、Users、Sessions 等端點 |
routes/items.py |
媒體庫、項目查詢、使用者資料、圖片 |
routes/playback.py |
PlaybackInfo、串流、進度回報、片頭片尾端點 |
routes/p115.py |
115 登入和同步的管理 API、pickcode 短連結 |
routes/web/ |
管理網頁和它的 API,照分頁分成 setup、scan、intro、moviepilot、organize、p115、server 幾個檔案 |
routes/common.py |
路由共用的小工具 |
web/admin.html |
管理網頁(單一檔案,HTML、CSS、JavaScript 都在裡面) |
資料庫 library.db 的資料表:meta(伺服器 id、115 登入狀態、同步進度等)、users、tokens、items、user_data(觀看紀錄、收藏)、p115_index(115 檔案 id 對本機路徑)、media_info、people、person_names、intro_obs、mp_no_image。
需要 Python 3.10 以上。在 venv 裡:
pip install -r requirements.txt pytest ruff
python -m pytest
ruff check embyserver tests測試也會用 node --check 檢查管理網頁的 JavaScript 語法(沒有 node 就略過),並檢查說明文件:Wiki 的連結和錨點都找得到、每一頁都在側欄、三種語言的設定檔參考都寫到每一個鍵。
改了 install.sh 之後:
shellcheck install.sh-
DDSRem-Dev/MoviePilot-Plugins:302 播放流程參考
embyreverseproxy外掛,差別是 Mi302 背後不需要真的 Emby,Emby API 自己實作;115 掃碼登入和取直鏈的流程參考p115strmhelper。 -
StrmAssistant(Emby 神醫助手):
X-mediainfo.json沿用它「媒體資訊持久化」的格式;片頭片尾的學法參考它的「片頭探測 - 播放行為」。 -
xiao-vvv/emby-mediainfo(MIT 授權):ffprobe 到 Emby 欄位的對照、探測的限速和 115 熔斷規則改寫自這個專案,版權聲明保留在
embyserver/mediainfo.py。