-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
85c4bd8
commit 49bc421
Showing
5 changed files
with
297 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,4 +5,3 @@ dist/ | |
__debug_bin | ||
abb_ia.log | ||
config.yaml | ||
audiobookshelf |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package audiobookshelf_test | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/vpoluyaktov/abb_ia/internal/audiobookshelf" | ||
"github.com/vpoluyaktov/abb_ia/internal/config" | ||
) | ||
|
||
func TestLogin(t *testing.T) { | ||
config.Load() | ||
url := config.AudiobookshelfUrl() | ||
username := config.AudiobookshelfUser() | ||
password := config.AudiobookshelfPassword() | ||
|
||
if url != "" && username != "" && password != "" { | ||
loginResp, err := audiobookshelf.Login(url+"/login", username, password) | ||
|
||
assert.NoError(t, err) | ||
assert.NotNil(t, loginResp.User.ID) | ||
assert.NotNil(t, loginResp.User.Token) | ||
} | ||
} | ||
|
||
func TestLibraries(t *testing.T) { | ||
config.Load() | ||
url := config.AudiobookshelfUrl() | ||
username := config.AudiobookshelfUser() | ||
password := config.AudiobookshelfPassword() | ||
|
||
if url != "" && username != "" && password != "" { | ||
loginResp, err := audiobookshelf.Login(url+"/login", username, password) | ||
assert.NoError(t, err) | ||
if err == nil { | ||
libraryResponse , err := audiobookshelf.Libraries(url, loginResp.User.Token) | ||
assert.NoError(t, err) | ||
assert.NotNil(t, libraryResponse) | ||
assert.NotEmpty(t, libraryResponse.Libraries) | ||
} | ||
} | ||
} | ||
|
||
func TestScan(t *testing.T) { | ||
config.Load() | ||
url := config.AudiobookshelfUrl() | ||
username := config.AudiobookshelfUser() | ||
password := config.AudiobookshelfPassword() | ||
libraryName := config.AudiobookshelfLibrary() | ||
|
||
if url != "" && username != "" && password != "" && libraryName != "" { | ||
loginResp, err := audiobookshelf.Login(url+"/login", username, password) | ||
if err == nil { | ||
assert.NoError(t, err) | ||
libraryResponse , err := audiobookshelf.Libraries(url, loginResp.User.Token) | ||
if err == nil { | ||
assert.NoError(t, err) | ||
libraryID, err := audiobookshelf.GetLibraryByName(libraryResponse.Libraries, libraryName) | ||
if err == nil { | ||
err = audiobookshelf.ScanLibrary(url, loginResp.User.Token, libraryID) | ||
assert.NoError(t, err) | ||
assert.NotNil(t, libraryResponse) | ||
assert.NotEmpty(t, libraryResponse.Libraries) | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
package audiobookshelf | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
) | ||
|
||
// Call the Audiobookshelf API login method. | ||
func Login(absUrl string, username, password string) (LoginResponse, error) { | ||
requestBody := LoginRequest{ | ||
Username: username, | ||
Password: password, | ||
} | ||
|
||
requestBodyBytes, err := json.Marshal(requestBody) | ||
if err != nil { | ||
return LoginResponse{}, fmt.Errorf("failed to marshal login response body: %v", err) | ||
} | ||
|
||
resp, err := http.Post(absUrl, "application/json", bytes.NewBuffer(requestBodyBytes)) | ||
if err != nil { | ||
return LoginResponse{}, fmt.Errorf("failed to make login API call: %v", err) | ||
} | ||
defer resp.Body.Close() | ||
|
||
if resp.StatusCode != http.StatusOK { | ||
return LoginResponse{}, fmt.Errorf("login API call returned status code: %d", resp.StatusCode) | ||
} | ||
|
||
var loginResp LoginResponse | ||
err = json.NewDecoder(resp.Body).Decode(&loginResp) | ||
if err != nil { | ||
return LoginResponse{}, fmt.Errorf("failed to decode login response: %v", err) | ||
} | ||
|
||
return loginResp, nil | ||
} | ||
|
||
// Call the Audiobookshelf API libraries method | ||
func Libraries(url string, token string) (LibrariesResponse, error) { | ||
|
||
client := &http.Client{} | ||
url = url + "/api/libraries" | ||
req, err := http.NewRequest("GET", url, nil) | ||
if err != nil { | ||
return LibrariesResponse{}, fmt.Errorf("error creating request: %v", err) | ||
} | ||
|
||
req.Header.Add("Authorization", "Bearer "+token) | ||
|
||
resp, err := client.Do(req) | ||
if err != nil { | ||
return LibrariesResponse{}, fmt.Errorf("error sending request: %v", err) | ||
} | ||
defer resp.Body.Close() | ||
|
||
body, err := ioutil.ReadAll(resp.Body) | ||
if err != nil { | ||
return LibrariesResponse{}, fmt.Errorf("error reading response body: %v", err) | ||
} | ||
|
||
var response LibrariesResponse | ||
err = json.Unmarshal(body, &response) | ||
if err != nil { | ||
return LibrariesResponse{}, fmt.Errorf("error parsing response body: %v", err) | ||
} | ||
|
||
return response, nil | ||
} | ||
|
||
func GetLibraryByName(libraries []Library, libraryName string) (libraryID string, err error) { | ||
|
||
for _, library := range libraries { | ||
if library.Name == libraryName { | ||
return library.ID, nil | ||
} | ||
} | ||
return "", fmt.Errorf("no library with name '%s' found", libraryName) | ||
} | ||
|
||
// Call the Audiobookshelf API | ||
func ScanLibrary(url string, authToken string, libraryID string) error { | ||
url = url + "/api/libraries/" + libraryID + "/scan" | ||
req, err := http.NewRequest("POST", url, nil) | ||
if err != nil { | ||
return fmt.Errorf("error creating request: %v", err) | ||
} | ||
req.Header.Set("Authorization", "Bearer "+authToken) | ||
|
||
client := &http.Client{} | ||
resp, err := client.Do(req) | ||
if err != nil { | ||
return fmt.Errorf("error sending request: %v", err) | ||
} | ||
defer resp.Body.Close() | ||
|
||
if resp.StatusCode == http.StatusForbidden { | ||
return fmt.Errorf("an admin user is required to start a scan") | ||
} else if resp.StatusCode == http.StatusNotFound { | ||
return fmt.Errorf("the user cannot access the library or no library with the provided ID exists") | ||
} else { | ||
return nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package audiobookshelf | ||
|
||
type LoginRequest struct { | ||
Username string `json:"username"` | ||
Password string `json:"password"` | ||
} | ||
|
||
type LoginResponse struct { | ||
User User `json:"user"` | ||
UserDefaultLibraryID string `json:"userDefaultLibraryId"` | ||
ServerSettings ServerSettings `json:"serverSettings"` | ||
Source string `json:"Source"` | ||
} | ||
|
||
type User struct { | ||
ID string `json:"id"` | ||
Username string `json:"username"` | ||
Type string `json:"type"` | ||
Token string `json:"token"` | ||
MediaProgress []MediaProgress `json:"mediaProgress"` | ||
SeriesHideFromContinueListening []interface{} `json:"seriesHideFromContinueListening"` | ||
Bookmarks []interface{} `json:"bookmarks"` | ||
IsActive bool `json:"isActive"` | ||
IsLocked bool `json:"isLocked"` | ||
LastSeen int64 `json:"lastSeen"` | ||
CreatedAt int64 `json:"createdAt"` | ||
Permissions Permissions `json:"permissions"` | ||
LibrariesAccessible []interface{} `json:"librariesAccessible"` | ||
ItemTagsAccessible []interface{} `json:"itemTagsAccessible"` | ||
} | ||
|
||
type MediaProgress struct { | ||
ID string `json:"id"` | ||
LibraryItemID string `json:"libraryItemId"` | ||
EpisodeID string `json:"episodeId"` | ||
Duration float64 `json:"duration"` | ||
Progress float64 `json:"progress"` | ||
CurrentTime float64 `json:"currentTime"` | ||
IsFinished bool `json:"isFinished"` | ||
HideFromContinueListening bool `json:"hideFromContinueListening"` | ||
LastUpdate int64 `json:"lastUpdate"` | ||
StartedAt int64 `json:"startedAt"` | ||
FinishedAt *int64 `json:"finishedAt"` | ||
} | ||
|
||
type Permissions struct { | ||
Download bool `json:"download"` | ||
Update bool `json:"update"` | ||
Delete bool `json:"delete"` | ||
Upload bool `json:"upload"` | ||
AccessAllLibraries bool `json:"accessAllLibraries"` | ||
AccessAllTags bool `json:"accessAllTags"` | ||
AccessExplicitContent bool `json:"accessExplicitContent"` | ||
} | ||
|
||
type ServerSettings struct { | ||
ID string `json:"id"` | ||
ScannerFindCovers bool `json:"scannerFindCovers"` | ||
ScannerCoverProvider string `json:"scannerCoverProvider"` | ||
ScannerParseSubtitle bool `json:"scannerParseSubtitle"` | ||
ScannerPreferAudioMetadata bool `json:"scannerPreferAudioMetadata"` | ||
ScannerPreferOpfMetadata bool `json:"scannerPreferOpfMetadata"` | ||
ScannerPreferMatchedMetadata bool `json:"scannerPreferMatchedMetadata"` | ||
ScannerDisableWatcher bool `json:"scannerDisableWatcher"` | ||
ScannerPreferOverdriveMediaMarker bool `json:"scannerPreferOverdriveMediaMarker"` | ||
ScannerUseSingleThreadedProber bool `json:"scannerUseSingleThreadedProber"` | ||
ScannerMaxThreads int `json:"scannerMaxThreads"` | ||
ScannerUseTone bool `json:"scannerUseTone"` | ||
StoreCoverWithItem bool `json:"storeCoverWithItem"` | ||
StoreMetadataWithItem bool `json:"storeMetadataWithItem"` | ||
MetadataFileFormat string `json:"metadataFileFormat"` | ||
RateLimitLoginRequests int `json:"rateLimitLoginRequests"` | ||
RateLimitLoginWindow int `json:"rateLimitLoginWindow"` | ||
BackupSchedule string `json:"backupSchedule"` | ||
BackupsToKeep int `json:"backupsToKeep"` | ||
MaxBackupSize int `json:"maxBackupSize"` | ||
BackupMetadataCovers bool `json:"backupMetadataCovers"` | ||
LoggerDailyLogsToKeep int `json:"loggerDailyLogsToKeep"` | ||
LoggerScannerLogsToKeep int `json:"loggerScannerLogsToKeep"` | ||
HomeBookshelfView int `json:"homeBookshelfView"` | ||
BookshelfView int `json:"bookshelfView"` | ||
SortingIgnorePrefix bool `json:"sortingIgnorePrefix"` | ||
SortingPrefixes []string `json:"sortingPrefixes"` | ||
ChromecastEnabled bool `json:"chromecastEnabled"` | ||
DateFormat string `json:"dateFormat"` | ||
Language string `json:"language"` | ||
LogLevel int `json:"logLevel"` | ||
Version string `json:"version"` | ||
} | ||
|
||
type LibrariesResponse struct { | ||
Libraries []Library `json:"libraries"` | ||
} | ||
|
||
type Library struct { | ||
ID string `json:"id"` | ||
Name string `json:"name"` | ||
Folders []Folder `json:"folders"` | ||
DisplayOrder int `json:"displayOrder"` | ||
Icon string `json:"icon"` | ||
MediaType string `json:"mediaType"` | ||
Provider string `json:"provider"` | ||
Settings LibrarySettings `json:"settings"` | ||
CreatedAt int64 `json:"createdAt"` | ||
LastUpdate int64 `json:"lastUpdate"` | ||
} | ||
|
||
type Folder struct { | ||
ID string `json:"id"` | ||
FullPath string `json:"fullPath"` | ||
LibraryID string `json:"libraryId"` | ||
AddedAt int64 `json:"addedAt,omitempty"` | ||
} | ||
|
||
type LibrarySettings struct { | ||
CoverAspectRatio float64 `json:"coverAspectRatio"` | ||
DisableWatcher bool `json:"disableWatcher"` | ||
SkipMatchingMediaWithASIN bool `json:"skipMatchingMediaWithAsin"` | ||
SkipMatchingMediaWithISBN bool `json:"skipMatchingMediaWithIsbn"` | ||
AutoScanCronExpression string `json:"autoScanCronExpression,omitempty"` | ||
} |