mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-28 11:16:37 +08:00
192 lines
6.3 KiB
Go
192 lines
6.3 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/ShukeBta/MediaStationGo/internal/model"
|
|
)
|
|
|
|
// SystemInfo returns the full Emby identity payload.
|
|
func (e *EmbyService) SystemInfo() map[string]any {
|
|
return map[string]any{
|
|
"Id": embyServerID,
|
|
"ServerId": embyServerID,
|
|
"ServerName": "MediaStationGo",
|
|
"Version": embyCompatVersion,
|
|
"ServerVersion": embyCompatVersion,
|
|
"ProductName": "Emby Server",
|
|
"OperatingSystem": "Windows",
|
|
"Architecture": "X64",
|
|
"LocalAddress": "",
|
|
"WanAddress": "",
|
|
"HasPendingRestart": false,
|
|
"IsShuttingDown": false,
|
|
"SupportsLibraryMonitor": true,
|
|
"SupportsHttps": false,
|
|
"SupportsAutoDiscovery": true,
|
|
"HttpServerPortNumber": e.cfg.App.Port,
|
|
"HttpsPortNumber": 0,
|
|
"PublishedServerUrl": "",
|
|
"WebSocketPortNumber": e.cfg.App.Port,
|
|
"CompletedInstallations": []any{},
|
|
"CanSelfRestart": false,
|
|
"CanLaunchWebBrowser": false,
|
|
"CanRestart": false,
|
|
}
|
|
}
|
|
|
|
// SystemInfoPublic 是不需要认证的精简版(Emby Web 客户端登陆前会拉)。
|
|
func (e *EmbyService) SystemInfoPublic() map[string]any {
|
|
return map[string]any{
|
|
"Id": embyServerID,
|
|
"ServerId": embyServerID,
|
|
"ServerName": "MediaStationGo",
|
|
"Version": embyCompatVersion,
|
|
"ServerVersion": embyCompatVersion,
|
|
"ProductName": "Emby Server",
|
|
"OperatingSystem": "Windows",
|
|
"LocalAddress": "",
|
|
"WanAddress": "",
|
|
"HttpServerPortNumber": e.cfg.App.Port,
|
|
"HttpsPortNumber": 0,
|
|
"SupportsHttps": false,
|
|
"SupportsAutoDiscovery": true,
|
|
"StartupWizardCompleted": true,
|
|
}
|
|
}
|
|
|
|
// ListUsers returns Emby-shaped users.
|
|
func (e *EmbyService) ListUsers(ctx context.Context) ([]map[string]any, error) {
|
|
users, err := e.repo.User.List(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]map[string]any, 0, len(users))
|
|
for _, u := range users {
|
|
out = append(out, e.userPayload(&u))
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// FindUser 用 ID 查用户,用于 /Users/Me 与 /Users/{id}。
|
|
func (e *EmbyService) FindUser(ctx context.Context, id string) (map[string]any, error) {
|
|
u, err := e.repo.User.FindByID(ctx, id)
|
|
if err != nil || u == nil {
|
|
return nil, err
|
|
}
|
|
return e.userPayload(u), nil
|
|
}
|
|
|
|
func (e *EmbyService) userPayload(u *model.User) map[string]any {
|
|
canDownload := u.Role == "admin"
|
|
return map[string]any{
|
|
"Id": u.ID,
|
|
"Name": u.Username,
|
|
"ServerId": embyServerID,
|
|
"ServerName": "MediaStationGo",
|
|
"HasPassword": true,
|
|
"HasConfiguredPassword": true,
|
|
"HasConfiguredEasyPassword": false,
|
|
"EnableAutoLogin": false,
|
|
"LastLoginDate": u.LastLoginAt,
|
|
"LastActivityDate": u.UpdatedAt,
|
|
"Configuration": map[string]any{
|
|
"PlayDefaultAudioTrack": true,
|
|
"DisplayCollectionsView": true,
|
|
"DisplayMissingEpisodes": false,
|
|
"SubtitleMode": "Default",
|
|
"EnableNextEpisodeAutoPlay": true,
|
|
"AudioLanguagePreference": "",
|
|
"SubtitleLanguagePreference": "",
|
|
},
|
|
"Policy": map[string]any{
|
|
"IsAdministrator": u.Role == "admin",
|
|
"IsHidden": false,
|
|
"IsDisabled": !u.IsActive,
|
|
"EnableUserPreferenceAccess": true,
|
|
"EnableRemoteAccess": true,
|
|
"EnableMediaPlayback": true,
|
|
"EnableAudioPlaybackTranscoding": true,
|
|
"EnableVideoPlaybackTranscoding": true,
|
|
"EnablePlaybackRemuxing": true,
|
|
"EnableLiveTvAccess": false,
|
|
"EnableContentDownloading": canDownload,
|
|
"EnableSyncTranscoding": canDownload,
|
|
"EnableMediaConversion": canDownload,
|
|
"EnableAllChannels": true,
|
|
"EnableAllFolders": true,
|
|
"EnableAllDevices": true,
|
|
"AuthenticationProviderId": embyLocalAuthenticationProviderID,
|
|
"PasswordResetProviderId": embyLocalPasswordResetProviderID,
|
|
},
|
|
}
|
|
}
|
|
|
|
// Views 返回 Emby 中"虚拟根目录"——每个 library 一个条目。
|
|
func (e *EmbyService) Views(ctx context.Context, userID string) (map[string]any, error) {
|
|
libs, err := e.repo.Library.List(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
libs = FilterDisplayCloudLibraries(ctx, e.repo, libs)
|
|
visibility := e.mediaVisibility(ctx, userID)
|
|
items := make([]map[string]any, 0, len(libs))
|
|
for _, l := range libs {
|
|
if !e.libraryVisibleFromCachedVisibility(l, visibility) {
|
|
continue
|
|
}
|
|
items = append(items, e.libraryAsView(&l))
|
|
}
|
|
return map[string]any{"Items": items, "TotalRecordCount": len(items), "StartIndex": 0}, nil
|
|
}
|
|
|
|
func (e *EmbyService) libraryAsView(l *model.Library) map[string]any {
|
|
collectionType := "movies"
|
|
switch l.Type {
|
|
case "tv":
|
|
collectionType = "tvshows"
|
|
case "anime":
|
|
collectionType = "tvshows" // Emby 没有专门的 anime CollectionType
|
|
case "variety":
|
|
collectionType = "tvshows"
|
|
case "music":
|
|
collectionType = "music"
|
|
}
|
|
return map[string]any{
|
|
"Id": l.ID,
|
|
"Name": l.Name,
|
|
"CollectionType": collectionType,
|
|
"ServerId": embyServerID,
|
|
"Type": "CollectionFolder",
|
|
"IsFolder": true,
|
|
"Path": l.Path,
|
|
"SortName": strings.ToLower(l.Name),
|
|
"DateCreated": l.CreatedAt.UTC().Format(time.RFC3339),
|
|
"CanDelete": false,
|
|
"CanDownload": false,
|
|
"DisplayPreferencesId": l.ID,
|
|
"PrimaryImageItemId": l.ID,
|
|
"PrimaryImageAspectRatio": 1.7777777777777777,
|
|
"RecursiveItemCount": 0,
|
|
"ChildCount": 0,
|
|
"SpecialFeatureCount": 0,
|
|
"EnableMediaSourceDisplay": true,
|
|
"PlayAccess": "Full",
|
|
"ExternalUrls": []any{},
|
|
"ProviderIds": map[string]string{},
|
|
"Genres": []string{},
|
|
"Tags": []string{},
|
|
"ImageTags": map[string]string{},
|
|
"BackdropImageTags": []string{},
|
|
"UserData": map[string]any{
|
|
"PlaybackPositionTicks": 0,
|
|
"PlayCount": 0,
|
|
"IsFavorite": false,
|
|
"Played": false,
|
|
"UnplayedItemCount": 0,
|
|
},
|
|
}
|
|
}
|