├── aiopyarr ├── py.typed ├── models │ ├── __init__.py │ ├── const.py │ ├── host_configuration.py │ └── base.py ├── __init__.py ├── exceptions.py └── const.py ├── .gitattributes ├── MANIFEST.in ├── tests ├── fixtures │ ├── common │ │ ├── restart.json │ │ ├── shutdown.json │ │ ├── tag.json │ │ ├── language.json │ │ ├── validation.json │ │ ├── filesystem-mediafiles.json │ │ ├── remotepathmapping.json │ │ ├── diskspace.json │ │ ├── system-backup.json │ │ ├── log-file.json │ │ ├── queue-status.json │ │ ├── exclusions.json │ │ ├── health.json │ │ ├── config-indexer.json │ │ ├── filesystem.json │ │ ├── rootfolder.json │ │ ├── customfilter.json │ │ ├── downloadclientconfig.json │ │ ├── validation-failed.json │ │ ├── delayprofile.json │ │ ├── initialize.js │ │ ├── qualitydefinition.json │ │ ├── system-task.json │ │ ├── releaseprofile.json │ │ ├── config-ui.json │ │ ├── logs.json │ │ ├── update.json │ │ ├── metadata.json │ │ ├── indexer.json │ │ ├── downloadclient.json │ │ ├── system-status.json │ │ ├── command.json │ │ ├── config-host.json │ │ ├── qualityprofile.json │ │ ├── commands.json │ │ └── config-mediamanagement.json │ ├── radarr │ │ ├── indexerflag.json │ │ ├── restriction.json │ │ ├── rename.json │ │ ├── extrafile.json │ │ ├── tag-detail.json │ │ ├── alttitle.json │ │ ├── config-naming.json │ │ ├── credit.json │ │ ├── importlist.json │ │ ├── notification.json │ │ ├── moviefile.json │ │ ├── moviefile-list.json │ │ ├── blocklist-movie.json │ │ ├── importlistmovie.json │ │ ├── history-movie.json │ │ ├── blocklist.json │ │ ├── release-push.json │ │ ├── release.json │ │ ├── queue-details.json │ │ ├── history.json │ │ ├── queue.json │ │ ├── queue-2.json │ │ ├── manualimport.json │ │ ├── calendar.json │ │ ├── movie.json │ │ ├── movie-list.json │ │ ├── movie-import.json │ │ └── parse.json │ ├── lidarr │ │ ├── config-metadataprovider.json │ │ ├── rename.json │ │ ├── tag-detail.json │ │ ├── retag.json │ │ ├── rootfolder.json │ │ ├── track.json │ │ ├── metadata-profile.json │ │ ├── trackfile.json │ │ ├── parse.json │ │ ├── queue-2.json │ │ ├── release.json │ │ ├── search.json │ │ ├── trackfile-details.json │ │ ├── track-details.json │ │ ├── importlist.json │ │ ├── blocklist.json │ │ ├── queue.json │ │ ├── album.json │ │ ├── album-lookup.json │ │ ├── calendar.json │ │ ├── artist.json │ │ ├── history.json │ │ ├── queue-details.json │ │ └── manualimport.json │ ├── readarr │ │ ├── rename.json │ │ ├── config-development.json │ │ ├── config-metadataprovider.json │ │ ├── tag-detail.json │ │ ├── importlistoptions.json │ │ ├── series.json │ │ ├── config-naming.json │ │ ├── retag.json │ │ ├── metadata-profile.json │ │ ├── rootfolder.json │ │ ├── importlist.json │ │ ├── notification.json │ │ ├── release.json │ │ ├── author-lookup.json │ │ ├── search.json │ │ ├── history.json │ │ ├── book-file.json │ │ ├── queue.json │ │ ├── queue-2.json │ │ ├── book-lookup.json │ │ ├── wanted-missing.json │ │ ├── queue-details.json │ │ └── manualimport.json │ └── sonarr │ │ ├── rename.json │ │ ├── tag-detail.json │ │ ├── languageprofile.json │ │ ├── episodemonitor.json │ │ ├── calendar.json │ │ ├── config-naming.json │ │ ├── wantedmissing.json │ │ ├── blocklist.json │ │ ├── importlist.json │ │ ├── episodefile.json │ │ ├── queue-2.json │ │ ├── notification.json │ │ ├── series-lookup.json │ │ ├── queue-details.json │ │ ├── history.json │ │ ├── series.json │ │ ├── release.json │ │ ├── wantedmissing-extended.json │ │ ├── queue.json │ │ ├── manualimport.json │ │ ├── episode.json │ │ ├── parse.json │ │ └── calendar-extended.json ├── __init__.py └── conftest.py ├── .vscode └── settings.json ├── mypy.ini ├── .github ├── release-drafter.yml ├── dependabot.yml ├── ISSUE_TEMPLATE │ ├── feature_request.yml │ └── bug.yml ├── workflows │ ├── release.yml │ ├── release-drafter.yml │ └── actions.yml └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .coveragerc ├── requirements.txt ├── .codecov.yml ├── setup.cfg ├── example.py ├── .devcontainer └── devcontainer.json ├── pyproject.toml ├── Makefile ├── LICENSE ├── setup.py └── README.md /aiopyarr/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf -------------------------------------------------------------------------------- /aiopyarr/models/__init__.py: -------------------------------------------------------------------------------- 1 | """PyArr models.""" 2 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | exclude tests/* 3 | exclude pyproject.toml -------------------------------------------------------------------------------- /tests/fixtures/common/restart.json: -------------------------------------------------------------------------------- 1 | { 2 | "restarting": true 3 | } 4 | -------------------------------------------------------------------------------- /tests/fixtures/common/shutdown.json: -------------------------------------------------------------------------------- 1 | { 2 | "shuttingDown": true 3 | } 4 | -------------------------------------------------------------------------------- /tests/fixtures/common/tag.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "amzn", 3 | "id": 1 4 | } 5 | -------------------------------------------------------------------------------- /tests/fixtures/common/language.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": -1, 3 | "name": "Any", 4 | "nameLower": "any" 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.analysis.extraPaths": ["./venv/lib/python3.10/site-packages"], 3 | } 4 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | disable_error_code = misc 3 | 4 | # For permitting unordered defaults in dataclasses when init is disabled 5 | -------------------------------------------------------------------------------- /tests/fixtures/common/validation.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "isValid": true, 5 | "validationFailures": [] 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/indexerflag.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "name": "String", 5 | "nameLower": "string" 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/restriction.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "required": "string", 4 | "ignored": "string", 5 | "tags": [0], 6 | "id": 0 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /tests/fixtures/common/filesystem-mediafiles.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "path": "string", 4 | "relativePath": "string", 5 | "name": "string" 6 | } 7 | ] 8 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/config-metadataprovider.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadataSource": "", 3 | "writeAudioTags": "no", 4 | "scrubAudioTags": false, 5 | "id": 1 6 | } 7 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/rename.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "movieId": 0, 4 | "movieFileId": 0, 5 | "existingPath": "string", 6 | "newPath": "string" 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /tests/fixtures/common/remotepathmapping.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "host": "localhost", 4 | "remotePath": "C:\\", 5 | "localPath": "A:\\Movies\\", 6 | "id": 1 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /tests/fixtures/common/diskspace.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "path": "D:\\", 4 | "label": "DrivePool", 5 | "freeSpace": 16187217043456, 6 | "totalSpace": 56009755148288 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/rename.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "authorId": 0, 4 | "bookId": 0, 5 | "bookFileId": 0, 6 | "existingPath": "string", 7 | "newPath": "string" 8 | } 9 | ] 10 | -------------------------------------------------------------------------------- /tests/fixtures/common/system-backup.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "name": "string", 5 | "path": "string", 6 | "type": "scheduled", 7 | "time": "2021-12-09T13:22:49.441Z" 8 | } 9 | ] -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | change-template: '- #$NUMBER $TITLE @$AUTHOR' 2 | sort-direction: ascending 3 | exclude-labels: 4 | - "release-drafter-ignore" 5 | template: | 6 | ## What’s Changed 7 | 8 | $CHANGES -------------------------------------------------------------------------------- /tests/fixtures/common/log-file.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "filename": "string", 4 | "lastWriteTime": "2021-12-09T23:19:21Z", 5 | "contentsUrl": "string", 6 | "downloadUrl": "string", 7 | "id": 0 8 | } 9 | ] -------------------------------------------------------------------------------- /tests/fixtures/readarr/config-development.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadataSource": "string", 3 | "consoleLogLevel": "string", 4 | "logSql": false, 5 | "logRotate": 0, 6 | "filterSentryEvents": true, 7 | "id": 0 8 | } 9 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/config-metadataprovider.json: -------------------------------------------------------------------------------- 1 | { 2 | "writeAudioTags": "no", 3 | "scrubAudioTags": false, 4 | "writeBookTags": "newFiles", 5 | "updateCovers": true, 6 | "embedMetadata": false, 7 | "id": 1 8 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .eggs 3 | include 4 | Lib 5 | Scripts 6 | venv 7 | pyvenv.cfg 8 | .pytest_cache 9 | *egg-info* 10 | RESPONSES 11 | coverage.xml 12 | build 13 | dist 14 | .coverage 15 | .DS_Store 16 | .mypy_cache -------------------------------------------------------------------------------- /tests/fixtures/common/queue-status.json: -------------------------------------------------------------------------------- 1 | { 2 | "totalCount": 0, 3 | "count": 0, 4 | "unknownCount": 0, 5 | "errors": true, 6 | "warnings": true, 7 | "unknownErrors": true, 8 | "unknownWarnings": true 9 | } 10 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/extrafile.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "movieId": 0, 4 | "movieFileId": 0, 5 | "relativePath": "string", 6 | "extension": ".srt", 7 | "type": "subtitle", 8 | "id": 0 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/rename.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "artistId": 0, 4 | "albumId": 0, 5 | "trackNumbers": [0], 6 | "trackFileId": 0, 7 | "existingPath": "string", 8 | "newPath": "string" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/tag-detail.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "string", 3 | "delayProfileIds": [0], 4 | "importListIds": [0], 5 | "notificationIds": [0], 6 | "restrictionIds": [0], 7 | "artistIds": [0], 8 | "id": 0 9 | } 10 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/tag-detail.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "string", 3 | "delayProfileIds": [0], 4 | "importListIds": [0], 5 | "notificationIds": [0], 6 | "restrictionIds": [0], 7 | "authorIds": [0], 8 | "id": 0 9 | } 10 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/rename.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "seriesId": 0, 4 | "seasonNumber": 0, 5 | "episodeNumbers": [0], 6 | "episodeFileId": 0, 7 | "existingPath": "string", 8 | "newPath": "string" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/importlistoptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "options": [ 3 | { 4 | "value": 3020, 5 | "name": "Audio/Video", 6 | "order": 0, 7 | "hint": "(3020)", 8 | "parentValue": 3000 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = aiopyarr 3 | 4 | omit = 5 | # omit setup 6 | setup.py 7 | 8 | # omit tests 9 | tests/* 10 | 11 | # omit example 12 | example.py 13 | 14 | [report] 15 | exclude_lines = 16 | if TYPE_CHECKING: -------------------------------------------------------------------------------- /tests/fixtures/radarr/tag-detail.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 0, 3 | "label": "string", 4 | "delayProfileIds": [0], 5 | "notificationIds": [0], 6 | "restrictionIds": [0], 7 | "importListIds": [0], 8 | "movieIds": [0], 9 | "indexerIds": [0] 10 | } 11 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/tag-detail.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "string", 3 | "delayProfileIds": [0], 4 | "importListIds": [0], 5 | "notificationIds": [0], 6 | "restrictionIds": [0], 7 | "indexerIds": [0], 8 | "seriesIds": [0], 9 | "id": 0 10 | } 11 | -------------------------------------------------------------------------------- /tests/fixtures/common/exclusions.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 0, 3 | "foreignId": "string", 4 | "artistName": "string", 5 | "authorName": "string", 6 | "title": "string", 7 | "tvdbId": 0, 8 | "tmdbId": 0, 9 | "movieTitle": "string", 10 | "movieYear": 0 11 | } 12 | -------------------------------------------------------------------------------- /tests/fixtures/common/health.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "source": "ImportMechanismCheck", 4 | "type": "warning", 5 | "message": "Enable Completed Download Handling", 6 | "wikiUrl": "https://wiki.servarr.com/radarr/system#completed-failed-download-handling" 7 | } 8 | ] 9 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp>=3.6.1,<4.0 2 | aresponses>=2.1.4 3 | black>=21.11b1 4 | ciso8601>=2.2.0 5 | isort>=5.10.1 6 | flake8>=4.0.1 7 | flake8-docstrings>=1.6.0 8 | mypy>=0.910 9 | orjson>=3.7.11 10 | pylint>=2.12.1 11 | pytest-cov>=3.0.0 12 | pytest-asyncio>=0.16.0 13 | pytest>=6.2.4 14 | -------------------------------------------------------------------------------- /tests/fixtures/common/config-indexer.json: -------------------------------------------------------------------------------- 1 | { 2 | "minimumAge": 0, 3 | "maximumSize": 0, 4 | "retention": 0, 5 | "rssSyncInterval": 360, 6 | "preferIndexerFlags": true, 7 | "availabilityDelay": 0, 8 | "allowHardcodedSubs": false, 9 | "whitelistedHardcodedSubs": "", 10 | "id": 1 11 | } 12 | -------------------------------------------------------------------------------- /tests/fixtures/common/filesystem.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "string", 3 | "directories": [ 4 | { 5 | "type": "folder", 6 | "name": "app", 7 | "path": "/app/", 8 | "size": 0, 9 | "lastModified": "2020-01-04T03:02:20Z" 10 | } 11 | ], 12 | "files": [] 13 | } 14 | -------------------------------------------------------------------------------- /tests/fixtures/common/rootfolder.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "path": "C:\\Downloads\\Movies", 4 | "accessible": true, 5 | "freeSpace": 282500063232, 6 | "unmappedFolders": [ 7 | { 8 | "name": "string", 9 | "path": "path" 10 | } 11 | ], 12 | "id": 1 13 | } 14 | ] 15 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/alttitle.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "sourceType": "tmdb", 4 | "movieId": 0, 5 | "title": "string", 6 | "sourceId": 0, 7 | "votes": 0, 8 | "voteCount": 0, 9 | "language": { 10 | "id": 0, 11 | "name": "English" 12 | }, 13 | "id": 0 14 | } 15 | ] 16 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/config-naming.json: -------------------------------------------------------------------------------- 1 | { 2 | "renameMovies": true, 3 | "replaceIllegalCharacters": true, 4 | "colonReplacementFormat": "string", 5 | "standardMovieFormat": "string", 6 | "movieFolderFormat": "string", 7 | "includeQuality": true, 8 | "replaceSpaces": true, 9 | "id": 0 10 | } 11 | -------------------------------------------------------------------------------- /tests/fixtures/common/customfilter.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "string", 4 | "label": "string", 5 | "filters": [ 6 | { 7 | "key": "string", 8 | "value": [ 9 | "string" 10 | ], 11 | "type": "string" 12 | } 13 | ], 14 | "id": 10 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /tests/fixtures/common/downloadclientconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "downloadClientWorkingFolders": "_UNPACK_|_FAILED_", 3 | "checkForFinishedDownloadInterval": 10, 4 | "enableCompletedDownloadHandling": true, 5 | "removeCompletedDownloads": false, 6 | "autoRedownloadFailed": true, 7 | "removeFailedDownloads": true, 8 | "id": 1 9 | } -------------------------------------------------------------------------------- /tests/fixtures/common/validation-failed.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "isValid": false, 5 | "validationFailures": [ 6 | { 7 | "isWarning": false, 8 | "propertyName": "string", 9 | "errorMessage": "string", 10 | "severity": "string" 11 | } 12 | ] 13 | } 14 | ] 15 | -------------------------------------------------------------------------------- /tests/fixtures/common/delayprofile.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "enableUsenet": true, 4 | "enableTorrent": true, 5 | "preferredProtocol": "unknown", 6 | "usenetDelay": 0, 7 | "torrentDelay": 0, 8 | "bypassIfHighestQuality": true, 9 | "order": 2147483647, 10 | "tags": [0], 11 | "id": 0 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/series.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "string", 4 | "description": "string", 5 | "links": [ 6 | { 7 | "position": "string", 8 | "seriesPosition": 0, 9 | "seriesId": 0, 10 | "bookId": 0, 11 | "id": 0 12 | } 13 | ], 14 | "id": 0 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /tests/fixtures/common/initialize.js: -------------------------------------------------------------------------------- 1 | window.Radarr = { 2 | apiRoot: '/api/v3', 3 | apiKey: '1234567890abcdef1234567890abcdef', 4 | release: '4.0.3.5849-develop', 5 | version: '4.0.3.5849', 6 | branch: 'nightly', 7 | analytics: true, 8 | userHash: 'abcd1234', 9 | urlBase: '', 10 | isProduction: true 11 | }; -------------------------------------------------------------------------------- /tests/fixtures/readarr/config-naming.json: -------------------------------------------------------------------------------- 1 | { 2 | "renameBooks": false, 3 | "replaceIllegalCharacters": true, 4 | "standardBookFormat": "string", 5 | "authorFolderFormat": "string", 6 | "includeAuthorName": false, 7 | "includeBookTitle": false, 8 | "includeQuality": false, 9 | "replaceSpaces": false, 10 | "id": 1 11 | } 12 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/retag.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "artistId": 0, 4 | "albumId": 0, 5 | "trackNumbers": [0], 6 | "trackFileId": 0, 7 | "path": "string", 8 | "changes": [ 9 | { 10 | "field": "string", 11 | "oldValue": "string", 12 | "newValue": "string" 13 | } 14 | ] 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/retag.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "authorId": 0, 4 | "bookId": 0, 5 | "trackNumbers": [0], 6 | "bookFileId": 0, 7 | "path": "string", 8 | "changes": [ 9 | { 10 | "field": "string", 11 | "oldValue": "string", 12 | "newValue": "string" 13 | } 14 | ] 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /tests/fixtures/common/qualitydefinition.json: -------------------------------------------------------------------------------- 1 | { 2 | "quality": { 3 | "id": 0, 4 | "name": "string", 5 | "source": "string", 6 | "resolution": 0, 7 | "modifier": "string" 8 | }, 9 | "title": "string", 10 | "weight": 0, 11 | "minSize": 0, 12 | "maxSize": 0.0, 13 | "preferredSize": 0, 14 | "id": 0 15 | } 16 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "06:00" 8 | open-pull-requests-limit: 10 9 | - package-ecosystem: "github-actions" 10 | directory: "/" 11 | schedule: 12 | interval: daily 13 | time: "06:00" 14 | open-pull-requests-limit: 10 -------------------------------------------------------------------------------- /tests/fixtures/readarr/metadata-profile.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "name": "string", 5 | "minPopularity": 0, 6 | "skipMissingDate": true, 7 | "skipMissingIsbn": true, 8 | "skipPartsAndSets": true, 9 | "skipSeriesSecondary": true, 10 | "allowedLanguages": "string", 11 | "minPages": 0, 12 | "ignored": "string" 13 | } 14 | ] -------------------------------------------------------------------------------- /tests/fixtures/common/system-task.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "string", 4 | "taskName": "string", 5 | "interval": 0, 6 | "lastExecution": "2020-02-08T14:24:40.993044Z", 7 | "lastStartTime": "2020-02-08T14:24:40.993044Z", 8 | "nextExecution": "2020-02-08T20:24:40.993044Z", 9 | "lastDuration": "00:00:00.1976902", 10 | "id": 0 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | comment: false 2 | codecov: 3 | branch: master 4 | 5 | coverage: 6 | precision: 2 7 | status: 8 | patch: off 9 | project: 10 | default: 11 | target: 100% 12 | 13 | parsers: 14 | gcov: 15 | branch_detection: 16 | conditional: yes 17 | loop: yes 18 | method: no 19 | macro: no 20 | 21 | ignore: 22 | - "tests" -------------------------------------------------------------------------------- /tests/fixtures/common/releaseprofile.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "enabled": true, 4 | "required": "string", 5 | "ignored": "string", 6 | "preferred": [ 7 | { 8 | "key": "string", 9 | "value": 0 10 | } 11 | ], 12 | "includePreferredWhenRenaming": false, 13 | "indexerId": 0, 14 | "tags": [0], 15 | "id": 0 16 | } 17 | ] 18 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/credit.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "personName": "string", 4 | "creditTmdbId": "string", 5 | "personTmdbId": 0, 6 | "movieId": 0, 7 | "images": [ 8 | { 9 | "coverType": "headshot", 10 | "url": "string" 11 | } 12 | ], 13 | "character": "string", 14 | "order": 0, 15 | "type": "cast", 16 | "id": 0 17 | } 18 | ] 19 | -------------------------------------------------------------------------------- /aiopyarr/__init__.py: -------------------------------------------------------------------------------- 1 | """An asynchronous client for Lidarr/Radarr/Readarr/Sonarr APIs.""" 2 | 3 | from .exceptions import * # noqa: F401, F403 4 | from .models.lidarr import * # noqa: F401, F403 5 | from .models.radarr import * # noqa: F401, F403 6 | from .models.readarr import * # noqa: F401, F403 7 | from .models.request import * # noqa: F401, F403 8 | from .models.sonarr import * # noqa: F401, F403 9 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/languageprofile.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "string", 4 | "upgradeAllowed": true, 5 | "cutoff": { 6 | "id": 0, 7 | "name": "string" 8 | }, 9 | "languages": [ 10 | { 11 | "language": { 12 | "id": 0, 13 | "name": "string" 14 | }, 15 | "allowed": false 16 | } 17 | ], 18 | "id": 0 19 | } 20 | ] 21 | -------------------------------------------------------------------------------- /tests/fixtures/common/config-ui.json: -------------------------------------------------------------------------------- 1 | { 2 | "firstDayOfWeek": 0, 3 | "calendarWeekColumnHeader": "ddd M/D", 4 | "movieRuntimeFormat": "hoursMinutes", 5 | "shortDateFormat": "MMM D YYYY", 6 | "longDateFormat": "dddd, MMMM D YYYY", 7 | "timeFormat": "h(:mm)a", 8 | "showRelativeDates": true, 9 | "enableColorImpairedMode": false, 10 | "movieInfoLanguage": 1, 11 | "uiLanguage": 1, 12 | "id": 1 13 | } 14 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/rootfolder.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "music", 4 | "path": "/music/", 5 | "defaultMetadataProfileId": 1, 6 | "defaultQualityProfileId": 1, 7 | "defaultMonitorOption": "all", 8 | "defaultNewItemMonitorOption": "all", 9 | "defaultTags": [], 10 | "accessible": true, 11 | "freeSpace": 1000000000, 12 | "totalSpace": 500000000000, 13 | "id": 2 14 | } 15 | ] -------------------------------------------------------------------------------- /tests/fixtures/lidarr/track.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "artistId": 0, 4 | "trackFileId": 0, 5 | "albumId": 0, 6 | "explicit": false, 7 | "absoluteTrackNumber": 0, 8 | "trackNumber": "0", 9 | "title": "string", 10 | "duration": 0, 11 | "mediumNumber": 0, 12 | "hasFile": false, 13 | "ratings": { 14 | "votes": 0, 15 | "value": 0.0 16 | }, 17 | "id": 0 18 | } 19 | ] 20 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/episodemonitor.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "seriesId": 0, 4 | "episodeFileId": 0, 5 | "seasonNumber": 0, 6 | "episodeNumber": 0, 7 | "title": "string", 8 | "airDate": "2020-10-24", 9 | "airDateUtc": "2020-10-24T05:00:00Z", 10 | "overview": "string", 11 | "hasFile": true, 12 | "monitored": false, 13 | "unverifiedSceneNumbering": false, 14 | "id": 0 15 | } 16 | ] 17 | -------------------------------------------------------------------------------- /tests/fixtures/common/logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 10, 4 | "sortKey": "logger", 5 | "sortDirection": "default", 6 | "totalRecords": 74433, 7 | "records": [ 8 | { 9 | "time": "2021-11-19T09:28:26.549994Z", 10 | "level": "info", 11 | "logger": "BackupService", 12 | "message": "Starting Backup", 13 | "id": 3920809, 14 | "exception": "string", 15 | "exceptionType": "string" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /tests/fixtures/common/update.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "version": "string", 4 | "branch": "string", 5 | "releaseDate": "2020-09-02T05:36:13.047313Z", 6 | "fileName": "string", 7 | "url": "string", 8 | "installed": false, 9 | "installedOn": "2020-10-01T05:01:04.521117Z", 10 | "installable": false, 11 | "latest": false, 12 | "changes": { 13 | "new": ["string"], 14 | "fixed": ["string"] 15 | }, 16 | "hash": "string" 17 | } 18 | ] 19 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [egg_info] 2 | tag_build = 3 | tag_date = 0 4 | 5 | [flake8] 6 | exclude = .venv,.git,.tox,docs,venv,bin,lib,deps,build 7 | max-complexity = 25 8 | doctests = True 9 | # To work with Black 10 | # E501: line too long 11 | # W503: Line break occurred before a binary operator 12 | # E203: Whitespace before ':' 13 | # D202 No blank lines allowed after function docstring 14 | # W504 line break after binary operator 15 | ignore = 16 | E501, 17 | W503, 18 | E203, 19 | D202, 20 | W504 21 | noqa-require-code = True -------------------------------------------------------------------------------- /tests/fixtures/readarr/rootfolder.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "books", 4 | "path": "/books/", 5 | "defaultMetadataProfileId": 1, 6 | "defaultQualityProfileId": 1, 7 | "defaultMonitorOption": "all", 8 | "defaultNewItemMonitorOption": "all", 9 | "defaultTags": [], 10 | "isCalibreLibrary": false, 11 | "port": 0, 12 | "outputProfile": "default", 13 | "useSsl": false, 14 | "accessible": true, 15 | "freeSpace": 1000000000, 16 | "totalSpace": 500000000000, 17 | "id": 1 18 | } 19 | ] 20 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/calendar.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "seriesId": 0, 4 | "episodeFileId": 0, 5 | "seasonNumber": 0, 6 | "episodeNumber": 0, 7 | "title": "string", 8 | "airDate": "2017-01-26", 9 | "airDateUtc": "2017-01-27T01:30:00Z", 10 | "overview": "string", 11 | "hasFile": false, 12 | "monitored": true, 13 | "sceneEpisodeNumber": 0, 14 | "sceneSeasonNumber": 0, 15 | "tvDbEpisodeId": 0, 16 | "unverifiedSceneNumbering": false, 17 | "downloading": false, 18 | "id": 0 19 | } 20 | ] 21 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/config-naming.json: -------------------------------------------------------------------------------- 1 | { 2 | "renameEpisodes": true, 3 | "replaceIllegalCharacters": true, 4 | "multiEpisodeStyle": 0, 5 | "standardEpisodeFormat": "string", 6 | "dailyEpisodeFormat": "string", 7 | "animeEpisodeFormat": "string", 8 | "seriesFolderFormat": "string", 9 | "seasonFolderFormat": "string", 10 | "specialsFolderFormat": "string", 11 | "includeSeriesTitle": false, 12 | "includeEpisodeTitle": false, 13 | "includeQuality": false, 14 | "replaceSpaces": true, 15 | "separator": "string", 16 | "numberStyle": "string", 17 | "id": 1 18 | } 19 | -------------------------------------------------------------------------------- /tests/fixtures/common/metadata.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "enable": true, 4 | "name": "string", 5 | "fields": [ 6 | { 7 | "order": 0, 8 | "name": "string", 9 | "label": "string", 10 | "helpText": "string", 11 | "value": "string", 12 | "type": "string", 13 | "advanced": true, 14 | "section": "metadata" 15 | } 16 | ], 17 | "implementationName": "string", 18 | "implementation": "string", 19 | "configContract": "string", 20 | "infoLink": "string", 21 | "tags": [ 22 | 0 23 | ], 24 | "id": 0 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | """Tests for PyArr.""" 2 | 3 | import pathlib 4 | 5 | from aiopyarr.models.host_configuration import PyArrHostConfiguration 6 | 7 | API_TOKEN = "1234567890abcdef1234567890abcdef" 8 | TEST_HOST_CONFIGURATION = PyArrHostConfiguration( 9 | api_token=API_TOKEN, ipaddress="127.0.0.1" 10 | ) 11 | LIDARR_API = "v1" 12 | RADARR_API = "v3" 13 | READARR_API = "v1" 14 | SONARR_API = "v3" 15 | 16 | 17 | def load_fixture(filename) -> str: 18 | """Load a fixture.""" 19 | return ( 20 | pathlib.Path(__file__) 21 | .parent.joinpath("fixtures", filename) 22 | .read_text(encoding="utf8") 23 | ) 24 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/wantedmissing.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 10, 4 | "sortKey": "airDateUtc", 5 | "sortDirection": "default", 6 | "totalRecords": 1, 7 | "records": [ 8 | { 9 | "seriesId": 0, 10 | "episodeFileId": 0, 11 | "seasonNumber": 0, 12 | "episodeNumber": 0, 13 | "title": "string", 14 | "airDate": "2010-03-07", 15 | "airDateUtc": "2010-03-07T05:00:00Z", 16 | "overview": "string", 17 | "hasFile": false, 18 | "monitored": true, 19 | "absoluteEpisodeNumber": 0, 20 | "unverifiedSceneNumbering": false, 21 | "id": 0 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | """Example usage of aiopyarr.""" 2 | 3 | import asyncio 4 | 5 | from aiopyarr.models.host_configuration import PyArrHostConfiguration 6 | from aiopyarr.radarr_client import RadarrClient 7 | 8 | IP = "192.168.100.3" 9 | TOKEN = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 10 | 11 | 12 | async def async_example(): 13 | """Example usage of aiopyarr.""" 14 | host_configuration = PyArrHostConfiguration(ipaddress=IP, api_token=TOKEN) 15 | async with RadarrClient(host_configuration=host_configuration) as client: 16 | print(await client.async_get_system_status()) 17 | 18 | 19 | asyncio.get_event_loop().run_until_complete(async_example()) 20 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aiopyarr", 3 | "context": "..", 4 | "dockerFile": "../Dockerfile.dev", 5 | "settings": { 6 | "terminal.integrated.shell.linux": "/bin/bash", 7 | "python.pythonPath": "/usr/local/bin/python", 8 | "python.linting.enabled": true, 9 | "python.linting.pylintEnabled": true, 10 | "python.linting.pylintPath": "/usr/local/bin/pylint", 11 | "python.formatting.provider": "black", 12 | "editor.formatOnPaste": false, 13 | "editor.formatOnSave": true, 14 | "editor.formatOnType": true, 15 | "files.trimTrailingWhitespace": true 16 | }, 17 | "extensions": [ 18 | "ms-python.python", 19 | "esbenp.prettier-vscode" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /tests/fixtures/common/indexer.json: -------------------------------------------------------------------------------- 1 | { 2 | "enableRss": true, 3 | "enableAutomaticSearch": true, 4 | "enableInteractiveSearch": true, 5 | "supportsRss": true, 6 | "supportsSearch": true, 7 | "protocol": "unknown", 8 | "priority": 0, 9 | "name": "string", 10 | "fields": [ 11 | { 12 | "order": 0, 13 | "name": "string", 14 | "label": "string", 15 | "helpText": "string", 16 | "value": "string", 17 | "type": "string", 18 | "advanced": true 19 | } 20 | ], 21 | "implementationName": "string", 22 | "implementation": "string", 23 | "configContract": "string", 24 | "infoLink": "string", 25 | "tags": [{}], 26 | "id": 0 27 | } 28 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/metadata-profile.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "string", 4 | "primaryAlbumTypes": [ 5 | { 6 | "albumType": { 7 | "id": 0, 8 | "name": "string" 9 | }, 10 | "allowed": false 11 | } 12 | ], 13 | "secondaryAlbumTypes": [ 14 | { 15 | "albumType": { 16 | "id": 0, 17 | "name": "string" 18 | }, 19 | "allowed": true 20 | } 21 | ], 22 | "releaseStatuses": [ 23 | { 24 | "releaseStatus": { 25 | "id": 0, 26 | "name": "string" 27 | }, 28 | "allowed": false 29 | } 30 | ], 31 | "id": 0 32 | } 33 | ] 34 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/trackfile.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "artistId": 0, 4 | "albumId": 0, 5 | "path": "string", 6 | "size": 0, 7 | "dateAdded": "2021-08-21T16:00:22.0108039Z", 8 | "quality": { 9 | "quality": { 10 | "id": 0, 11 | "name": "string" 12 | }, 13 | "revision": { 14 | "version": 0, 15 | "real": 0, 16 | "isRepack": false 17 | } 18 | }, 19 | "qualityWeight": 0, 20 | "mediaInfo": { 21 | "audioChannels": 0.0, 22 | "audioBitRate": "string", 23 | "audioCodec": "string", 24 | "audioBits": "string", 25 | "audioSampleRate": "string" 26 | }, 27 | "qualityCutoffNotMet": false, 28 | "id": 0 29 | } 30 | ] 31 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.pylint.MASTER] 2 | py-version = "3.10" 3 | load-plugins = [ 4 | "pylint.extensions.code_style", 5 | "pylint.extensions.typing", 6 | ] 7 | extension-pkg-allow-list = [ 8 | "ciso8601", 9 | "orjson", 10 | ] 11 | 12 | [tool.pylint."MESSAGES CONTROL"] 13 | # Reasons disabled: 14 | # duplicate-code - unavoidable 15 | disable = [ 16 | "duplicate-code", 17 | ] 18 | 19 | [tool.isort] 20 | # https://github.com/PyCQA/isort/wiki/isort-Settings 21 | profile = "black" 22 | # will group `import x` and `from x import` of the same module. 23 | force_sort_within_sections = true 24 | known_first_party = [ 25 | "aiopyarr", 26 | "tests", 27 | ] 28 | forced_separate = [ 29 | "tests", 30 | ] 31 | combine_as_imports = true 32 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/parse.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "string", 3 | "parsedAlbumInfo": { 4 | "albumTitle": "string", 5 | "artistName": "string", 6 | "artistTitleInfo": { 7 | "title": "string", 8 | "year": 0 9 | }, 10 | "quality": { 11 | "quality": { 12 | "id": 0, 13 | "name": "string" 14 | }, 15 | "revision": { 16 | "version": 0, 17 | "real": 0, 18 | "isRepack": false 19 | } 20 | }, 21 | "releaseDate": "0", 22 | "discography": false, 23 | "discographyStart": 0, 24 | "discographyEnd": 0, 25 | "releaseGroup": "string", 26 | "releaseHash": "string", 27 | "releaseVersion": "string" 28 | }, 29 | "artist": {}, 30 | "albums": ["test"] 31 | } 32 | -------------------------------------------------------------------------------- /tests/fixtures/common/downloadclient.json: -------------------------------------------------------------------------------- 1 | { 2 | "enable": true, 3 | "protocol": "unknown", 4 | "priority": 0, 5 | "name": "string", 6 | "fields": [ 7 | { 8 | "order": 0, 9 | "name": "string", 10 | "label": "string", 11 | "helpText": "string", 12 | "value": "string", 13 | "type": "string", 14 | "advanced": true, 15 | "selectOptions": [ 16 | { 17 | "value": 0, 18 | "name": "Last", 19 | "order": 0, 20 | "dividerAfter": false 21 | } 22 | ] 23 | } 24 | ], 25 | "implementationName": "string", 26 | "implementation": "string", 27 | "configContract": "string", 28 | "infoLink": "string", 29 | "tags": [ 30 | 0 31 | ], 32 | "id": 0 33 | } 34 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/blocklist.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 10, 4 | "sortKey": "date", 5 | "sortDirection": "descending", 6 | "totalRecords": 0, 7 | "records": [ 8 | { 9 | "seriesId": 0, 10 | "episodeIds": [0], 11 | "sourceTitle": "string", 12 | "language": { 13 | "id": 0, 14 | "name": "string" 15 | }, 16 | "quality": { 17 | "quality": { 18 | "id": 0, 19 | "name": "string", 20 | "source": "string", 21 | "resolution": 0 22 | }, 23 | "revision": { 24 | "version": 0, 25 | "real": 0, 26 | "isRepack": false 27 | } 28 | }, 29 | "date": "2021-09-19T08:14:33.582863Z", 30 | "protocol": "unknown", 31 | "indexer": "string", 32 | "message": "string", 33 | "id": 0 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := help 2 | 3 | help: ## Shows this help message 4 | @printf "\033[1m%s\033[36m %s\033[32m %s\033[0m \n\n" "Development environment for" "aiopyarr" ""; 5 | @awk 'BEGIN {FS = ":.*##";} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m make %-25s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST); 6 | @echo 7 | 8 | requirements: ## Install requirements 9 | @python3 -m pip --disable-pip-version-check install -r requirements.txt 10 | 11 | lint: ## Lint all files 12 | @isort . 13 | @python3 -m black --fast . 14 | @python3 -m pylint aiopyarr tests 15 | @python3 -m flake8 aiopyarr tests 16 | @python3 -m mypy aiopyarr 17 | 18 | coverage: ## Check the coverage of the package 19 | @python3 -m pytest tests --asyncio-mode=strict --cov=aiopyarr --cov-report term-missing -vv 20 | 21 | setup: ## Setup the package 22 | @python3 setup.py develop -------------------------------------------------------------------------------- /tests/fixtures/readarr/importlist.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "enableAutomaticAdd": true, 4 | "shouldMonitor": "all", 5 | "shouldMonitorExisting": false, 6 | "shouldSearch": false, 7 | "rootFolderPath": "string", 8 | "monitorNewItems": "string", 9 | "qualityProfileId": 0, 10 | "metadataProfileId": 0, 11 | "listType": "string", 12 | "listOrder": 0, 13 | "name": "string", 14 | "fields": [ 15 | { 16 | "order": 0, 17 | "name": "string", 18 | "label": "string", 19 | "helpText": "string", 20 | "value": ["string"], 21 | "type": "string", 22 | "advanced": false, 23 | "hidden": "string" 24 | } 25 | ], 26 | "implementationName": "string", 27 | "implementation": "string", 28 | "configContract": "string", 29 | "infoLink": "string", 30 | "tags": [0], 31 | "id": 0 32 | } 33 | ] 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Feature request" 3 | description: Suggest an idea for this project. 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: Before you open a new feature request, search through the existing issues to see if others have had the same problem. 8 | 9 | - type: textarea 10 | attributes: 11 | label: "Describe the solution you'd like" 12 | description: "A clear and concise description of what you want to happen.." 13 | validations: 14 | required: true 15 | 16 | - type: textarea 17 | attributes: 18 | label: "Describe alternatives you've considered" 19 | description: "A clear and concise description of any alternative solutions or features you've considered." 20 | validations: 21 | required: true 22 | 23 | - type: textarea 24 | attributes: 25 | label: "Additional context" 26 | description: "Add any other context or screenshots about the feature request here." 27 | -------------------------------------------------------------------------------- /tests/fixtures/common/system-status.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": "test", 3 | "instanceName": "test", 4 | "version": "10.0.0.34882", 5 | "buildTime": "2020-09-01T23:23:23.9621974Z", 6 | "isDebug": true, 7 | "isProduction": false, 8 | "isAdmin": false, 9 | "isUserInteractive": true, 10 | "startupPath": "C:\\ProgramData\\Radarr", 11 | "appData": "C:\\ProgramData\\Radarr", 12 | "osName": "Windows", 13 | "osVersion": "10.0.18363.0", 14 | "isNetCore": true, 15 | "isMono": false, 16 | "isMonoRuntime": false, 17 | "isLinux": false, 18 | "isOsx": false, 19 | "isWindows": true, 20 | "isDocker": false, 21 | "mode": "console", 22 | "branch": "nightly", 23 | "authentication": "none", 24 | "sqliteVersion": "3.32.1", 25 | "migrationVersion": 180, 26 | "urlBase": "", 27 | "runtimeVersion": "3.1.10", 28 | "runtimeName": "netCore", 29 | "startTime": "2020-09-01T23:50:20.2415965Z", 30 | "packageUpdateMechanism": "builtIn" 31 | } 32 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/importlist.json: -------------------------------------------------------------------------------- 1 | { 2 | "enabled": true, 3 | "enableAuto": true, 4 | "shouldMonitor": true, 5 | "rootFolderPath": "string", 6 | "qualityProfileId": 0, 7 | "searchOnAdd": true, 8 | "minimumAvailability": "string", 9 | "listType": "string", 10 | "listOrder": 0, 11 | "name": "string", 12 | "fields": [ 13 | { 14 | "order": 0, 15 | "name": "string", 16 | "label": "string", 17 | "helpText": "string", 18 | "value": "string", 19 | "type": "string", 20 | "advanced": true, 21 | "hidden": "string", 22 | "selectOptions": [ 23 | { 24 | "value": 0, 25 | "name": "string", 26 | "order": 0, 27 | "dividerAfter": false 28 | } 29 | ] 30 | } 31 | ], 32 | "implementationName": "string", 33 | "implementation": "string", 34 | "configContract": "string", 35 | "infoLink": "string", 36 | "tags": [0], 37 | "id": 0 38 | } 39 | -------------------------------------------------------------------------------- /tests/fixtures/common/command.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MessagingCleanup", 3 | "commandName": "Messaging Cleanup", 4 | "message": "Completed", 5 | "body": { 6 | "sendUpdatesToClient": false, 7 | "updateScheduledTask": true, 8 | "completionMessage": "Completed", 9 | "requiresDiskAccess": false, 10 | "isExclusive": false, 11 | "isTypeExclusive": false, 12 | "name": "MessagingCleanup", 13 | "lastExecutionTime": "2021-11-29T19:57:46Z", 14 | "lastStartTime": "2021-11-29T19:57:46Z", 15 | "trigger": "scheduled", 16 | "suppressMessages": false 17 | }, 18 | "priority": "low", 19 | "status": "completed", 20 | "queued": "2021-11-29T20:03:16Z", 21 | "started": "2021-11-29T20:03:16Z", 22 | "ended": "2021-11-29T20:03:16Z", 23 | "duration": "00:00:00.0102456", 24 | "trigger": "scheduled", 25 | "stateChangeTime": "2021-11-29T20:03:16Z", 26 | "sendUpdatesToClient": false, 27 | "updateScheduledTask": true, 28 | "lastExecutionTime": "2021-11-29T19:57:46Z", 29 | "id": 0 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: ["published"] 6 | 7 | jobs: 8 | deploy: 9 | runs-on: ubuntu-latest 10 | name: Deploy to PyPi 11 | steps: 12 | - name: 📥 Checkout the repository 13 | uses: actions/checkout@v3 14 | 15 | - name: 🛠 Set up Python ${{ matrix.python-version }} 16 | uses: actions/setup-python@v4.1.0 17 | id: python 18 | with: 19 | python-version: 3.x 20 | 21 | - name: 📦 Install dependencies 22 | run: python3 -m pip install setuptools wheel twine 23 | 24 | - name: 🔢 Set version number 25 | run: | 26 | export version=${{ github.ref }} 27 | sed -i "s|master|${version##*/}|" ./setup.py 28 | cat ./setup.py 29 | 30 | - name: 🚀 Publish to PyPi 31 | env: 32 | TWINE_USERNAME: __token__ 33 | TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} 34 | run: | 35 | python setup.py sdist bdist_wheel 36 | twine upload dist/* -------------------------------------------------------------------------------- /tests/fixtures/common/config-host.json: -------------------------------------------------------------------------------- 1 | { 2 | "bindAddress": "string", 3 | "port": 0, 4 | "sslPort": 0, 5 | "enableSsl": true, 6 | "launchBrowser": true, 7 | "authenticationMethod": "string", 8 | "analyticsEnabled": true, 9 | "username": "string", 10 | "password": "string", 11 | "logLevel": "string", 12 | "consoleLogLevel": "string", 13 | "branch": "string", 14 | "apiKey": "string", 15 | "sslCertHash": "string", 16 | "sslCertPath": "string", 17 | "sslCertPassword": "string", 18 | "urlBase": "string", 19 | "updateAutomatically": true, 20 | "updateMechanism": "docker", 21 | "updateScriptPath": "string", 22 | "proxyEnabled": true, 23 | "proxyType": "http", 24 | "proxyHostname": "string", 25 | "proxyPort": 0, 26 | "proxyUsername": "string", 27 | "proxyPassword": "string", 28 | "proxyBypassFilter": "string", 29 | "proxyBypassLocalAddresses": true, 30 | "certificateValidation": "disabled", 31 | "backupFolder": "string", 32 | "backupInterval": 0, 33 | "backupRetention": 0, 34 | "id": 0 35 | } 36 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/importlist.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "enableAutomaticAdd": true, 4 | "shouldMonitor": "string", 5 | "rootFolderPath": "string", 6 | "qualityProfileId": 0, 7 | "languageProfileId": 0, 8 | "seriesType": "string", 9 | "seasonFolder": true, 10 | "listType": "string", 11 | "listOrder": 0, 12 | "name": "string", 13 | "fields": [ 14 | { 15 | "order": 0, 16 | "name": "string", 17 | "label": "string", 18 | "helpText": "string", 19 | "value": "string", 20 | "type": "string", 21 | "advanced": false, 22 | "hidden": "string", 23 | "selectOptions": [ 24 | { 25 | "value": 0, 26 | "name": "string", 27 | "order": 0, 28 | "dividerAfter": false 29 | } 30 | ] 31 | } 32 | ], 33 | "implementationName": "string", 34 | "implementation": "string", 35 | "configContract": "string", 36 | "infoLink": "string", 37 | "tags": [0], 38 | "id": 0 39 | } 40 | ] 41 | -------------------------------------------------------------------------------- /tests/fixtures/common/qualityprofile.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "string", 4 | "upgradeAllowed": true, 5 | "cutoff": 0, 6 | "items": [ 7 | { 8 | "quality": { 9 | "id": 0, 10 | "name": "string", 11 | "source": "string", 12 | "resolution": 0, 13 | "modifier": "string" 14 | }, 15 | "items": [], 16 | "allowed": false 17 | }, 18 | { 19 | "name": "string", 20 | "items": [ 21 | { 22 | "quality": { 23 | "id": 0, 24 | "name": "string", 25 | "source": "string", 26 | "resolution": 0, 27 | "modifier": "string" 28 | }, 29 | "items": [], 30 | "allowed": true 31 | } 32 | ], 33 | "allowed": true, 34 | "id": 0 35 | } 36 | ], 37 | "minFormatScore": 0, 38 | "cutoffFormatScore": 0, 39 | "formatItems": [], 40 | "language": { 41 | "id": 0, 42 | "name": "string" 43 | }, 44 | "id": 0 45 | } 46 | ] 47 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/queue-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 20, 4 | "sortKey": "timeleft", 5 | "sortDirection": "ascending", 6 | "totalRecords": 51, 7 | "records": [ 8 | { 9 | "artistId": 0, 10 | "albumId": 0, 11 | "quality": { 12 | "quality": { 13 | "id": 6, 14 | "name": "FLAC" 15 | }, 16 | "revision": { 17 | "version": 1, 18 | "real": 0, 19 | "isRepack": false 20 | } 21 | }, 22 | "size": 20000, 23 | "title": "string", 24 | "sizeleft": 0, 25 | "status": "completed", 26 | "trackedDownloadStatus": "string", 27 | "trackedDownloadState": "string", 28 | "statusMessages": [ 29 | { 30 | "title": "string", 31 | "messages": ["string"] 32 | } 33 | ], 34 | "downloadId": "string", 35 | "protocol": "torrent", 36 | "downloadClient": "Transmission", 37 | "indexer": "string", 38 | "outputPath": "string", 39 | "downloadForced": false, 40 | "id": 0 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/notification.json: -------------------------------------------------------------------------------- 1 | { 2 | "onGrab": true, 3 | "onDownload": true, 4 | "onUpgrade": true, 5 | "onRename": true, 6 | "onHealthIssue": true, 7 | "supportsOnGrab": true, 8 | "supportsOnDownload": true, 9 | "supportsOnUpgrade": true, 10 | "supportsOnRename": true, 11 | "supportsOnHealthIssue": true, 12 | "includeHealthWarnings": true, 13 | "name": "string", 14 | "fields": [ 15 | { 16 | "order": 0, 17 | "name": "string", 18 | "label": "string", 19 | "helpText": "string", 20 | "value": "string", 21 | "type": "string", 22 | "advanced": true, 23 | "selectOptions": [ 24 | { 25 | "value": 0, 26 | "name": "string", 27 | "order": 0, 28 | "dividerAfter": false 29 | } 30 | ] 31 | } 32 | ], 33 | "implementationName": "string", 34 | "implementation": "string", 35 | "configContract": "string", 36 | "infoLink": "string", 37 | "message": { 38 | "message": "string", 39 | "type": "string" 40 | }, 41 | "tags": [ 42 | 0 43 | ], 44 | "id": 0 45 | } 46 | -------------------------------------------------------------------------------- /tests/fixtures/common/commands.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "MessagingCleanup", 4 | "commandName": "Messaging Cleanup", 5 | "message": "Completed", 6 | "body": { 7 | "sendUpdatesToClient": false, 8 | "updateScheduledTask": true, 9 | "completionMessage": "Completed", 10 | "requiresDiskAccess": false, 11 | "isExclusive": false, 12 | "isNewMovie": false, 13 | "isTypeExclusive": false, 14 | "name": "MessagingCleanup", 15 | "lastExecutionTime": "2021-11-29T19:57:46Z", 16 | "lastStartTime": "2021-11-29T19:57:46Z", 17 | "trigger": "scheduled", 18 | "suppressMessages": false 19 | }, 20 | "priority": "low", 21 | "status": "completed", 22 | "queued": "2021-11-29T20:03:16Z", 23 | "started": "2021-11-29T20:03:16Z", 24 | "ended": "2021-11-29T20:03:16Z", 25 | "duration": "00:00:00.0102456", 26 | "trigger": "scheduled", 27 | "stateChangeTime": "2021-11-29T20:03:16Z", 28 | "sendUpdatesToClient": false, 29 | "updateScheduledTask": true, 30 | "lastExecutionTime": "2021-11-29T19:57:46Z", 31 | "id": 0 32 | } 33 | ] -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Bug report" 3 | description: You use this when something is not doing what it's supposed to do. 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: Before you open a new issue, search through the existing issues to see if others have had the same problem. 8 | - type: checkboxes 9 | attributes: 10 | label: Checklist 11 | options: 12 | - label: I'm running the newest version 13 | required: true 14 | - label: I have filled out the issue template to the best of my ability. 15 | required: true 16 | - label: This issue only contains 1 issue (if you have multiple issues, open one issue for each issue). 17 | required: true 18 | - type: textarea 19 | attributes: 20 | label: "Describe the issue" 21 | description: "A clear and concise description of what the issue is." 22 | validations: 23 | required: true 24 | - type: textarea 25 | attributes: 26 | label: "Logs" 27 | description: "Paste your logs here" 28 | render: text 29 | validations: 30 | required: true 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Robert Hillis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /tests/fixtures/common/config-mediamanagement.json: -------------------------------------------------------------------------------- 1 | { 2 | "allowFingerprinting": "newFiles", 3 | "autoRenameFolders": true, 4 | "autoUnmonitorPreviouslyDownloadedBooks": false, 5 | "autoUnmonitorPreviouslyDownloadedEpisodes": false, 6 | "autoUnmonitorPreviouslyDownloadedMovies": false, 7 | "autoUnmonitorPreviouslyDownloadedTracks": false, 8 | "chmodFolder": "002", 9 | "chownGroup": "", 10 | "copyUsingHardlinks": false, 11 | "createEmptyAuthorFolders": false, 12 | "createEmptyMovieFolders": false, 13 | "createEmptySeriesFolders": false, 14 | "deleteEmptyFolders": false, 15 | "downloadPropersAndRepacks": "preferAndUpgrade", 16 | "enableMediaInfo": true, 17 | "episodeTitleRequired": "always", 18 | "extraFileExtensions": "srt,mp4,avi,mkv", 19 | "fileDate": "string", 20 | "id": 1, 21 | "importExtraFiles": true, 22 | "minimumFreeSpaceWhenImporting": 100, 23 | "pathsDefaultStatic": false, 24 | "recycleBin": "/recycle/", 25 | "recycleBinCleanupDays": 7, 26 | "rescanAfterRefresh": "afterManual", 27 | "setPermissionsLinux": false, 28 | "skipFreeSpaceCheckWhenImporting": false, 29 | "watchLibraryForChanges": true 30 | } 31 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/episodefile.json: -------------------------------------------------------------------------------- 1 | { 2 | "seriesId": 0, 3 | "seasonNumber": 0, 4 | "relativePath": "string", 5 | "path": "string", 6 | "size": 0, 7 | "dateAdded": "2019-05-19T05:33:25.295709Z", 8 | "releaseGroup": "string", 9 | "language": { 10 | "id": 0, 11 | "name": "string" 12 | }, 13 | "quality": { 14 | "quality": { 15 | "id": 0, 16 | "name": "string", 17 | "source": "string", 18 | "resolution": 0 19 | }, 20 | "revision": { 21 | "version": 0, 22 | "real": 0, 23 | "isRepack": false 24 | } 25 | }, 26 | "mediaInfo": { 27 | "audioBitrate": 0, 28 | "audioChannels": 0.0, 29 | "audioCodec": "string", 30 | "audioLanguages": "string", 31 | "audioStreamCount": 0, 32 | "videoBitDepth": 0, 33 | "videoBitrate": 0, 34 | "videoCodec": "string", 35 | "videoFps": 0.0, 36 | "resolution": "string", 37 | "runTime": "00:00", 38 | "scanType": "string", 39 | "subtitles": "string" 40 | }, 41 | "qualityCutoffNotMet": true, 42 | "languageCutoffNotMet": false, 43 | "id": 0 44 | } 45 | -------------------------------------------------------------------------------- /aiopyarr/exceptions.py: -------------------------------------------------------------------------------- 1 | """Exceptions for Arr Api Client.""" 2 | 3 | from __future__ import annotations 4 | 5 | from typing import TYPE_CHECKING 6 | 7 | if TYPE_CHECKING: 8 | from aiohttp import ClientResponse 9 | 10 | from .request_client import RequestClient 11 | 12 | 13 | class ArrException(Exception): 14 | """Base arr exception.""" 15 | 16 | def __init__( 17 | self, 18 | client: RequestClient | None = None, 19 | message: str | BaseException | ClientResponse | Exception = "", 20 | ) -> None: 21 | """Initialize.""" 22 | super().__init__(str(message) if client is not None else message) 23 | 24 | 25 | class ArrAuthenticationException(ArrException): 26 | """Arr authentication exception.""" 27 | 28 | 29 | class ArrConnectionException(ArrException): 30 | """Arr connection exception.""" 31 | 32 | 33 | class ArrResourceNotFound(ArrException): 34 | """Arr resource not found exception.""" 35 | 36 | 37 | class ArrWrongAppException(ArrException): 38 | """Arr wrong application exception.""" 39 | 40 | 41 | class ArrZeroConfException(ArrException): 42 | """Arr Zero Configuration failed exception.""" 43 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/moviefile.json: -------------------------------------------------------------------------------- 1 | { 2 | "movieId": 0, 3 | "relativePath": "string", 4 | "path": "string", 5 | "size": 0, 6 | "dateAdded": "2018-12-28T06:35:27Z", 7 | "sceneName": "string", 8 | "indexerFlags": 0, 9 | "quality": { 10 | "quality": { 11 | "id": 0, 12 | "name": "string", 13 | "source": "string", 14 | "resolution": 0, 15 | "modifier": "string" 16 | }, 17 | "revision": { 18 | "version": 0, 19 | "real": 0, 20 | "isRepack": true 21 | } 22 | }, 23 | "mediaInfo": { 24 | "audioAdditionalFeatures": "string", 25 | "audioBitrate": 0, 26 | "audioChannels": 0.0, 27 | "audioCodec": "string", 28 | "audioLanguages": "string", 29 | "audioStreamCount": 0, 30 | "videoBitDepth": 0, 31 | "videoBitrate": 0, 32 | "videoCodec": "string", 33 | "videoFps": 0, 34 | "resolution": "string", 35 | "runTime": "string", 36 | "scanType": "string", 37 | "subtitles": "string" 38 | }, 39 | "qualityCutoffNotMet": true, 40 | "languages": [ 41 | { 42 | "id": 0, 43 | "name": "string" 44 | } 45 | ], 46 | "edition": "string", 47 | "id": 0 48 | } 49 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/notification.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "onGrab": true, 4 | "onReleaseImport": true, 5 | "onUpgrade": true, 6 | "onRename": true, 7 | "onHealthIssue": true, 8 | "onDownloadFailure": true, 9 | "onImportFailure": true, 10 | "onBookRetag": true, 11 | "supportsOnGrab": true, 12 | "supportsOnReleaseImport": true, 13 | "supportsOnUpgrade": true, 14 | "supportsOnRename": true, 15 | "supportsOnHealthIssue": true, 16 | "includeHealthWarnings": false, 17 | "supportsOnDownloadFailure": true, 18 | "supportsOnImportFailure": true, 19 | "supportsOnBookRetag": true, 20 | "name": "string", 21 | "fields": [ 22 | { 23 | "order": 0, 24 | "name": "string", 25 | "label": "string", 26 | "helpText": "string", 27 | "value": "string", 28 | "type": "string", 29 | "advanced": false 30 | } 31 | ], 32 | "implementationName": "string", 33 | "implementation": "string", 34 | "configContract": "string", 35 | "infoLink": "string", 36 | "tags": [0], 37 | "id": 0 38 | } 39 | ] 40 | -------------------------------------------------------------------------------- /aiopyarr/const.py: -------------------------------------------------------------------------------- 1 | """PyArr constants.""" 2 | 3 | from enum import Enum 4 | from logging import Logger, getLogger 5 | from typing import Any 6 | 7 | LOGGER: Logger = getLogger(__package__) 8 | 9 | ALBUM_ID = "albumId" 10 | ALL = "all" 11 | ARTIST_ID = "artistId" 12 | ATTR_DATA = "basedata" 13 | AUTHOR_ID = "authorId" 14 | BOOK_ID = "bookId" 15 | DATE = "date" 16 | EPISODE_ID = "episodeId" 17 | EVENT_TYPE = "eventType" 18 | HEADERS: dict[str, Any] = { 19 | "Accept-Encoding": "gzip, deflate", 20 | "Accept": "application/json", 21 | "Connection": "keep-alive", 22 | "Content-Type": "application/json", 23 | } 24 | HEADERS_JS = { 25 | "Accept-Encoding": "gzip, deflate", 26 | "Accept": "application/javascript", 27 | "Content-Type": "application/javascript", 28 | } 29 | IS_VALID = "isValid" 30 | MOVIE_ID = "movieId" 31 | NOTIFICATION = "notification" 32 | PAGE = "page" 33 | PAGE_SIZE = "pageSize" 34 | PATH = "path" 35 | SERIES_ID = "seriesId" 36 | SORT_DIRECTION = "sortDirection" 37 | SORT_KEY = "sortKey" 38 | TERM = "term" 39 | TITLE = "title" 40 | 41 | 42 | class HTTPMethod(Enum): 43 | """HTTPMethod Enum.""" 44 | 45 | DELETE = "DELETE" 46 | GET = "GET" 47 | POST = "POST" 48 | PUT = "PUT" 49 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/release.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "guid": "string", 4 | "quality": { 5 | "quality": { 6 | "id": 0, 7 | "name": "string" 8 | }, 9 | "revision": { 10 | "version": 0, 11 | "real": 0, 12 | "isRepack": false 13 | } 14 | }, 15 | "qualityWeight": 0, 16 | "age": 0, 17 | "ageHours": 0.0, 18 | "ageMinutes": 0.0, 19 | "size": 0, 20 | "indexerId": 0, 21 | "indexer": "string", 22 | "title": "string", 23 | "discography": false, 24 | "sceneSource": false, 25 | "authorName": "string", 26 | "bookTitle": "string", 27 | "approved": false, 28 | "temporarilyRejected": false, 29 | "rejected": true, 30 | "rejections": [ 31 | { 32 | "reason": "string", 33 | "type": "permanent" 34 | } 35 | ], 36 | "publishDate": "2021-11-23T05:00:00Z", 37 | "commentUrl": "string", 38 | "downloadUrl": "string", 39 | "infoUrl": "string", 40 | "downloadAllowed": true, 41 | "releaseWeight": 0, 42 | "preferredWordScore": 0, 43 | "magnetUrl": "string", 44 | "infoHash": "string", 45 | "seeders": 0, 46 | "leechers": 0, 47 | "protocol": "unknown" 48 | } 49 | ] 50 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/release.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "guid": "string", 4 | "quality": { 5 | "quality": { 6 | "id": 0, 7 | "name": "string" 8 | }, 9 | "revision": { 10 | "version": 0, 11 | "real": 0, 12 | "isRepack": false 13 | } 14 | }, 15 | "qualityWeight": 0, 16 | "age": 0, 17 | "ageHours": 0.0, 18 | "ageMinutes": 0.0, 19 | "size": 0, 20 | "indexerId": 0, 21 | "indexer": "string", 22 | "releaseHash": "string", 23 | "title": "string", 24 | "discography": false, 25 | "sceneSource": false, 26 | "artistName": "string", 27 | "albumTitle": "string", 28 | "approved": false, 29 | "temporarilyRejected": false, 30 | "rejected": true, 31 | "rejections": [ 32 | { 33 | "reason": "string", 34 | "type": "permanent" 35 | } 36 | ], 37 | "publishDate": "2020-02-16T20:08:16Z", 38 | "commentUrl": "string", 39 | "downloadUrl": "string", 40 | "infoUrl": "string", 41 | "downloadAllowed": true, 42 | "releaseWeight": 0, 43 | "preferredWordScore": 0, 44 | "magnetUrl": "string", 45 | "infoHash": "string", 46 | "seeders": 0, 47 | "leechers": 0, 48 | "protocol": "unknown" 49 | } 50 | ] 51 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/queue-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 10, 4 | "sortKey": "timeleft", 5 | "sortDirection": "ascending", 6 | "totalRecords": 2, 7 | "records": [ 8 | { 9 | "seriesId": 0, 10 | "episodeId": 0, 11 | "language": { 12 | "id": 0, 13 | "name": "string" 14 | }, 15 | "quality": { 16 | "quality": { 17 | "id": 0, 18 | "name": "string", 19 | "source": "string", 20 | "resolution": 0 21 | }, 22 | "revision": { 23 | "version": 0, 24 | "real": 0, 25 | "isRepack": false 26 | } 27 | }, 28 | "size": 200000.0, 29 | "title": "string", 30 | "sizeleft": 100000.0, 31 | "status": "string", 32 | "trackedDownloadStatus": "string", 33 | "statusMessages": [ 34 | { 35 | "title": "string", 36 | "messages": ["string"] 37 | } 38 | ], 39 | "downloadId": "string", 40 | "protocol": "unknown", 41 | "downloadClient": "string", 42 | "indexer": "string", 43 | "outputPath": "string", 44 | "id": 0 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """The setup script.""" 2 | 3 | from setuptools import find_packages, setup 4 | 5 | with open("README.md") as readme_file: 6 | readme = readme_file.read() 7 | 8 | setup( 9 | name="aiopyarr", 10 | version="master", 11 | author="Robert Hillis", 12 | author_email="tkdrob4390@yahoo.com", 13 | description="An Asynchronous Lidarr, Radarr, Readarr, Sonarr APIs for Python.", 14 | long_description=readme, 15 | long_description_content_type="text/markdown", 16 | url="https://github.com/tkdrob/aiopyarr", 17 | package_data={"aiopyarr": ["py.typed"]}, 18 | packages=find_packages(include=["aiopyarr", "aiopyarr*"]), 19 | install_requires=["aiohttp>=3.6.1,<4.0"], 20 | keywords=["aiopyarr", "radarr", "sonarr", "plex"], 21 | license="MIT license", 22 | classifiers=[ 23 | "Intended Audience :: Developers", 24 | "License :: OSI Approved :: MIT License", 25 | "Natural Language :: English", 26 | "Operating System :: OS Independent", 27 | "Programming Language :: Python :: 3", 28 | "Programming Language :: Python :: 3.9", 29 | "Programming Language :: Python :: 3.10", 30 | "Topic :: Software Development :: Libraries :: Python Modules", 31 | ], 32 | python_requires=">=3.9", 33 | ) 34 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/moviefile-list.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "movieId": 0, 4 | "relativePath": "string", 5 | "path": "string", 6 | "size": 0, 7 | "dateAdded": "2018-12-28T06:35:27Z", 8 | "sceneName": "string", 9 | "indexerFlags": 0, 10 | "quality": { 11 | "quality": { 12 | "id": 0, 13 | "name": "string", 14 | "source": "string", 15 | "resolution": 0, 16 | "modifier": "string" 17 | }, 18 | "revision": { 19 | "version": 0, 20 | "real": 0, 21 | "isRepack": true 22 | } 23 | }, 24 | "mediaInfo": { 25 | "audioAdditionalFeatures": "string", 26 | "audioBitrate": 0, 27 | "audioChannels": 0.0, 28 | "audioCodec": "string", 29 | "audioLanguages": "string", 30 | "audioStreamCount": 0, 31 | "videoBitDepth": 0, 32 | "videoBitrate": 0, 33 | "videoCodec": "string", 34 | "videoFps": 0, 35 | "resolution": "string", 36 | "runTime": "string", 37 | "scanType": "string", 38 | "subtitles": "string" 39 | }, 40 | "qualityCutoffNotMet": true, 41 | "languages": [ 42 | { 43 | "id": 0, 44 | "name": "string" 45 | } 46 | ], 47 | "edition": "string", 48 | "id": 0 49 | } 50 | ] 51 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/author-lookup.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "authorMetadataId": 1, 4 | "status": "string", 5 | "ended": false, 6 | "authorName": "string", 7 | "authorNameLastFirst": "string", 8 | "foreignAuthorId": "string", 9 | "titleSlug": "0", 10 | "overview": "string", 11 | "links": [ 12 | { 13 | "url": "string", 14 | "name": "string" 15 | } 16 | ], 17 | "images": [ 18 | { 19 | "url": "string", 20 | "coverType": "poster", 21 | "extension": ".jpg" 22 | } 23 | ], 24 | "remotePoster": "string", 25 | "path": "string", 26 | "qualityProfileId": 0, 27 | "metadataProfileId": 0, 28 | "monitored": true, 29 | "monitorNewItems": "string", 30 | "genres": ["string"], 31 | "cleanName": "string", 32 | "sortName": "string", 33 | "sortNameLastFirst": "string", 34 | "tags": [0], 35 | "added": "2021-12-06T22:23:55Z", 36 | "ratings": { 37 | "votes": 0, 38 | "value": 0.0, 39 | "popularity": 0.0 40 | }, 41 | "statistics": { 42 | "bookFileCount": 0, 43 | "bookCount": 0, 44 | "availableBookCount": 0, 45 | "totalBookCount": 0, 46 | "sizeOnDisk": 0, 47 | "percentOfBooks": 0.0 48 | }, 49 | "id": 0 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/search.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "foreignId": "string", 4 | "artist": { 5 | "status": "string", 6 | "ended": true, 7 | "artistName": "string", 8 | "foreignArtistId": "string", 9 | "tadbId": 0, 10 | "discogsId": 0, 11 | "overview": "string", 12 | "artistType": "string", 13 | "disambiguation": "string", 14 | "links": [ 15 | { 16 | "url": "string", 17 | "name": "string" 18 | } 19 | ], 20 | "images": [ 21 | { 22 | "url": "string", 23 | "coverType": "poster", 24 | "extension": "string" 25 | } 26 | ], 27 | "remotePoster": "string", 28 | "path": "string", 29 | "qualityProfileId": 0, 30 | "metadataProfileId": 0, 31 | "monitored": true, 32 | "genres": ["string"], 33 | "cleanName": "string", 34 | "sortName": "string", 35 | "tags": [0], 36 | "added": "2021-08-21T15:35:54.398878Z", 37 | "ratings": { 38 | "votes": 0, 39 | "value": 0.0 40 | }, 41 | "statistics": { 42 | "albumCount": 0, 43 | "trackFileCount": 0, 44 | "trackCount": 0, 45 | "totalTrackCount": 0, 46 | "sizeOnDisk": 0, 47 | "percentOfTracks": 0.0 48 | }, 49 | "id": 0 50 | }, 51 | "id": 0 52 | } 53 | ] 54 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | release-drafter: 10 | name: Release Drafter 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: 📥 Checkout the repository 14 | uses: actions/checkout@v3 15 | with: 16 | fetch-depth: 0 17 | 18 | - name: ⏭️ Get next version 19 | id: version 20 | run: | 21 | declare -i newpost 22 | latest=$(git describe --tags $(git rev-list --tags --max-count=1)) 23 | latestpre=$(echo "$latest" | awk '{split($0,a,"."); print a[1] "." a[2]}') 24 | datepre=$(date --utc '+%y.%-m') 25 | if [[ "$latestpre" == "$datepre" ]]; then 26 | latestpost=$(echo "$latest" | awk '{split($0,a,"."); print a[3]}') 27 | newpost=$latestpost+1 28 | else 29 | newpost=0 30 | fi 31 | echo Current version: $latest 32 | echo New target version: $datepre.$newpost 33 | echo "::set-output name=version::$datepre.$newpost" 34 | 35 | - name: 🏃 Run Release Drafter 36 | uses: release-drafter/release-drafter@v5 37 | with: 38 | tag: ${{ steps.version.outputs.version }} 39 | name: ${{ steps.version.outputs.version }} 40 | env: 41 | GITHUB_TOKEN: ${{ secrets.GIT_TOKEN }} -------------------------------------------------------------------------------- /tests/fixtures/sonarr/notification.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "onGrab": false, 4 | "onDownload": false, 5 | "onUpgrade": true, 6 | "onRename": false, 7 | "onSeriesDelete": false, 8 | "onEpisodeFileDelete": false, 9 | "onEpisodeFileDeleteForUpgrade": true, 10 | "onHealthIssue": true, 11 | "supportsOnGrab": true, 12 | "supportsOnDownload": true, 13 | "supportsOnUpgrade": true, 14 | "supportsOnRename": true, 15 | "supportsOnSeriesDelete": true, 16 | "supportsOnEpisodeFileDelete": true, 17 | "supportsOnEpisodeFileDeleteForUpgrade": true, 18 | "supportsOnHealthIssue": true, 19 | "includeHealthWarnings": true, 20 | "name": "string", 21 | "fields": [ 22 | { 23 | "order": 0, 24 | "name": "string", 25 | "label": "string", 26 | "helpText": "string", 27 | "value": [ 28 | 0 29 | ], 30 | "type": "string", 31 | "advanced": true, 32 | "selectOptions": [ 33 | { 34 | "value": 0, 35 | "name": "string", 36 | "order": 0 37 | } 38 | ] 39 | } 40 | ], 41 | "implementationName": "string", 42 | "implementation": "string", 43 | "configContract": "string", 44 | "infoLink": "string", 45 | "tags": [0], 46 | "id": 0 47 | } 48 | ] 49 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/search.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "foreignId": "0", 4 | "author": { 5 | "authorMetadataId": 0, 6 | "status": "string", 7 | "ended": false, 8 | "authorName": "string", 9 | "authorNameLastFirst": "string", 10 | "foreignAuthorId": "0", 11 | "titleSlug": "0", 12 | "overview": "string", 13 | "links": [ 14 | { 15 | "url": "string", 16 | "name": "string" 17 | } 18 | ], 19 | "images": [ 20 | { 21 | "url": "string", 22 | "coverType": "poster", 23 | "extension": "string" 24 | } 25 | ], 26 | "remotePoster": "string", 27 | "path": "string", 28 | "qualityProfileId": 0, 29 | "metadataProfileId": 0, 30 | "monitored": true, 31 | "monitorNewItems": "string", 32 | "genres": ["string"], 33 | "cleanName": "string", 34 | "sortName": "string", 35 | "sortNameLastFirst": "string", 36 | "tags": [0], 37 | "added": "2021-10-06T23:38:49Z", 38 | "ratings": { 39 | "votes": 0, 40 | "value": 0.0, 41 | "popularity": 0.0 42 | }, 43 | "statistics": { 44 | "bookFileCount": 0, 45 | "bookCount": 0, 46 | "availableBookCount": 0, 47 | "totalBookCount": 0, 48 | "sizeOnDisk": 0, 49 | "percentOfBooks": 0.0 50 | }, 51 | "id": 0 52 | }, 53 | "id": 0 54 | } 55 | ] 56 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/trackfile-details.json: -------------------------------------------------------------------------------- 1 | { 2 | "artistId": 0, 3 | "albumId": 0, 4 | "path": "string", 5 | "size": 0, 6 | "dateAdded": "2021-08-21T16:00:17.9301483Z", 7 | "quality": { 8 | "quality": { 9 | "id": 0, 10 | "name": "string" 11 | }, 12 | "revision": { 13 | "version": 0, 14 | "real": 0, 15 | "isRepack": false 16 | } 17 | }, 18 | "qualityWeight": 0, 19 | "mediaInfo": { 20 | "audioChannels": 0.0, 21 | "audioBitRate": "string", 22 | "audioCodec": "string", 23 | "audioBits": "string", 24 | "audioSampleRate": "string" 25 | }, 26 | "qualityCutoffNotMet": false, 27 | "audioTags": { 28 | "title": "string", 29 | "cleanTitle": "string", 30 | "artistTitle": "string", 31 | "albumTitle": "string", 32 | "artistTitleInfo": { 33 | "title": "string", 34 | "year": 0 35 | }, 36 | "discNumber": 0, 37 | "discCount": 0, 38 | "year": 0, 39 | "duration": "00:15:08.0093333", 40 | "quality": { 41 | "quality": { 42 | "id": 0, 43 | "name": "string" 44 | }, 45 | "revision": { 46 | "version": 0, 47 | "real": 0, 48 | "isRepack": false 49 | } 50 | }, 51 | "mediaInfo": { 52 | "audioFormat": "string", 53 | "audioBitrate": 0, 54 | "audioChannels": 0.0, 55 | "audioBits": 0, 56 | "audioSampleRate": 0 57 | }, 58 | "trackNumbers": [0] 59 | }, 60 | "id": 0 61 | } 62 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/history.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 10, 4 | "sortKey": "books.releaseDate", 5 | "sortDirection": "descending", 6 | "totalRecords": 5, 7 | "records": [ 8 | { 9 | "bookId": 0, 10 | "authorId": 0, 11 | "sourceTitle": "string", 12 | "quality": { 13 | "quality": { 14 | "id": 3, 15 | "name": "EPUB" 16 | }, 17 | "revision": { 18 | "version": 1, 19 | "real": 0, 20 | "isRepack": false 21 | } 22 | }, 23 | "qualityCutoffNotMet": false, 24 | "date": "2021-12-31T01:13:38Z", 25 | "downloadId": "string", 26 | "eventType": "grabbed", 27 | "data": { 28 | "indexer": "string", 29 | "nzbInfoUrl": "string", 30 | "releaseGroup": null, 31 | "age": "0", 32 | "ageHours": "0.0", 33 | "ageMinutes": "0.0", 34 | "publishedDate": "2020-06-06T04:00:00Z", 35 | "downloadClient": "string", 36 | "size": "0", 37 | "downloadUrl": "string", 38 | "guid": "string", 39 | "protocol": "0", 40 | "downloadForced": "False", 41 | "torrentInfoHash": "string", 42 | "fileId": "0", 43 | "reason": "string", 44 | "droppedPath": "string", 45 | "importedPath": "string", 46 | "downloadClientName": "string" 47 | }, 48 | "id": 0 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/series-lookup.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "string", 4 | "sortTitle": "string", 5 | "status": "string", 6 | "ended": false, 7 | "overview": "string", 8 | "network": "string", 9 | "airTime": "00:00", 10 | "images": [ 11 | { 12 | "coverType": "poster", 13 | "url": "string", 14 | "remoteUrl": "string" 15 | } 16 | ], 17 | "remotePoster": "string", 18 | "seasons": [ 19 | { 20 | "seasonNumber": 0, 21 | "monitored": true 22 | } 23 | ], 24 | "year": 0, 25 | "path": "string", 26 | "qualityProfileId": 0, 27 | "languageProfileId": 0, 28 | "seasonFolder": true, 29 | "monitored": true, 30 | "useSceneNumbering": false, 31 | "runtime": 0, 32 | "tvdbId": 0, 33 | "tvRageId": 0, 34 | "tvMazeId": 0, 35 | "firstAired": "2018-10-12T00:00:00Z", 36 | "seriesType": "string", 37 | "cleanTitle": "string", 38 | "imdbId": "string", 39 | "titleSlug": "0", 40 | "folder": "string", 41 | "certification": "string", 42 | "genres": ["string"], 43 | "tags": [0], 44 | "added": "2018-10-31T05:49:55.35715Z", 45 | "ratings": { 46 | "votes": 0, 47 | "value": 0.0 48 | }, 49 | "statistics": { 50 | "seasonCount": 0, 51 | "episodeFileCount": 0, 52 | "episodeCount": 0, 53 | "totalEpisodeCount": 0, 54 | "sizeOnDisk": 0, 55 | "percentOfEpisodes": 0.0 56 | }, 57 | "id": 0 58 | } 59 | ] 60 | -------------------------------------------------------------------------------- /aiopyarr/models/const.py: -------------------------------------------------------------------------------- 1 | """PyArr model constants.""" 2 | 3 | from enum import Enum 4 | 5 | CONVERT_TO_BOOL = ("downloadForced",) 6 | 7 | CONVERT_TO_FLOAT = ( 8 | "ageHours", 9 | "ageMinutes", 10 | ) 11 | 12 | CONVERT_TO_INTEGER = ( 13 | "age", 14 | "fileId", 15 | "foreignAuthorId", 16 | "foreignEditionId", 17 | "isbn", 18 | "isbn13", 19 | "preferredWordScore", 20 | "size", 21 | "sizeleft", 22 | "titleSlug", 23 | "trackNumber", 24 | "tvdbId", 25 | "tvRageId", 26 | ) 27 | 28 | CONVERT_TO_DATE = ( 29 | "digitalRelease", 30 | "physicalRelease", 31 | "inCinemas", 32 | ) 33 | 34 | CONVERT_TO_DATETIME = ( 35 | "added", 36 | "airDate", 37 | "airDateUtc", 38 | "born", 39 | "buildTime", 40 | "date", 41 | "dateAdded", 42 | "died", 43 | "ended", 44 | "estimatedCompletionTime", 45 | "firstAired", 46 | "installedOn", 47 | "lastExecution", 48 | "lastExecutionTime", 49 | "lastInfoSync", 50 | "lastModified", 51 | "lastStartTime", 52 | "lastWriteTime", 53 | "modified", 54 | "nextExecution", 55 | "publishDate", 56 | "publishedDate", 57 | "queued", 58 | "releaseDate", 59 | "started", 60 | "startTime", 61 | "stateChangeTime", 62 | "time", 63 | ) 64 | 65 | CONVERT_TO_ENUM = ( 66 | "preferredProtocol", 67 | "protocol", 68 | ) 69 | 70 | 71 | class ProtocolType(Enum): 72 | """Protocol type.""" 73 | 74 | UNKNOWN = 0 75 | USENET = 1 76 | TORRENT = 2 77 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/track-details.json: -------------------------------------------------------------------------------- 1 | { 2 | "artistId": 0, 3 | "trackFileId": 0, 4 | "albumId": 0, 5 | "explicit": false, 6 | "absoluteTrackNumber": 0, 7 | "trackNumber": "0", 8 | "title": "string", 9 | "duration": 0, 10 | "mediumNumber": 0, 11 | "hasFile": false, 12 | "artist": { 13 | "status": "string", 14 | "ended": true, 15 | "artistName": "string", 16 | "foreignArtistId": "string", 17 | "tadbId": 0, 18 | "discogsId": 0, 19 | "overview": "string", 20 | "artistType": "string", 21 | "disambiguation": "string", 22 | "links": [ 23 | { 24 | "url": "string", 25 | "name": "string" 26 | } 27 | ], 28 | "images": [ 29 | { 30 | "url": "string", 31 | "coverType": "poster", 32 | "extension": "string" 33 | } 34 | ], 35 | "path": "string", 36 | "qualityProfileId": 0, 37 | "metadataProfileId": 0, 38 | "monitored": true, 39 | "genres": ["string"], 40 | "cleanName": "string", 41 | "sortName": "string", 42 | "tags": [0], 43 | "added": "2021-08-21T15:35:54.398878Z", 44 | "ratings": { 45 | "votes": 0, 46 | "value": 0.0 47 | }, 48 | "statistics": { 49 | "albumCount": 0, 50 | "trackFileCount": 0, 51 | "trackCount": 0, 52 | "totalTrackCount": 0, 53 | "sizeOnDisk": 0, 54 | "percentOfTracks": 0.0 55 | }, 56 | "id": 0 57 | }, 58 | "ratings": { 59 | "votes": 0, 60 | "value": 0.0 61 | }, 62 | "id": 0 63 | } 64 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/blocklist-movie.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "movieId": 0, 4 | "sourceTitle": "string", 5 | "languages": [ 6 | { 7 | "id": 0, 8 | "name": "string" 9 | } 10 | ], 11 | "quality": { 12 | "quality": { 13 | "id": 0, 14 | "name": "string", 15 | "source": "string", 16 | "resolution": 0, 17 | "modifier": "string" 18 | }, 19 | "revision": { 20 | "version": 0, 21 | "real": 0, 22 | "isRepack": true 23 | } 24 | }, 25 | "customFormats": [ 26 | { 27 | "id": 0, 28 | "name": "string", 29 | "includeCustomFormatWhenRenaming": true, 30 | "specifications": [ 31 | { 32 | "name": "string", 33 | "implementation": "string", 34 | "implementationName": "string", 35 | "infoLink": "string", 36 | "negate": true, 37 | "required": true, 38 | "fields": [ 39 | { 40 | "order": 0, 41 | "name": "string", 42 | "label": "string", 43 | "helpText": "string", 44 | "value": "string", 45 | "type": "string", 46 | "advanced": true 47 | } 48 | ] 49 | } 50 | ] 51 | } 52 | ], 53 | "date": "2019-04-10T15:09:11Z", 54 | "protocol": "unknown", 55 | "indexer": "string", 56 | "message": "string", 57 | "id": 0 58 | } 59 | ] 60 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/queue-details.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "seriesId": 0, 4 | "episodeId": 0, 5 | "episode": { 6 | "seriesId": 0, 7 | "episodeFileId": 0, 8 | "seasonNumber": 0, 9 | "episodeNumber": 0, 10 | "title": "string", 11 | "airDate": "2020-07-09", 12 | "airDateUtc": "2020-07-10T02:00:00Z", 13 | "overview": "string", 14 | "hasFile": false, 15 | "monitored": true, 16 | "absoluteEpisodeNumber": 0, 17 | "unverifiedSceneNumbering": false, 18 | "id": 0 19 | }, 20 | "language": { 21 | "id": 0, 22 | "name": "string" 23 | }, 24 | "quality": { 25 | "quality": { 26 | "id": 0, 27 | "name": "string", 28 | "source": "string", 29 | "resolution": 0 30 | }, 31 | "revision": { 32 | "version": 0, 33 | "real": 0, 34 | "isRepack": false 35 | } 36 | }, 37 | "size": 200000.0, 38 | "title": "string", 39 | "sizeleft": 100000.0, 40 | "timeleft": "00:00:00", 41 | "estimatedCompletionTime": "2022-01-07T10:40:32.56084Z", 42 | "status": "string", 43 | "trackedDownloadStatus": "string", 44 | "trackedDownloadState": "downloading", 45 | "statusMessages": [ 46 | { 47 | "title": "string", 48 | "messages": ["string"] 49 | } 50 | ], 51 | "downloadId": "string", 52 | "protocol": "unknown", 53 | "downloadClient": "string", 54 | "indexer": "string", 55 | "outputPath": "string", 56 | "id": 0 57 | } 58 | ] 59 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/importlist.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "enableAutomaticAdd": false, 4 | "shouldMonitor": "entireArtist", 5 | "rootFolderPath": "string", 6 | "qualityProfileId": 0, 7 | "metadataProfileId": 0, 8 | "listType": "other", 9 | "listOrder": 0, 10 | "name": "string", 11 | "fields": [ 12 | { 13 | "order": 0, 14 | "name": "string", 15 | "label": "string", 16 | "helpText": "string", 17 | "value": [], 18 | "type": "string", 19 | "advanced": false, 20 | "selectOptionsProviderAction": "string" 21 | } 22 | ], 23 | "implementationName": "string", 24 | "implementation": "string", 25 | "configContract": "string", 26 | "infoLink": "string", 27 | "tags": [0], 28 | "presets": [ 29 | { 30 | "enableAutomaticAdd": false, 31 | "shouldMonitor": "entireArtist", 32 | "rootFolderPath": "string", 33 | "qualityProfileId": 0, 34 | "metadataProfileId": 0, 35 | "listType": "other", 36 | "listOrder": 0, 37 | "name": "string", 38 | "fields": [ 39 | { 40 | "order": 0, 41 | "name": "string", 42 | "label": "string", 43 | "value": "string", 44 | "type": "string", 45 | "advanced": true 46 | } 47 | ], 48 | "implementation": "string", 49 | "configContract": "string", 50 | "infoLink": "string", 51 | "tags": [0] 52 | } 53 | ], 54 | "id": 0 55 | } 56 | ] 57 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/importlistmovie.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "string", 4 | "sortTitle": "string", 5 | "status": "released", 6 | "overview": "string", 7 | "inCinemas": "2018-03-09T00:00:00Z", 8 | "physicalRelease": "2018-06-10T00:00:00Z", 9 | "digitalRelease": "2018-05-25T00:00:00Z", 10 | "images": [ 11 | { 12 | "coverType": "poster", 13 | "url": "string" 14 | } 15 | ], 16 | "website": "string", 17 | "remotePoster": "string", 18 | "year": 0, 19 | "youTubeTrailerId": "string", 20 | "studio": "string", 21 | "runtime": 0, 22 | "imdbId": "string", 23 | "tmdbId": 0, 24 | "folder": "string", 25 | "certification": "string", 26 | "genres": ["string"], 27 | "ratings": { 28 | "imdb": { 29 | "votes": 0, 30 | "value": 0, 31 | "type": "user" 32 | }, 33 | "tmdb": { 34 | "votes": 0, 35 | "value": 0.0, 36 | "type": "user" 37 | }, 38 | "metacritic": { 39 | "votes": 0, 40 | "value": 0, 41 | "type": "user" 42 | }, 43 | "rottenTomatoes": { 44 | "votes": 0, 45 | "value": 0, 46 | "type": "user" 47 | } 48 | }, 49 | "collection": { 50 | "name": "string", 51 | "tmdbId": 0, 52 | "images": [ 53 | { 54 | "coverType": "poster", 55 | "url": "string", 56 | "remoteUrl": "string" 57 | } 58 | ] 59 | }, 60 | "isExcluded": false, 61 | "isExisting": true, 62 | "isRecommendation": false, 63 | "lists": [0] 64 | } 65 | ] 66 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/history.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 10, 4 | "sortKey": "date", 5 | "sortDirection": "descending", 6 | "totalRecords": 1, 7 | "records": [ 8 | { 9 | "episodeId": 0, 10 | "seriesId": 0, 11 | "sourceTitle": "string", 12 | "language": { 13 | "id": 0, 14 | "name": "string" 15 | }, 16 | "quality": { 17 | "quality": { 18 | "id": 0, 19 | "name": "string", 20 | "source": "string", 21 | "resolution": 0 22 | }, 23 | "revision": { 24 | "version": 0, 25 | "real": 0, 26 | "isRepack": false 27 | } 28 | }, 29 | "qualityCutoffNotMet": true, 30 | "languageCutoffNotMet": false, 31 | "date": "2019-11-01T09:09:34.288036Z", 32 | "downloadId": "string", 33 | "eventType": "grabbed", 34 | "data": { 35 | "indexer": "string", 36 | "nzbInfoUrl": "string", 37 | "releaseGroup": "string", 38 | "age": "0", 39 | "ageHours": "0.0", 40 | "ageMinutes": "0.0", 41 | "publishedDate": "2020-02-08T13:30:37Z", 42 | "fileId": "0", 43 | "droppedPath": "string", 44 | "importedPath": "string", 45 | "downloadClient": "string", 46 | "downloadClientName": "string", 47 | "preferredWordScore": "0", 48 | "size": "0", 49 | "downloadUrl": "string", 50 | "guid": "string", 51 | "tvdbId": "0", 52 | "tvRageId": "0", 53 | "protocol": "0", 54 | "torrentInfoHash": "string" 55 | }, 56 | "id": 0 57 | } 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aiopyarr 2 | 3 | [![codecov](https://codecov.io/gh/tkdrob/aiopyarr/branch/master/graph/badge.svg)](https://codecov.io/gh/tkdrob/aiopyarr) 4 | ![python version](https://img.shields.io/badge/Python-3.9=><=3.12-blue.svg) 5 | [![PyPI](https://img.shields.io/pypi/v/aiopyarr)](https://pypi.org/project/aiopyarr) 6 | ![Actions](https://github.com/tkdrob/aiopyarr/workflows/Actions/badge.svg?branch=master) 7 | 8 | _Python API client for Lidarr/Radarr/Readarr/Sonarr._ 9 | 10 | ## Installation 11 | 12 | ```bash 13 | python3 -m pip install aiopyarr 14 | ``` 15 | 16 | ## Example usage 17 | 18 | More examples can be found in the `tests` directory. 19 | 20 | ```python 21 | """Example usage of aiopyarr.""" 22 | import asyncio 23 | from aiopyarr.models.host_configuration import PyArrHostConfiguration 24 | from aiopyarr.radarr_client import RadarrClient 25 | 26 | IP = "192.168.100.3" 27 | TOKEN = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 28 | 29 | 30 | async def async_example(): 31 | """Example usage of aiopyarr.""" 32 | host_configuration = PyArrHostConfiguration(ipaddress=IP, api_token=TOKEN) 33 | async with RadarrClient(host_configuration=host_configuration) as client: 34 | print(await client.async_get_system_status()) 35 | 36 | asyncio.get_event_loop().run_until_complete(async_example()) 37 | ``` 38 | 39 | ## Contribute 40 | 41 | **All** contributions are welcome! 42 | 43 | 1. Fork the repository 44 | 2. Clone the repository locally and open the devcontainer or use GitHub codespaces 45 | 3. Do your changes 46 | 4. Lint the files with `make lint` 47 | 5. Ensure all tests passes with `make test` 48 | 6. Ensure 100% coverage with `make coverage` 49 | 7. Commit your work, and push it to GitHub 50 | 8. Create a PR against the `master` branch 51 | -------------------------------------------------------------------------------- /aiopyarr/models/host_configuration.py: -------------------------------------------------------------------------------- 1 | """PyArrHostConfiguration.""" 2 | 3 | from __future__ import annotations 4 | 5 | from dataclasses import dataclass 6 | 7 | from .. import ArrException 8 | 9 | 10 | @dataclass 11 | class PyArrHostConfiguration: # pylint: disable=too-many-instance-attributes 12 | """PyArrHostConfiguration.""" 13 | 14 | api_token: str | None = None 15 | hostname: str | None = None 16 | ipaddress: str | None = None 17 | port: int | None = None 18 | ssl: bool = False 19 | verify_ssl: bool = True 20 | base_api_path: str | None = None 21 | url: str | None = None 22 | api_ver: str | None = None 23 | 24 | def __post_init__(self) -> None: 25 | """Post init.""" 26 | if self.api_token is None: 27 | raise ArrException(message="No api token to the server was provided") 28 | if self.hostname is None and self.ipaddress is None and self.url is None: 29 | raise ArrException( 30 | message="No url, hostname or ipaddress to the server was provided" 31 | ) 32 | 33 | def api_url(self, command: str, initialize: bool = False) -> str: 34 | """Return the generated base URL based on host configuration.""" 35 | if initialize: 36 | return f"{self.base_url}/initialize.js" 37 | return f"{self.base_url}/api/{self.api_ver}/{command}" 38 | 39 | @property 40 | def base_url(self) -> str: 41 | """Return the base URL for the configured service.""" 42 | if self.url is not None: 43 | return self.url 44 | protocol = f"http{'s' if self.ssl else ''}" 45 | host = f"{self.hostname or self.ipaddress}:{self.port}" 46 | return f"{protocol}://{host}{self.base_api_path or ''}" 47 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/history-movie.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "movieId": 0, 4 | "sourceTitle": "string", 5 | "languages": [ 6 | { 7 | "id": 0, 8 | "name": "string" 9 | } 10 | ], 11 | "quality": { 12 | "quality": { 13 | "id": 0, 14 | "name": "string", 15 | "source": "string", 16 | "resolution": 0, 17 | "modifier": "string" 18 | }, 19 | "revision": { 20 | "version": 0, 21 | "real": 0, 22 | "isRepack": true 23 | } 24 | }, 25 | "customFormats": [ 26 | { 27 | "id": 0, 28 | "name": "string", 29 | "includeCustomFormatWhenRenaming": true, 30 | "specifications": [ 31 | { 32 | "name": "string", 33 | "implementation": "string", 34 | "implementationName": "string", 35 | "infoLink": "string", 36 | "negate": true, 37 | "required": true, 38 | "fields": [ 39 | { 40 | "order": 0, 41 | "name": "string", 42 | "label": "string", 43 | "helpText": "string", 44 | "value": "string", 45 | "type": "string", 46 | "advanced": true 47 | } 48 | ] 49 | } 50 | ] 51 | } 52 | ], 53 | "qualityCutoffNotMet": true, 54 | "date": "2020-06-16T21:19:05Z", 55 | "downloadId": "string", 56 | "eventType": "grabbed", 57 | "data": { 58 | "droppedPath": "string", 59 | "importedPath": "string", 60 | "downloadClient": "string", 61 | "downloadClientName": "string", 62 | "reason": "string" 63 | }, 64 | "id": 0 65 | } 66 | ] 67 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/series.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "string", 3 | "alternateTitles": [ 4 | { 5 | "title": "string", 6 | "seasonNumber": 0 7 | } 8 | ], 9 | "sortTitle": "string", 10 | "status": "string", 11 | "ended": true, 12 | "overview": "string", 13 | "previousAiring": "string", 14 | "network": "string", 15 | "airTime": "00:00", 16 | "images": [ 17 | { 18 | "coverType": "poster", 19 | "url": "string", 20 | "remoteUrl": "string" 21 | } 22 | ], 23 | "seasons": [ 24 | { 25 | "seasonNumber": 0, 26 | "monitored": true, 27 | "statistics": { 28 | "previousAiring": "2019-07-05T07:00:00Z", 29 | "episodeFileCount": 0, 30 | "episodeCount": 0, 31 | "totalEpisodeCount": 0, 32 | "sizeOnDisk": 0, 33 | "percentOfEpisodes": 0.0 34 | } 35 | } 36 | ], 37 | "year": 0, 38 | "path": "string", 39 | "qualityProfileId": 0, 40 | "languageProfileId": 0, 41 | "seasonFolder": true, 42 | "monitored": true, 43 | "useSceneNumbering": false, 44 | "runtime": 0, 45 | "tvdbId": 0, 46 | "tvRageId": 0, 47 | "tvMazeId": 0, 48 | "firstAired": "2018-05-31T00:00:00Z", 49 | "seriesType": "string", 50 | "cleanTitle": "string", 51 | "imdbId": "string", 52 | "titleSlug": "0", 53 | "rootFolderPath": "string", 54 | "certification": "string", 55 | "genres": ["string"], 56 | "tags": [0], 57 | "added": "2018-06-19T05:33:15.99487Z", 58 | "ratings": { 59 | "votes": 0, 60 | "value": 0.0 61 | }, 62 | "statistics": { 63 | "seasonCount": 0, 64 | "episodeFileCount": 0, 65 | "episodeCount": 0, 66 | "totalEpisodeCount": 0, 67 | "sizeOnDisk": 0, 68 | "percentOfEpisodes": 0.0 69 | }, 70 | "id": 0 71 | } 72 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/blocklist.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 0, 3 | "pageSize": 0, 4 | "sortDirection": "ascending", 5 | "sortKey": "date", 6 | "totalRecords": 0, 7 | "records": [ 8 | { 9 | "movieId": 0, 10 | "sourceTitle": "string", 11 | "languages": [ 12 | { 13 | "id": 0, 14 | "name": "string" 15 | } 16 | ], 17 | "quality": { 18 | "quality": { 19 | "id": 0, 20 | "name": "string", 21 | "source": "string", 22 | "resolution": 0, 23 | "modifier": "string" 24 | }, 25 | "revision": { 26 | "version": 0, 27 | "real": 0, 28 | "isRepack": true 29 | } 30 | }, 31 | "customFormats": [ 32 | { 33 | "id": 0, 34 | "name": "string", 35 | "includeCustomFormatWhenRenaming": true, 36 | "specifications": [ 37 | { 38 | "name": "string", 39 | "implementation": "string", 40 | "implementationName": "string", 41 | "infoLink": "string", 42 | "negate": true, 43 | "required": true, 44 | "fields": [ 45 | { 46 | "order": 0, 47 | "name": "string", 48 | "label": "string", 49 | "helpText": "string", 50 | "value": "string", 51 | "type": "string", 52 | "advanced": true 53 | } 54 | ] 55 | } 56 | ] 57 | } 58 | ], 59 | "date": "2021-09-19T14:24:13Z", 60 | "protocol": "unknown", 61 | "indexer": "string", 62 | "message": "string", 63 | "id": 0 64 | } 65 | ] 66 | } 67 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/release-push.json: -------------------------------------------------------------------------------- 1 | { 2 | "guid": "string", 3 | "quality": { 4 | "quality": { 5 | "id": 0, 6 | "name": "string", 7 | "source": "string", 8 | "resolution": 0, 9 | "modifier": "string" 10 | }, 11 | "revision": { 12 | "version": 0, 13 | "real": 0, 14 | "isRepack": false 15 | } 16 | }, 17 | "customFormats": [ 18 | { 19 | "name": "string", 20 | "includeCustomFormatWhenRenaming": false, 21 | "specifications": [ 22 | { 23 | "implementation": "string", 24 | "negate": false, 25 | "required": false, 26 | "fields": { 27 | "value": 0 28 | } 29 | } 30 | ] 31 | } 32 | ], 33 | "customFormatScore": 0, 34 | "qualityWeight": 0, 35 | "age": 0, 36 | "ageHours": 0.0, 37 | "ageMinutes": 0.0, 38 | "size": 0, 39 | "indexerId": 0, 40 | "indexer": "string", 41 | "releaseGroup": "string", 42 | "releaseHash": "string", 43 | "title": "string", 44 | "sceneSource": false, 45 | "movieTitles": ["string"], 46 | "languages": [ 47 | { 48 | "id": 1, 49 | "name": "string" 50 | } 51 | ], 52 | "approved": true, 53 | "temporarilyRejected": false, 54 | "rejected": false, 55 | "tmdbId": 0, 56 | "imdbId": 0, 57 | "rejections": [ 58 | { 59 | "reason": "string", 60 | "type": "permanent" 61 | } 62 | ], 63 | "publishDate": "2022-01-07T04:20:36Z", 64 | "commentUrl": "string", 65 | "downloadUrl": "string", 66 | "infoUrl": "string", 67 | "downloadAllowed": true, 68 | "releaseWeight": 0, 69 | "indexerFlags": ["string"], 70 | "edition": "string", 71 | "magnetUrl": "string", 72 | "infoHash": "string", 73 | "seeders": 0, 74 | "leechers": 0, 75 | "protocol": "unknown" 76 | } 77 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | """Tests configuration.""" 2 | 3 | # pylint:disable=redefined-outer-name 4 | import asyncio 5 | 6 | from aiohttp import ClientSession 7 | import pytest_asyncio 8 | 9 | from aiopyarr.lidarr_client import LidarrClient 10 | from aiopyarr.radarr_client import RadarrClient 11 | from aiopyarr.readarr_client import ReadarrClient 12 | from aiopyarr.sonarr_client import SonarrClient 13 | 14 | from tests import TEST_HOST_CONFIGURATION 15 | 16 | 17 | @pytest_asyncio.fixture(autouse=True) 18 | def loop_factory(): 19 | """Create loop.""" 20 | return asyncio.new_event_loop 21 | 22 | 23 | @pytest_asyncio.fixture() 24 | async def apisession(): 25 | """Create client session.""" 26 | async with ClientSession() as sess: 27 | yield sess 28 | 29 | 30 | @pytest_asyncio.fixture() 31 | async def lidarr_client(apisession): 32 | """Create Lidarr Client.""" 33 | async with LidarrClient( 34 | session=apisession, host_configuration=TEST_HOST_CONFIGURATION 35 | ) as obj: 36 | yield obj 37 | 38 | 39 | @pytest_asyncio.fixture() 40 | async def radarr_client(apisession): 41 | """Create Radarr Client.""" 42 | async with RadarrClient( 43 | session=apisession, host_configuration=TEST_HOST_CONFIGURATION 44 | ) as obj: 45 | yield obj 46 | 47 | 48 | @pytest_asyncio.fixture() 49 | async def readarr_client(apisession): 50 | """Create Readarr Client.""" 51 | async with ReadarrClient( 52 | session=apisession, host_configuration=TEST_HOST_CONFIGURATION 53 | ) as obj: 54 | yield obj 55 | 56 | 57 | @pytest_asyncio.fixture() 58 | async def sonarr_client(apisession): 59 | """Create Sonarr Client.""" 60 | async with SonarrClient( 61 | session=apisession, host_configuration=TEST_HOST_CONFIGURATION 62 | ) as obj: 63 | yield obj 64 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/release.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "guid": "string", 4 | "quality": { 5 | "quality": { 6 | "id": 0, 7 | "name": "string", 8 | "source": "string", 9 | "resolution": 0, 10 | "modifier": "string" 11 | }, 12 | "revision": { 13 | "version": 0, 14 | "real": 0, 15 | "isRepack": false 16 | } 17 | }, 18 | "customFormats": [ 19 | { 20 | "name": "string", 21 | "includeCustomFormatWhenRenaming": false, 22 | "specifications": [ 23 | { 24 | "implementation": "string", 25 | "negate": false, 26 | "required": false, 27 | "fields": { 28 | "value": 0 29 | } 30 | } 31 | ] 32 | } 33 | ], 34 | "customFormatScore": 0, 35 | "qualityWeight": 0, 36 | "age": 0, 37 | "ageHours": 0.0, 38 | "ageMinutes": 0.0, 39 | "size": 0, 40 | "indexerId": 0, 41 | "indexer": "string", 42 | "releaseGroup": "string", 43 | "releaseHash": "string", 44 | "title": "string", 45 | "sceneSource": false, 46 | "movieTitles": ["string"], 47 | "languages": [ 48 | { 49 | "id": 1, 50 | "name": "string" 51 | } 52 | ], 53 | "approved": true, 54 | "temporarilyRejected": false, 55 | "rejected": false, 56 | "tmdbId": 0, 57 | "imdbId": 0, 58 | "rejections": [ 59 | { 60 | "reason": "string", 61 | "type": "permanent" 62 | } 63 | ], 64 | "publishDate": "2022-01-07T04:20:36Z", 65 | "commentUrl": "string", 66 | "downloadUrl": "string", 67 | "infoUrl": "string", 68 | "downloadAllowed": true, 69 | "releaseWeight": 0, 70 | "indexerFlags": ["string"], 71 | "edition": "string", 72 | "magnetUrl": "string", 73 | "infoHash": "string", 74 | "seeders": 0, 75 | "leechers": 0, 76 | "protocol": "unknown" 77 | } 78 | ] 79 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/release.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "guid": "string", 4 | "quality": { 5 | "quality": { 6 | "id": 0, 7 | "name": "string", 8 | "source": "string", 9 | "resolution": 0 10 | }, 11 | "revision": { 12 | "version": 0, 13 | "real": 0, 14 | "isRepack": false 15 | } 16 | }, 17 | "qualityWeight": 0, 18 | "age": 0, 19 | "ageHours": 0.0, 20 | "ageMinutes": 0.0, 21 | "size": 0, 22 | "indexerId": 0, 23 | "indexer": "string", 24 | "releaseGroup": "string", 25 | "releaseHash": "string", 26 | "title": "string", 27 | "fullSeason": false, 28 | "sceneSource": false, 29 | "seasonNumber": 0, 30 | "language": { 31 | "id": 0, 32 | "name": "string" 33 | }, 34 | "languageWeight": 0, 35 | "seriesTitle": "string", 36 | "episodeNumbers": [0], 37 | "absoluteEpisodeNumbers": [0], 38 | "mappedSeasonNumber": 0, 39 | "mappedEpisodeNumbers": [0], 40 | "mappedAbsoluteEpisodeNumbers": [0], 41 | "approved": false, 42 | "temporarilyRejected": false, 43 | "rejected": true, 44 | "tvdbId": 0, 45 | "tvRageId": 0, 46 | "rejections": [ 47 | { 48 | "reason": "string", 49 | "type": "permanent" 50 | } 51 | ], 52 | "publishDate": "2020-01-08T15:31:03Z", 53 | "commentUrl": "string", 54 | "downloadUrl": "string", 55 | "infoUrl": "string", 56 | "episodeRequested": false, 57 | "downloadAllowed": true, 58 | "releaseWeight": 0, 59 | "preferredWordScore": 0, 60 | "sceneMapping": { 61 | "title": "string", 62 | "seasonNumber": 0 63 | }, 64 | "magnetUrl": "string", 65 | "infoHash": "string", 66 | "seeders": 0, 67 | "leechers": 0, 68 | "protocol": "unknown", 69 | "isDaily": false, 70 | "isAbsoluteNumbering": false, 71 | "isPossibleSpecialEpisode": false, 72 | "special": false 73 | } 74 | ] 75 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/queue-details.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "movieId": 0, 4 | "languages": [ 5 | { 6 | "id": 0, 7 | "name": "string" 8 | } 9 | ], 10 | "quality": { 11 | "quality": { 12 | "id": 0, 13 | "name": "string", 14 | "source": "string", 15 | "resolution": 0, 16 | "modifier": "string" 17 | }, 18 | "revision": { 19 | "version": 0, 20 | "real": 0, 21 | "isRepack": true 22 | } 23 | }, 24 | "customFormats": [ 25 | { 26 | "id": 0, 27 | "name": "string", 28 | "includeCustomFormatWhenRenaming": true, 29 | "specifications": [ 30 | { 31 | "name": "string", 32 | "implementation": "string", 33 | "implementationName": "string", 34 | "infoLink": "string", 35 | "negate": true, 36 | "required": true, 37 | "fields": [ 38 | { 39 | "order": 0, 40 | "name": "string", 41 | "label": "string", 42 | "helpText": "string", 43 | "value": "string", 44 | "type": "string", 45 | "advanced": true 46 | } 47 | ] 48 | } 49 | ] 50 | } 51 | ], 52 | "size": 0, 53 | "title": "string", 54 | "sizeleft": 0, 55 | "timeleft": "string", 56 | "estimatedCompletionTime": "2020-01-21T00:01:59Z", 57 | "status": "string", 58 | "trackedDownloadStatus": "string", 59 | "trackedDownloadState": "string", 60 | "statusMessages": [ 61 | { 62 | "title": "string", 63 | "messages": ["string"] 64 | } 65 | ], 66 | "errorMessage": "string", 67 | "downloadId": "string", 68 | "protocol": "unknown", 69 | "downloadClient": "string", 70 | "indexer": "string", 71 | "outputPath": "string", 72 | "id": 0 73 | } 74 | ] 75 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/wantedmissing-extended.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 10, 4 | "sortKey": "airDateUtc", 5 | "sortDirection": "default", 6 | "totalRecords": 1, 7 | "records": [ 8 | { 9 | "seriesId": 0, 10 | "episodeFileId": 0, 11 | "seasonNumber": 0, 12 | "episodeNumber": 0, 13 | "title": "string", 14 | "airDate": "2010-03-07", 15 | "airDateUtc": "2010-03-07T05:00:00Z", 16 | "overview": "string", 17 | "hasFile": false, 18 | "monitored": true, 19 | "absoluteEpisodeNumber": 0, 20 | "unverifiedSceneNumbering": false, 21 | "series": { 22 | "title": "string", 23 | "sortTitle": "string", 24 | "status": "string", 25 | "ended": true, 26 | "overview": "string", 27 | "network": "string", 28 | "airTime": "00:00", 29 | "images": [ 30 | { 31 | "coverType": "poster", 32 | "url": "string" 33 | } 34 | ], 35 | "seasons": [ 36 | { 37 | "seasonNumber": 0, 38 | "monitored": false 39 | } 40 | ], 41 | "year": 0, 42 | "path": "string", 43 | "qualityProfileId": 0, 44 | "languageProfileId": 0, 45 | "seasonFolder": true, 46 | "monitored": true, 47 | "useSceneNumbering": false, 48 | "runtime": 0, 49 | "tvdbId": 0, 50 | "tvRageId": 0, 51 | "tvMazeId": 0, 52 | "firstAired": "2017-04-05T00:00:00Z", 53 | "seriesType": "string", 54 | "cleanTitle": "string", 55 | "imdbId": "string", 56 | "titleSlug": "0", 57 | "certification": "string", 58 | "genres": ["string"], 59 | "tags": [0], 60 | "added": "2019-05-19T05:33:42.24392Z", 61 | "ratings": { 62 | "votes": 0, 63 | "value": 0.0 64 | }, 65 | "id": 0 66 | }, 67 | "id": 0 68 | } 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | ## Proposed change 7 | 8 | 14 | 15 | ## Type of change 16 | 17 | 23 | 24 | - [ ] Dependency upgrade 25 | - [ ] Bugfix (non-breaking change which fixes an issue) 26 | - [ ] New feature (which adds functionality) 27 | - [ ] Breaking change (fix/feature causing existing functionality to break) 28 | - [ ] Code quality improvements to existing code or addition of tests 29 | 30 | ## Additional information 31 | 32 | 36 | 37 | - This PR fixes or closes issue: fixes # 38 | - This PR is related to issue: 39 | - Link to documentation pull request: 40 | 41 | ## Checklist 42 | 43 | 49 | 50 | - [ ] The code change is tested and works locally. 51 | - [ ] Local tests pass. 52 | - [ ] There is no commented out code in this PR. 53 | - [ ] The code has been formatted (`make lint`) 54 | - [ ] Tests have been added to verify that the new code works. 55 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/blocklist.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 10, 4 | "sortKey": "date", 5 | "sortDirection": "descending", 6 | "totalRecords": 1, 7 | "records": [ 8 | { 9 | "artistId": 0, 10 | "albumIds": [0], 11 | "sourceTitle": "string", 12 | "quality": { 13 | "quality": { 14 | "id": 0, 15 | "name": "string" 16 | }, 17 | "revision": { 18 | "version": 0, 19 | "real": 0, 20 | "isRepack": false 21 | } 22 | }, 23 | "date": "2020-02-15T19:24:28.4760603Z", 24 | "protocol": "unknown", 25 | "indexer": "string", 26 | "message": "string", 27 | "artist": { 28 | "status": "ended", 29 | "ended": true, 30 | "artistName": "string", 31 | "foreignArtistId": "string", 32 | "tadbId": 0, 33 | "discogsId": 0, 34 | "overview": "string", 35 | "artistType": "string", 36 | "disambiguation": "string", 37 | "links": [ 38 | { 39 | "url": "string", 40 | "name": "string" 41 | } 42 | ], 43 | "images": [ 44 | { 45 | "url": "string", 46 | "coverType": "poster", 47 | "extension": "string" 48 | } 49 | ], 50 | "path": "string", 51 | "qualityProfileId": 0, 52 | "metadataProfileId": 0, 53 | "monitored": true, 54 | "genres": ["string"], 55 | "cleanName": "string", 56 | "sortName": "string", 57 | "tags": [0], 58 | "added": "2021-08-21T15:35:54.398878Z", 59 | "ratings": { 60 | "votes": 0, 61 | "value": 0.0 62 | }, 63 | "statistics": { 64 | "albumCount": 0, 65 | "trackFileCount": 0, 66 | "trackCount": 0, 67 | "totalTrackCount": 0, 68 | "sizeOnDisk": 0, 69 | "percentOfTracks": 0.0 70 | }, 71 | "id": 0 72 | }, 73 | "id": 0 74 | } 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/history.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 10, 4 | "sortKey": "date", 5 | "sortDirection": "descending", 6 | "totalRecords": 1, 7 | "records": [ 8 | { 9 | "movieId": 0, 10 | "sourceTitle": "string", 11 | "languages": [ 12 | { 13 | "id": 0, 14 | "name": "string" 15 | } 16 | ], 17 | "quality": { 18 | "quality": { 19 | "id": 0, 20 | "name": "string", 21 | "source": "string", 22 | "resolution": 0, 23 | "modifier": "string" 24 | }, 25 | "revision": { 26 | "version": 0, 27 | "real": 0, 28 | "isRepack": true 29 | } 30 | }, 31 | "customFormats": [ 32 | { 33 | "id": 0, 34 | "name": "string", 35 | "includeCustomFormatWhenRenaming": true, 36 | "specifications": [ 37 | { 38 | "name": "string", 39 | "implementation": "string", 40 | "implementationName": "string", 41 | "infoLink": "string", 42 | "negate": true, 43 | "required": true, 44 | "fields": [ 45 | { 46 | "order": 0, 47 | "name": "string", 48 | "label": "string", 49 | "helpText": "string", 50 | "value": "string", 51 | "type": "string", 52 | "advanced": true 53 | } 54 | ] 55 | } 56 | ] 57 | } 58 | ], 59 | "qualityCutoffNotMet": true, 60 | "date": "2020-02-20T21:34:52Z", 61 | "downloadId": "string", 62 | "eventType": "grabbed", 63 | "data": { 64 | "fileId": "0", 65 | "droppedPath": "string", 66 | "importedPath": "string", 67 | "downloadClient": "string", 68 | "downloadClientName": "string", 69 | "reason": "string" 70 | }, 71 | "id": 0 72 | } 73 | ] 74 | } 75 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/book-file.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 0, 3 | "authorId": 0, 4 | "bookId": 0, 5 | "path": "string", 6 | "size": 0, 7 | "dateAdded": "2021-12-09T20:39:08.079Z", 8 | "quality": { 9 | "quality": { 10 | "id": 0, 11 | "name": "string" 12 | }, 13 | "revision": { 14 | "version": 0, 15 | "real": 0, 16 | "isRepack": true 17 | } 18 | }, 19 | "qualityWeight": 0, 20 | "mediaInfo": { 21 | "id": 0, 22 | "audioChannels": 0.0, 23 | "audioBitRate": 0, 24 | "audioCodec": "string", 25 | "audioBits": 0, 26 | "audioSampleRate": "string" 27 | }, 28 | "qualityCutoffNotMet": true, 29 | "audioTags": { 30 | "title": "string", 31 | "cleanTitle": "string", 32 | "authors": [ 33 | "string" 34 | ], 35 | "authorTitle": "string", 36 | "bookTitle": "string", 37 | "seriesTitle": "string", 38 | "seriesIndex": "string", 39 | "isbn": "string", 40 | "asin": "string", 41 | "goodreadsId": "string", 42 | "authorMBId": "string", 43 | "bookMBId": "string", 44 | "releaseMBId": "string", 45 | "recordingMBId": "string", 46 | "trackMBId": "string", 47 | "discNumber": 0, 48 | "discCount": 0, 49 | "country": { 50 | "twoLetterCode": "string", 51 | "name": "string" 52 | }, 53 | "year": 0, 54 | "publisher": "string", 55 | "label": "string", 56 | "source": "string", 57 | "catalogNumber": "string", 58 | "disambiguation": "string", 59 | "duration": "00:00:00", 60 | "quality": { 61 | "quality": { 62 | "id": 0, 63 | "name": "string" 64 | }, 65 | "revision": { 66 | "version": 0, 67 | "real": 0, 68 | "isRepack": true 69 | } 70 | }, 71 | "mediaInfo": { 72 | "audioFormat": "string", 73 | "audioBitrate": 0, 74 | "audioChannels": 0.0, 75 | "audioBits": 0, 76 | "audioSampleRate": "string" 77 | }, 78 | "trackNumbers": [ 79 | 0 80 | ], 81 | "language": "string", 82 | "releaseGroup": "string", 83 | "releaseHash": "string" 84 | } 85 | } -------------------------------------------------------------------------------- /.github/workflows/actions.yml: -------------------------------------------------------------------------------- 1 | name: Actions 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | push: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | lint: 13 | name: Lint 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: 📥 Checkout the repository 17 | uses: actions/checkout@v3 18 | 19 | - name: 🛠 Set up Python 3 20 | uses: actions/setup-python@v4.1.0 21 | id: python 22 | with: 23 | python-version: 3.x 24 | 25 | - name: 📦 Install dependencies 26 | run: make requirements 27 | 28 | - name: 🖤 Format with black 29 | run: black . --check 30 | 31 | - name: 🗃 Organize with isort 32 | run: isort . 33 | 34 | - name: 🧹 Lint files with pylint 35 | run: pylint aiopyarr tests 36 | 37 | - name: 🧹 Lint files with flake8 38 | run: flake8 aiopyarr tests 39 | 40 | - name: 🔍 Inspect with mypy 41 | run: mypy aiopyarr 42 | test: 43 | name: Test with Python ${{ matrix.python-version }} 44 | needs: lint 45 | runs-on: ubuntu-latest 46 | strategy: 47 | matrix: 48 | python-version: [3.9, "3.10", 3.11, 3.12] 49 | steps: 50 | - name: 📥 Checkout the repository 51 | uses: actions/checkout@v3 52 | 53 | - name: 🛠️ Set up Python ${{ matrix.python-version }} 54 | uses: actions/setup-python@v4.1.0 55 | with: 56 | python-version: ${{ matrix.python-version }} 57 | 58 | - name: 📦 Install dependencies 59 | run: make requirements 60 | 61 | - name: 🏃 Run tests 62 | run: make coverage 63 | 64 | coverage: 65 | name: Upload coverage to Codecov 66 | needs: test 67 | runs-on: ubuntu-latest 68 | steps: 69 | - name: 📥 Checkout the repository 70 | uses: actions/checkout@v3 71 | 72 | - name: 🛠️ Set up Python 73 | uses: actions/setup-python@v4.1.0 74 | with: 75 | python-version: 3.x 76 | 77 | - name: 📦 Install dependencies 78 | run: make requirements 79 | 80 | - name: 📤 Upload coverage to Codecov 81 | run: | 82 | make coverage 83 | curl -sfSL https://codecov.io/bash | bash - 84 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/queue.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 10, 4 | "sortKey": "timeleft", 5 | "sortDirection": "ascending", 6 | "totalRecords": 1, 7 | "records": [ 8 | { 9 | "movieId": 0, 10 | "languages": [ 11 | { 12 | "id": 0, 13 | "name": "string" 14 | } 15 | ], 16 | "quality": { 17 | "quality": { 18 | "id": 0, 19 | "name": "string", 20 | "source": "string", 21 | "resolution": 0, 22 | "modifier": "string" 23 | }, 24 | "revision": { 25 | "version": 0, 26 | "real": 0, 27 | "isRepack": true 28 | } 29 | }, 30 | "customFormats": [ 31 | { 32 | "id": 0, 33 | "name": "string", 34 | "includeCustomFormatWhenRenaming": true, 35 | "specifications": [ 36 | { 37 | "name": "string", 38 | "implementation": "string", 39 | "implementationName": "string", 40 | "infoLink": "string", 41 | "negate": true, 42 | "required": true, 43 | "fields": [ 44 | { 45 | "order": 0, 46 | "name": "string", 47 | "label": "string", 48 | "helpText": "string", 49 | "value": "string", 50 | "type": "string", 51 | "advanced": true 52 | } 53 | ] 54 | } 55 | ] 56 | } 57 | ], 58 | "size": 200000, 59 | "title": "string", 60 | "sizeleft": 100000, 61 | "timeleft": "00:00:20", 62 | "estimatedCompletionTime": "2020-01-21T00:01:59Z", 63 | "status": "string", 64 | "trackedDownloadStatus": "string", 65 | "trackedDownloadState": "downloading", 66 | "statusMessages": [ 67 | { 68 | "title": "string", 69 | "messages": ["string"] 70 | } 71 | ], 72 | "errorMessage": "string", 73 | "downloadId": "string", 74 | "protocol": "unknown", 75 | "downloadClient": "string", 76 | "indexer": "string", 77 | "outputPath": "string", 78 | "id": 0 79 | } 80 | ] 81 | } 82 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/queue.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 10, 4 | "sortKey": "timeleft", 5 | "sortDirection": "ascending", 6 | "totalRecords": 2, 7 | "records": [ 8 | { 9 | "seriesId": 0, 10 | "episodeId": 0, 11 | "language": { 12 | "id": 0, 13 | "name": "string" 14 | }, 15 | "quality": { 16 | "quality": { 17 | "id": 0, 18 | "name": "string", 19 | "source": "string", 20 | "resolution": 0 21 | }, 22 | "revision": { 23 | "version": 0, 24 | "real": 0, 25 | "isRepack": false 26 | } 27 | }, 28 | "size": 200000.0, 29 | "title": "string", 30 | "sizeleft": 100000.0, 31 | "timeleft": "00:00:00", 32 | "estimatedCompletionTime": "2020-02-09T13:14:14.379532Z", 33 | "status": "string", 34 | "trackedDownloadStatus": "string", 35 | "statusMessages": [ 36 | { 37 | "title": "string", 38 | "messages": ["string"] 39 | } 40 | ], 41 | "downloadId": "string", 42 | "protocol": "unknown", 43 | "downloadClient": "string", 44 | "indexer": "string", 45 | "outputPath": "string", 46 | "id": 0 47 | }, 48 | { 49 | "seriesId": 0, 50 | "episodeId": 0, 51 | "language": { 52 | "id": 0, 53 | "name": "string" 54 | }, 55 | "quality": { 56 | "quality": { 57 | "id": 0, 58 | "name": "string", 59 | "source": "string", 60 | "resolution": 0 61 | }, 62 | "revision": { 63 | "version": 0, 64 | "real": 0, 65 | "isRepack": false 66 | } 67 | }, 68 | "size": 200000.0, 69 | "title": "string", 70 | "sizeleft": 100000.0, 71 | "estimatedCompletionTime": "2020-02-09T13:14:14.379532Z", 72 | "status": "string", 73 | "trackedDownloadStatus": "string", 74 | "statusMessages": [ 75 | { 76 | "title": "string", 77 | "messages": ["string"] 78 | } 79 | ], 80 | "downloadId": "string", 81 | "protocol": "unknown", 82 | "downloadClient": "string", 83 | "indexer": "string", 84 | "outputPath": "string", 85 | "id": 0 86 | } 87 | ] 88 | } 89 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/queue-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 10, 4 | "sortKey": "timeleft", 5 | "sortDirection": "ascending", 6 | "totalRecords": 1, 7 | "records": [ 8 | { 9 | "movieId": 0, 10 | "languages": [ 11 | { 12 | "id": 0, 13 | "name": "string" 14 | } 15 | ], 16 | "quality": { 17 | "quality": { 18 | "id": 0, 19 | "name": "string", 20 | "source": "string", 21 | "resolution": 0, 22 | "modifier": "string" 23 | }, 24 | "revision": { 25 | "version": 0, 26 | "real": 0, 27 | "isRepack": true 28 | } 29 | }, 30 | "customFormats": [ 31 | { 32 | "id": 0, 33 | "name": "string", 34 | "includeCustomFormatWhenRenaming": true, 35 | "specifications": [ 36 | { 37 | "name": "string", 38 | "implementation": "string", 39 | "implementationName": "string", 40 | "infoLink": "string", 41 | "negate": true, 42 | "required": true, 43 | "fields": [ 44 | { 45 | "order": 0, 46 | "name": "string", 47 | "label": "string", 48 | "helpText": "string", 49 | "value": "string", 50 | "type": "string", 51 | "advanced": true 52 | } 53 | ] 54 | } 55 | ] 56 | } 57 | ], 58 | "size": 200000, 59 | "title": "string", 60 | "sizeleft": 100000, 61 | "status": "string", 62 | "trackedDownloadStatus": "string", 63 | "trackedDownloadState": "downloading", 64 | "statusMessages": [ 65 | { 66 | "title": "string", 67 | "messages": ["string"] 68 | } 69 | ], 70 | "errorMessage": "string", 71 | "downloadId": "string", 72 | "protocol": "unknown", 73 | "downloadClient": "string", 74 | "indexer": "string", 75 | "outputPath": "string", 76 | "id": 0 77 | } 78 | ] 79 | } 80 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/manualimport.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "path": "string", 4 | "relativePath": "string", 5 | "folderName": "string", 6 | "name": "string", 7 | "size": 0, 8 | "movie": { 9 | "title": "string", 10 | "originalTitle": "string", 11 | "alternateTitles": [ 12 | { 13 | "sourceType": "tmdb", 14 | "movieId": 0, 15 | "title": "string", 16 | "sourceId": 0, 17 | "votes": 0, 18 | "voteCount": 0, 19 | "language": { 20 | "id": 0, 21 | "name": "string" 22 | }, 23 | "id": 0 24 | } 25 | ], 26 | "secondaryYearSourceId": 0, 27 | "sortTitle": "string", 28 | "sizeOnDisk": 0, 29 | "status": "released", 30 | "overview": "string", 31 | "inCinemas": "2010-05-20T00:00:00Z", 32 | "images": [ 33 | { 34 | "coverType": "string", 35 | "url": "string" 36 | } 37 | ], 38 | "website": "string", 39 | "year": 0, 40 | "hasFile": false, 41 | "youTubeTrailerId": "string", 42 | "studio": "string", 43 | "path": "string", 44 | "qualityProfileId": 0, 45 | "monitored": true, 46 | "minimumAvailability": "announced", 47 | "isAvailable": true, 48 | "folderName": "string", 49 | "runtime": 0, 50 | "cleanTitle": "string", 51 | "imdbId": "string", 52 | "tmdbId": 0, 53 | "titleSlug": "0", 54 | "certification": "string", 55 | "genres": ["string"], 56 | "tags": [0], 57 | "added": "2019-05-31T05:01:52Z", 58 | "ratings": { 59 | "imdb": { 60 | "votes": 0, 61 | "value": 0, 62 | "type": "user" 63 | } 64 | }, 65 | "id": 0 66 | }, 67 | "quality": { 68 | "quality": { 69 | "id": 0, 70 | "name": "string", 71 | "source": "string", 72 | "resolution": 0, 73 | "modifier": "none" 74 | }, 75 | "revision": { 76 | "version": 0, 77 | "real": 0, 78 | "isRepack": false 79 | } 80 | }, 81 | "languages": [ 82 | { 83 | "id": 0, 84 | "name": "string" 85 | } 86 | ], 87 | "releaseGroup": "string", 88 | "qualityWeight": 0, 89 | "downloadId": "string", 90 | "rejections": [ 91 | { 92 | "reason": "string", 93 | "type": "permanent" 94 | } 95 | ], 96 | "id": 0 97 | } 98 | ] 99 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/queue.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 1, 4 | "sortKey": "timeleft", 5 | "sortDirection": "ascending", 6 | "totalRecords": 1, 7 | "records": [ 8 | { 9 | "artistId": 0, 10 | "albumId": 0, 11 | "artist": { 12 | "status": "string", 13 | "ended": false, 14 | "artistName": "string", 15 | "foreignArtistId": "string", 16 | "tadbId": 0, 17 | "discogsId": 0, 18 | "overview": "string", 19 | "artistType": "string", 20 | "disambiguation": "string", 21 | "links": [ 22 | { 23 | "url": "string", 24 | "name": "string" 25 | } 26 | ], 27 | "images": [ 28 | { 29 | "url": "string", 30 | "coverType": "poster", 31 | "extension": "string" 32 | } 33 | ], 34 | "path": "string", 35 | "qualityProfileId": 0, 36 | "metadataProfileId": 0, 37 | "monitored": true, 38 | "genres": ["string"], 39 | "cleanName": "string", 40 | "sortName": "string", 41 | "tags": [0], 42 | "added": "2021-08-21T15:38:22.7238511Z", 43 | "ratings": { 44 | "votes": 0, 45 | "value": 0.0 46 | }, 47 | "statistics": { 48 | "albumCount": 0, 49 | "trackFileCount": 0, 50 | "trackCount": 0, 51 | "totalTrackCount": 0, 52 | "sizeOnDisk": 0, 53 | "percentOfTracks": 0.0 54 | }, 55 | "id": 0 56 | }, 57 | "quality": { 58 | "quality": { 59 | "id": 0, 60 | "name": "string" 61 | }, 62 | "revision": { 63 | "version": 0, 64 | "real": 0, 65 | "isRepack": false 66 | } 67 | }, 68 | "size": 20000.0, 69 | "title": "string", 70 | "sizeleft": 10000.0, 71 | "timeleft": "00:00:00", 72 | "estimatedCompletionTime": "2020-02-16T23:34:44.8856492Z", 73 | "status": "string", 74 | "trackedDownloadStatus": "string", 75 | "trackedDownloadState": "string", 76 | "statusMessages": [ 77 | { 78 | "title": "string", 79 | "messages": ["string"] 80 | } 81 | ], 82 | "downloadId": "string", 83 | "protocol": "unknown", 84 | "downloadClient": "string", 85 | "indexer": "string", 86 | "outputPath": "string", 87 | "downloadForced": false, 88 | "id": 0 89 | } 90 | ] 91 | } 92 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/queue.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 10, 4 | "sortKey": "timeleft", 5 | "sortDirection": "ascending", 6 | "totalRecords": 1, 7 | "records": [ 8 | { 9 | "authorId": 0, 10 | "bookId": 0, 11 | "author": { 12 | "authorMetadataId": 0, 13 | "status": "string", 14 | "ended": false, 15 | "authorName": "string", 16 | "authorNameLastFirst": "string", 17 | "foreignAuthorId": "0", 18 | "titleSlug": "0", 19 | "overview": "string", 20 | "links": [ 21 | { 22 | "url": "string", 23 | "name": "string" 24 | } 25 | ], 26 | "images": [ 27 | { 28 | "url": "string", 29 | "coverType": "poster", 30 | "extension": "string" 31 | } 32 | ], 33 | "path": "string", 34 | "qualityProfileId": 0, 35 | "metadataProfileId": 0, 36 | "monitored": true, 37 | "monitorNewItems": "string", 38 | "genres": ["string"], 39 | "cleanName": "string", 40 | "sortName": "string", 41 | "sortNameLastFirst": "string", 42 | "tags": [0], 43 | "added": "2020-11-06T23:38:03Z", 44 | "ratings": { 45 | "votes": 0, 46 | "value": 0.0, 47 | "popularity": 0.0 48 | }, 49 | "statistics": { 50 | "bookFileCount": 0, 51 | "bookCount": 0, 52 | "availableBookCount": 0, 53 | "totalBookCount": 0, 54 | "sizeOnDisk": 0, 55 | "percentOfBooks": 0 56 | }, 57 | "id": 72 58 | }, 59 | "quality": { 60 | "quality": { 61 | "id": 0, 62 | "name": "string" 63 | }, 64 | "revision": { 65 | "version": 0, 66 | "real": 0, 67 | "isRepack": false 68 | } 69 | }, 70 | "size": 200000, 71 | "title": "string", 72 | "sizeleft": 100000, 73 | "timeleft": "00:00:10", 74 | "estimatedCompletionTime": "2020-02-09T23:22:30Z", 75 | "status": "string", 76 | "trackedDownloadStatus": "string", 77 | "trackedDownloadState": "downloading", 78 | "statusMessages": [ 79 | { 80 | "title": "string", 81 | "messages": ["string"] 82 | } 83 | ], 84 | "downloadId": "string", 85 | "protocol": "unknown", 86 | "downloadClient": "string", 87 | "indexer": "string", 88 | "outputPath": "string", 89 | "downloadForced": false, 90 | "id": 0 91 | } 92 | ] 93 | } 94 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/manualimport.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "path": "string", 4 | "relativePath": "string", 5 | "folderName": "string", 6 | "name": "string", 7 | "size": 0, 8 | "series": { 9 | "title": "string", 10 | "sortTitle": "string", 11 | "status": "continuing", 12 | "ended": false, 13 | "overview": "string", 14 | "network": "string", 15 | "airTime": "00:00", 16 | "images": [ 17 | { 18 | "coverType": "banner", 19 | "url": "string" 20 | } 21 | ], 22 | "seasons": [ 23 | { 24 | "seasonNumber": 0, 25 | "monitored": false 26 | } 27 | ], 28 | "year": 0, 29 | "path": "string", 30 | "qualityProfileId": 0, 31 | "languageProfileId": 0, 32 | "seasonFolder": true, 33 | "monitored": true, 34 | "useSceneNumbering": false, 35 | "runtime": 0, 36 | "tvdbId": 0, 37 | "tvRageId": 0, 38 | "tvMazeId": 0, 39 | "firstAired": "2020-08-29T00:00:00Z", 40 | "seriesType": "standard", 41 | "cleanTitle": "string", 42 | "imdbId": "string", 43 | "titleSlug": "0", 44 | "certification": "string", 45 | "genres": ["string"], 46 | "tags": [0], 47 | "added": "2019-05-19T05:33:56.69425Z", 48 | "ratings": { 49 | "votes": 0, 50 | "value": 0.0 51 | }, 52 | "id": 0 53 | }, 54 | "seasonNumber": 0, 55 | "episodes": [ 56 | { 57 | "seriesId": 0, 58 | "episodeFileId": 0, 59 | "seasonNumber": 0, 60 | "episodeNumber": 0, 61 | "title": "string", 62 | "airDate": "2020-10-05", 63 | "airDateUtc": "2020-10-05T07:00:00Z", 64 | "overview": "string", 65 | "hasFile": true, 66 | "monitored": true, 67 | "absoluteEpisodeNumber": 0, 68 | "unverifiedSceneNumbering": false, 69 | "id": 0 70 | } 71 | ], 72 | "quality": { 73 | "quality": { 74 | "id": 0, 75 | "name": "string", 76 | "source": "web", 77 | "resolution": 0 78 | }, 79 | "revision": { 80 | "version": 0, 81 | "real": 0, 82 | "isRepack": false 83 | } 84 | }, 85 | "language": { 86 | "id": 0, 87 | "name": "string" 88 | }, 89 | "qualityWeight": 0, 90 | "downloadId": "string", 91 | "rejections": [ 92 | { 93 | "reason": "string", 94 | "type": "permanent" 95 | } 96 | ], 97 | "id": 0 98 | } 99 | ] 100 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/queue-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 10, 4 | "sortKey": "timeleft", 5 | "sortDirection": "ascending", 6 | "totalRecords": 1, 7 | "records": [ 8 | { 9 | "authorId": 0, 10 | "bookId": 0, 11 | "author": { 12 | "authorMetadataId": 0, 13 | "status": "string", 14 | "ended": false, 15 | "authorName": "string", 16 | "authorNameLastFirst": "string", 17 | "foreignAuthorId": "0", 18 | "titleSlug": "0", 19 | "overview": "string", 20 | "links": [ 21 | { 22 | "url": "string", 23 | "name": "string" 24 | } 25 | ], 26 | "images": [ 27 | { 28 | "url": "string", 29 | "coverType": "poster", 30 | "extension": "string" 31 | } 32 | ], 33 | "path": "string", 34 | "qualityProfileId": 0, 35 | "metadataProfileId": 0, 36 | "monitored": true, 37 | "monitorNewItems": "string", 38 | "genres": ["string"], 39 | "cleanName": "string", 40 | "sortName": "string", 41 | "sortNameLastFirst": "string", 42 | "tags": [0], 43 | "added": "2020-11-06T23:38:03Z", 44 | "ratings": { 45 | "votes": 0, 46 | "value": 0.0, 47 | "popularity": 0.0 48 | }, 49 | "statistics": { 50 | "bookFileCount": 0, 51 | "bookCount": 0, 52 | "availableBookCount": 0, 53 | "totalBookCount": 0, 54 | "sizeOnDisk": 0, 55 | "percentOfBooks": 0 56 | }, 57 | "id": 72 58 | }, 59 | "quality": { 60 | "quality": { 61 | "id": 0, 62 | "name": "string" 63 | }, 64 | "revision": { 65 | "version": 0, 66 | "real": 0, 67 | "isRepack": false 68 | } 69 | }, 70 | "size": 200000, 71 | "title": "string", 72 | "sizeleft": 100000, 73 | "status": "string", 74 | "trackedDownloadStatus": "string", 75 | "trackedDownloadState": "downloading", 76 | "statusMessages": [ 77 | { 78 | "title": "string", 79 | "messages": ["string"] 80 | } 81 | ], 82 | "downloadId": "string", 83 | "protocol": "unknown", 84 | "downloadClient": "string", 85 | "indexer": "string", 86 | "outputPath": "string", 87 | "downloadForced": false, 88 | "id": 0 89 | } 90 | ] 91 | } 92 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/episode.json: -------------------------------------------------------------------------------- 1 | { 2 | "seriesId": 0, 3 | "episodeFileId": 0, 4 | "seasonNumber": 0, 5 | "episodeNumber": 0, 6 | "title": "string", 7 | "airDate": "2009-09-17", 8 | "airDateUtc": "2009-09-18T02:00:00Z", 9 | "overview": "string", 10 | "episodeFile": { 11 | "seriesId": 0, 12 | "seasonNumber": 0, 13 | "relativePath": "string", 14 | "path": "string", 15 | "size": 0, 16 | "dateAdded": "2019-06-13T09:08:03.775081Z", 17 | "releaseGroup": "string", 18 | "language": { 19 | "id": 0, 20 | "name": "string" 21 | }, 22 | "quality": { 23 | "quality": { 24 | "id": 0, 25 | "name": "string", 26 | "source": "string", 27 | "resolution": 0 28 | }, 29 | "revision": { 30 | "version": 0, 31 | "real": 0, 32 | "isRepack": false 33 | } 34 | }, 35 | "mediaInfo": { 36 | "audioBitrate": 0, 37 | "audioChannels": 0.0, 38 | "audioCodec": "string", 39 | "audioLanguages": "string", 40 | "audioStreamCount": 0, 41 | "videoBitDepth": 0, 42 | "videoBitrate": 0, 43 | "videoCodec": "string", 44 | "videoFps": 0.0, 45 | "resolution": "string", 46 | "runTime": "00:00", 47 | "scanType": "string", 48 | "subtitles": "string" 49 | }, 50 | "qualityCutoffNotMet": false, 51 | "languageCutoffNotMet": false, 52 | "id": 0 53 | }, 54 | "hasFile": true, 55 | "monitored": true, 56 | "absoluteEpisodeNumber": 0, 57 | "unverifiedSceneNumbering": false, 58 | "series": { 59 | "title": "string", 60 | "sortTitle": "string", 61 | "status": "string", 62 | "ended": true, 63 | "overview": "string", 64 | "network": "string", 65 | "airTime": "00:00", 66 | "images": [ 67 | { 68 | "coverType": "poster", 69 | "url": "string" 70 | } 71 | ], 72 | "seasons": [ 73 | { 74 | "seasonNumber": 0, 75 | "monitored": false 76 | } 77 | ], 78 | "year": 0, 79 | "path": "string", 80 | "qualityProfileId": 0, 81 | "languageProfileId": 0, 82 | "seasonFolder": true, 83 | "monitored": true, 84 | "useSceneNumbering": false, 85 | "runtime": 0, 86 | "tvdbId": 0, 87 | "tvRageId": 0, 88 | "tvMazeId": 0, 89 | "firstAired": "2017-04-05T00:00:00Z", 90 | "seriesType": "string", 91 | "cleanTitle": "string", 92 | "imdbId": "string", 93 | "titleSlug": "0", 94 | "certification": "string", 95 | "genres": ["string"], 96 | "tags": [0], 97 | "added": "2019-05-19T05:33:42.24392Z", 98 | "ratings": { 99 | "votes": 0, 100 | "value": 0.0 101 | }, 102 | "id": 0 103 | }, 104 | "id": 0 105 | } 106 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/parse.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "title": "string", 4 | "parsedEpisodeInfo": { 5 | "releaseTitle": "string", 6 | "seriesTitle": "string", 7 | "seriesTitleInfo": { 8 | "title": "string", 9 | "titleWithoutYear": "string", 10 | "year": 0 11 | }, 12 | "quality": { 13 | "quality": { 14 | "id": 0, 15 | "name": "string", 16 | "source": "string", 17 | "resolution": 0 18 | }, 19 | "revision": { 20 | "version": 0, 21 | "real": 0, 22 | "isRepack": false 23 | } 24 | }, 25 | "seasonNumber": 0, 26 | "episodeNumbers": [0], 27 | "absoluteEpisodeNumbers": [0], 28 | "specialAbsoluteEpisodeNumbers": [0], 29 | "language": { 30 | "id": 0, 31 | "name": "string" 32 | }, 33 | "fullSeason": false, 34 | "isPartialSeason": false, 35 | "isMultiSeason": false, 36 | "isSeasonExtra": false, 37 | "special": false, 38 | "releaseHash": "string", 39 | "seasonPart": 0, 40 | "releaseTokens": "string", 41 | "isDaily": false, 42 | "isAbsoluteNumbering": false, 43 | "isPossibleSpecialEpisode": false, 44 | "isPossibleSceneSeasonSpecial": false 45 | }, 46 | "series": { 47 | "title": "string", 48 | "sortTitle": "string", 49 | "status": "string", 50 | "ended": true, 51 | "overview": "string", 52 | "network": "string", 53 | "airTime": "00:00", 54 | "images": [ 55 | { 56 | "coverType": "poster", 57 | "url": "string" 58 | } 59 | ], 60 | "seasons": [ 61 | { 62 | "seasonNumber": 0, 63 | "monitored": false 64 | } 65 | ], 66 | "year": 0, 67 | "path": "string", 68 | "qualityProfileId": 0, 69 | "languageProfileId": 0, 70 | "seasonFolder": true, 71 | "monitored": true, 72 | "useSceneNumbering": false, 73 | "runtime": 0, 74 | "tvdbId": 0, 75 | "tvRageId": 0, 76 | "tvMazeId": 0, 77 | "firstAired": "2011-09-26T00:00:00Z", 78 | "seriesType": "string", 79 | "cleanTitle": "string", 80 | "imdbId": "string", 81 | "titleSlug": "0", 82 | "certification": "string", 83 | "genres": ["string"], 84 | "tags": [0], 85 | "added": "2020-05-19T05:33:31.868402Z", 86 | "ratings": { 87 | "votes": 0, 88 | "value": 0.0 89 | }, 90 | "id": 0 91 | }, 92 | "episodes": [ 93 | { 94 | "seriesId": 0, 95 | "episodeFileId": 0, 96 | "seasonNumber": 0, 97 | "episodeNumber": 0, 98 | "title": "string", 99 | "airDate": "2010-08-26", 100 | "airDateUtc": "2006-09-27T00:00:00Z", 101 | "overview": "string", 102 | "hasFile": true, 103 | "monitored": false, 104 | "absoluteEpisodeNumber": 0, 105 | "unverifiedSceneNumbering": false, 106 | "id": 0 107 | } 108 | ] 109 | } 110 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/album.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "string", 3 | "disambiguation": "string", 4 | "overview": "string", 5 | "artistId": 0, 6 | "foreignAlbumId": "string", 7 | "monitored": true, 8 | "anyReleaseOk": true, 9 | "profileId": 0, 10 | "duration": 0, 11 | "albumType": "string", 12 | "secondaryTypes": [ 13 | { 14 | "id": 0, 15 | "name": "string" 16 | } 17 | ], 18 | "mediumCount": 0, 19 | "ratings": { 20 | "votes": 0, 21 | "value": 0.0 22 | }, 23 | "releaseDate": "2010-08-23T00:00:00Z", 24 | "releases": [ 25 | { 26 | "id": 0, 27 | "albumId": 0, 28 | "foreignReleaseId": "string", 29 | "title": "string", 30 | "status": "string", 31 | "duration": 0, 32 | "trackCount": 0, 33 | "media": [ 34 | { 35 | "mediumNumber": 0, 36 | "mediumName": "string", 37 | "mediumFormat": "string" 38 | } 39 | ], 40 | "mediumCount": 0, 41 | "disambiguation": "string", 42 | "country": ["string"], 43 | "label": ["string"], 44 | "format": "string", 45 | "monitored": true 46 | } 47 | ], 48 | "genres": ["string"], 49 | "media": [ 50 | { 51 | "mediumNumber": 0, 52 | "mediumName": "string", 53 | "mediumFormat": "string" 54 | } 55 | ], 56 | "artist": { 57 | "status": "ended", 58 | "ended": true, 59 | "artistName": "string", 60 | "foreignArtistId": "string", 61 | "tadbId": 0, 62 | "discogsId": 0, 63 | "overview": "string", 64 | "artistType": "string", 65 | "disambiguation": "string", 66 | "links": [ 67 | { 68 | "url": "string", 69 | "name": "string" 70 | } 71 | ], 72 | "images": [ 73 | { 74 | "url": "string", 75 | "coverType": "poster", 76 | "extension": "string" 77 | } 78 | ], 79 | "path": "string", 80 | "qualityProfileId": 0, 81 | "metadataProfileId": 0, 82 | "monitored": true, 83 | "genres": ["string"], 84 | "cleanName": "string", 85 | "sortName": "string", 86 | "tags": [0], 87 | "added": "2021-08-21T15:35:54.398878Z", 88 | "ratings": { 89 | "votes": 0, 90 | "value": 0.0 91 | }, 92 | "statistics": { 93 | "albumCount": 0, 94 | "trackFileCount": 0, 95 | "trackCount": 0, 96 | "totalTrackCount": 0, 97 | "sizeOnDisk": 0, 98 | "percentOfTracks": 0.0 99 | }, 100 | "id": 0 101 | }, 102 | "images": [ 103 | { 104 | "url": "string", 105 | "coverType": "poster", 106 | "extension": "string" 107 | } 108 | ], 109 | "links": [ 110 | { 111 | "url": "string", 112 | "name": "string" 113 | } 114 | ], 115 | "statistics": { 116 | "trackFileCount": 0, 117 | "trackCount": 0, 118 | "totalTrackCount": 0, 119 | "sizeOnDisk": 0, 120 | "percentOfTracks": 0.0 121 | }, 122 | "id": 0 123 | } 124 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/calendar.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "string", 4 | "originalTitle": "string", 5 | "alternateTitles": [], 6 | "secondaryYearSourceId": 0, 7 | "sortTitle": "string", 8 | "sizeOnDisk": 0, 9 | "status": "string", 10 | "overview": "string", 11 | "physicalRelease": "2021-12-03T00:00:00Z", 12 | "digitalRelease": "2020-08-11T00:00:00Z", 13 | "images": [ 14 | { 15 | "coverType": "poster", 16 | "url": "string" 17 | } 18 | ], 19 | "website": "string", 20 | "year": 0, 21 | "hasFile": true, 22 | "youTubeTrailerId": "string", 23 | "studio": "string", 24 | "path": "string", 25 | "qualityProfileId": 0, 26 | "monitored": true, 27 | "minimumAvailability": "string", 28 | "isAvailable": true, 29 | "folderName": "string", 30 | "runtime": 0, 31 | "cleanTitle": "string", 32 | "imdbId": "string", 33 | "tmdbId": 0, 34 | "titleSlug": "0", 35 | "genres": ["string"], 36 | "tags": [], 37 | "added": "2020-07-16T13:25:37Z", 38 | "ratings": { 39 | "imdb": { 40 | "votes": 0, 41 | "value": 0.0, 42 | "type": "string" 43 | }, 44 | "tmdb": { 45 | "votes": 0, 46 | "value": 0.0, 47 | "type": "string" 48 | }, 49 | "metacritic": { 50 | "votes": 0, 51 | "value": 0, 52 | "type": "string" 53 | }, 54 | "rottenTomatoes": { 55 | "votes": 0, 56 | "value": 0, 57 | "type": "string" 58 | } 59 | }, 60 | "movieFile": { 61 | "movieId": 0, 62 | "relativePath": "string", 63 | "path": "string", 64 | "size": 0, 65 | "dateAdded": "2021-06-01T04:08:20Z", 66 | "sceneName": "string", 67 | "indexerFlags": 0, 68 | "quality": { 69 | "quality": { 70 | "id": 0, 71 | "name": "string", 72 | "source": "string", 73 | "resolution": 0, 74 | "modifier": "string" 75 | }, 76 | "revision": { 77 | "version": 0, 78 | "real": 0, 79 | "isRepack": false 80 | } 81 | }, 82 | "mediaInfo": { 83 | "audioBitrate": 0, 84 | "audioChannels": 0.0, 85 | "audioCodec": "string", 86 | "audioLanguages": "string", 87 | "audioStreamCount": 0, 88 | "videoBitDepth": 0, 89 | "videoBitrate": 0, 90 | "videoCodec": "string", 91 | "videoFps": 0.0, 92 | "resolution": "string", 93 | "runTime": "00:00:00", 94 | "scanType": "string", 95 | "subtitles": "string" 96 | }, 97 | "originalFilePath": "string", 98 | "qualityCutoffNotMet": false, 99 | "languages": [ 100 | { 101 | "id": 0, 102 | "name": "string" 103 | } 104 | ], 105 | "releaseGroup": "string", 106 | "edition": "string", 107 | "id": 0 108 | }, 109 | "id": 0 110 | } 111 | ] -------------------------------------------------------------------------------- /tests/fixtures/lidarr/album-lookup.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "string", 4 | "disambiguation": "string", 5 | "overview": "string", 6 | "artistId": 0, 7 | "foreignAlbumId": "string", 8 | "monitored": false, 9 | "anyReleaseOk": true, 10 | "profileId": 0, 11 | "duration": 0, 12 | "albumType": "string", 13 | "secondaryTypes": [ 14 | { 15 | "id": 0, 16 | "name": "string" 17 | } 18 | ], 19 | "mediumCount": 0, 20 | "ratings": { 21 | "votes": 0, 22 | "value": 0.0 23 | }, 24 | "releaseDate": "2018-03-09T00:00:00Z", 25 | "releases": [ 26 | { 27 | "id": 0, 28 | "albumId": 0, 29 | "foreignReleaseId": "string", 30 | "title": "string", 31 | "status": "string", 32 | "duration": 0, 33 | "trackCount": 0, 34 | "media": [ 35 | { 36 | "mediumNumber": 0, 37 | "mediumName": "string", 38 | "mediumFormat": "string" 39 | } 40 | ], 41 | "mediumCount": 0, 42 | "disambiguation": "string", 43 | "country": ["string"], 44 | "label": ["string"], 45 | "format": "string", 46 | "monitored": true 47 | } 48 | ], 49 | "genres": ["string"], 50 | "media": [ 51 | { 52 | "mediumNumber": 0, 53 | "mediumName": "string", 54 | "mediumFormat": "string" 55 | } 56 | ], 57 | "artist": { 58 | "status": "string", 59 | "ended": false, 60 | "artistName": "string", 61 | "foreignArtistId": "string", 62 | "tadbId": 0, 63 | "discogsId": 0, 64 | "overview": "string", 65 | "artistType": "string", 66 | "disambiguation": "string", 67 | "links": [ 68 | { 69 | "url": "string", 70 | "name": "string" 71 | } 72 | ], 73 | "images": [ 74 | { 75 | "url": "string", 76 | "coverType": "poster", 77 | "extension": "string" 78 | } 79 | ], 80 | "path": "string", 81 | "qualityProfileId": 0, 82 | "metadataProfileId": 0, 83 | "monitored": true, 84 | "genres": ["string"], 85 | "cleanName": "string", 86 | "sortName": "string", 87 | "tags": [0], 88 | "added": "2021-08-21T15:36:08.777408Z", 89 | "ratings": { 90 | "votes": 0, 91 | "value": 0.0 92 | }, 93 | "statistics": { 94 | "albumCount": 0, 95 | "trackFileCount": 0, 96 | "trackCount": 0, 97 | "totalTrackCount": 0, 98 | "sizeOnDisk": 0, 99 | "percentOfTracks": 0.0 100 | }, 101 | "id": 0 102 | }, 103 | "images": [ 104 | { 105 | "url": "string", 106 | "coverType": "poster", 107 | "extension": "string" 108 | } 109 | ], 110 | "links": [ 111 | { 112 | "url": "string", 113 | "name": "string" 114 | } 115 | ], 116 | "remoteCover": "string" 117 | } 118 | ] 119 | -------------------------------------------------------------------------------- /tests/fixtures/sonarr/calendar-extended.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "seriesId": 0, 4 | "episodeFileId": 0, 5 | "seasonNumber": 0, 6 | "episodeNumber": 0, 7 | "title": "string", 8 | "airDate": "2017-01-26", 9 | "airDateUtc": "2017-01-27T01:30:00Z", 10 | "overview": "string", 11 | "episodeFile": { 12 | "seriesId": 0, 13 | "seasonNumber": 0, 14 | "relativePath": "string", 15 | "path": "string", 16 | "size": 0, 17 | "dateAdded": "2019-06-13T09:08:03.775081Z", 18 | "releaseGroup": "string", 19 | "language": { 20 | "id": 0, 21 | "name": "string" 22 | }, 23 | "quality": { 24 | "quality": { 25 | "id": 0, 26 | "name": "string", 27 | "source": "string", 28 | "resolution": 0 29 | }, 30 | "revision": { 31 | "version": 0, 32 | "real": 0, 33 | "isRepack": false 34 | } 35 | }, 36 | "mediaInfo": { 37 | "audioBitrate": 0, 38 | "audioChannels": 0.0, 39 | "audioCodec": "string", 40 | "audioLanguages": "string", 41 | "audioStreamCount": 0, 42 | "videoBitDepth": 0, 43 | "videoBitrate": 0, 44 | "videoCodec": "string", 45 | "videoFps": 0.0, 46 | "resolution": "string", 47 | "runTime": "00:00", 48 | "scanType": "string", 49 | "subtitles": "string" 50 | }, 51 | "qualityCutoffNotMet": false, 52 | "languageCutoffNotMet": false, 53 | "id": 0 54 | }, 55 | "hasFile": true, 56 | "monitored": true, 57 | "sceneEpisodeNumber": 0, 58 | "sceneSeasonNumber": 0, 59 | "tvDbEpisodeId": 0, 60 | "unverifiedSceneNumbering": false, 61 | "downloading": false, 62 | "series": { 63 | "title": "string", 64 | "sortTitle": "string", 65 | "status": "string", 66 | "ended": true, 67 | "overview": "string", 68 | "network": "string", 69 | "airTime": "00:00", 70 | "images": [ 71 | { 72 | "coverType": "poster", 73 | "url": "string" 74 | } 75 | ], 76 | "seasons": [ 77 | { 78 | "seasonNumber": 0, 79 | "monitored": false 80 | } 81 | ], 82 | "year": 0, 83 | "path": "string", 84 | "qualityProfileId": 0, 85 | "languageProfileId": 0, 86 | "seasonFolder": true, 87 | "monitored": true, 88 | "useSceneNumbering": false, 89 | "runtime": 0, 90 | "tvdbId": 0, 91 | "tvRageId": 0, 92 | "tvMazeId": 0, 93 | "firstAired": "2017-04-05T00:00:00Z", 94 | "seriesType": "string", 95 | "cleanTitle": "string", 96 | "imdbId": "string", 97 | "titleSlug": "0", 98 | "certification": "string", 99 | "genres": ["string"], 100 | "tags": [0], 101 | "added": "2019-05-19T05:33:42.24392Z", 102 | "ratings": { 103 | "votes": 0, 104 | "value": 0.0 105 | }, 106 | "id": 0 107 | }, 108 | "id": 0 109 | } 110 | ] 111 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/book-lookup.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "string", 4 | "authorTitle": "string", 5 | "seriesTitle": "string", 6 | "disambiguation": "string", 7 | "overview": "string", 8 | "authorId": 0, 9 | "foreignBookId": "string", 10 | "titleSlug": "0", 11 | "monitored": false, 12 | "anyEditionOk": true, 13 | "ratings": { 14 | "votes": 0, 15 | "value": 0.0, 16 | "popularity": 0.0 17 | }, 18 | "releaseDate": "1869-01-01T00:00:00Z", 19 | "pageCount": 0, 20 | "genres": ["string"], 21 | "author": { 22 | "authorMetadataId": 0, 23 | "status": "string", 24 | "ended": false, 25 | "authorName": "string", 26 | "authorNameLastFirst": "string", 27 | "foreignAuthorId": "string", 28 | "titleSlug": "0", 29 | "links": [ 30 | { 31 | "url": "string", 32 | "name": "string" 33 | } 34 | ], 35 | "images": [ 36 | { 37 | "url": "string", 38 | "coverType": "poster", 39 | "extension": "string" 40 | } 41 | ], 42 | "qualityProfileId": 0, 43 | "metadataProfileId": 0, 44 | "monitored": false, 45 | "monitorNewItems": "string", 46 | "genres": ["string"], 47 | "cleanName": "string", 48 | "sortName": "string", 49 | "sortNameLastFirst": "string", 50 | "tags": [0], 51 | "added": "0001-01-01T04:57:00Z", 52 | "ratings": { 53 | "votes": 0, 54 | "value": 0.0, 55 | "popularity": 0.0 56 | }, 57 | "statistics": { 58 | "bookFileCount": 0, 59 | "bookCount": 0, 60 | "availableBookCount": 0, 61 | "totalBookCount": 0, 62 | "sizeOnDisk": 0, 63 | "percentOfBooks": 0.0 64 | } 65 | }, 66 | "images": [ 67 | { 68 | "url": "string", 69 | "coverType": "cover", 70 | "extension": ".jpg" 71 | } 72 | ], 73 | "links": [ 74 | { 75 | "url": "string", 76 | "name": "string" 77 | } 78 | ], 79 | "added": "0001-01-01T04:57:00Z", 80 | "remoteCover": "string", 81 | "editions": [ 82 | { 83 | "bookId": 0, 84 | "foreignEditionId": "string", 85 | "titleSlug": "0", 86 | "title": "string", 87 | "language": "string", 88 | "overview": "string", 89 | "isEbook": false, 90 | "disambiguation": "string", 91 | "publisher": "string", 92 | "pageCount": 0, 93 | "releaseDate": "1998-06-25T00:00:00Z", 94 | "images": [ 95 | { 96 | "url": "string", 97 | "coverType": "cover", 98 | "extension": ".jpg" 99 | } 100 | ], 101 | "links": [ 102 | { 103 | "url": "string", 104 | "name": "string" 105 | } 106 | ], 107 | "ratings": { 108 | "votes": 0, 109 | "value": 0.0, 110 | "popularity": 0.0 111 | }, 112 | "monitored": true, 113 | "manualAdd": false, 114 | "grabbed": false 115 | } 116 | ], 117 | "grabbed": false 118 | } 119 | ] -------------------------------------------------------------------------------- /tests/fixtures/lidarr/calendar.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "string", 4 | "disambiguation": "string", 5 | "overview": "string", 6 | "artistId": 0, 7 | "foreignAlbumId": "string", 8 | "monitored": true, 9 | "anyReleaseOk": true, 10 | "profileId": 0, 11 | "duration": 0, 12 | "albumType": "string", 13 | "secondaryTypes": [ 14 | { 15 | "id": 0, 16 | "name": "string" 17 | } 18 | ], 19 | "mediumCount": 0, 20 | "ratings": { 21 | "votes": 0, 22 | "value": 0.0 23 | }, 24 | "releaseDate": "2020-02-14T00:00:00Z", 25 | "releases": [ 26 | { 27 | "id": 0, 28 | "albumId": 0, 29 | "foreignReleaseId": "string", 30 | "title": "string", 31 | "status": "string", 32 | "duration": 0, 33 | "trackCount": 0, 34 | "media": [ 35 | { 36 | "mediumNumber": 0, 37 | "mediumName": "string", 38 | "mediumFormat": "string" 39 | } 40 | ], 41 | "mediumCount": 0, 42 | "disambiguation": "string", 43 | "country": ["string"], 44 | "label": ["string"], 45 | "format": "string", 46 | "monitored": true 47 | } 48 | ], 49 | "genres": ["string"], 50 | "media": [ 51 | { 52 | "mediumNumber": 0, 53 | "mediumName": "string", 54 | "mediumFormat": "string" 55 | } 56 | ], 57 | "artist": { 58 | "status": "ended", 59 | "ended": true, 60 | "artistName": "string", 61 | "foreignArtistId": "string", 62 | "tadbId": 0, 63 | "discogsId": 0, 64 | "overview": "string", 65 | "artistType": "string", 66 | "disambiguation": "string", 67 | "links": [ 68 | { 69 | "url": "string", 70 | "name": "string" 71 | } 72 | ], 73 | "images": [ 74 | { 75 | "url": "string", 76 | "coverType": "poster", 77 | "extension": "string" 78 | } 79 | ], 80 | "path": "string", 81 | "qualityProfileId": 0, 82 | "metadataProfileId": 0, 83 | "monitored": true, 84 | "genres": ["string"], 85 | "cleanName": "string", 86 | "sortName": "string", 87 | "tags": [0], 88 | "added": "2021-08-21T15:37:39.2259471Z", 89 | "ratings": { 90 | "votes": 0, 91 | "value": 0.0 92 | }, 93 | "statistics": { 94 | "albumCount": 0, 95 | "trackFileCount": 0, 96 | "trackCount": 0, 97 | "totalTrackCount": 0, 98 | "sizeOnDisk": 0, 99 | "percentOfTracks": 0.0 100 | }, 101 | "id": 0 102 | }, 103 | "images": [ 104 | { 105 | "url": "string", 106 | "coverType": "poster", 107 | "extension": "string" 108 | } 109 | ], 110 | "links": [ 111 | { 112 | "url": "string", 113 | "name": "string" 114 | } 115 | ], 116 | "statistics": { 117 | "trackFileCount": 0, 118 | "trackCount": 0, 119 | "totalTrackCount": 0, 120 | "sizeOnDisk": 0, 121 | "percentOfTracks": 0.0 122 | }, 123 | "id": 0 124 | } 125 | ] 126 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/movie.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 0, 3 | "title": "string", 4 | "originalTitle": "string", 5 | "alternateTitles": [ 6 | { 7 | "sourceType": "tmdb", 8 | "movieId": 1, 9 | "title": "string", 10 | "sourceId": 0, 11 | "votes": 0, 12 | "voteCount": 0, 13 | "language": { 14 | "id": 1, 15 | "name": "string" 16 | }, 17 | "id": 1 18 | } 19 | ], 20 | "sortTitle": "string", 21 | "sizeOnDisk": 0, 22 | "overview": "string", 23 | "inCinemas": "2020-11-06T00:00:00Z", 24 | "physicalRelease": "2019-03-19T00:00:00Z", 25 | "images": [ 26 | { 27 | "coverType": "poster", 28 | "url": "string", 29 | "remoteUrl": "string" 30 | } 31 | ], 32 | "website": "string", 33 | "year": 0, 34 | "hasFile": true, 35 | "youTubeTrailerId": "string", 36 | "studio": "string", 37 | "path": "string", 38 | "rootFolderPath": "string", 39 | "qualityProfileId": 0, 40 | "monitored": true, 41 | "minimumAvailability": "string", 42 | "isAvailable": true, 43 | "folderName": "string", 44 | "runtime": 0, 45 | "cleanTitle": "string", 46 | "imdbId": "string", 47 | "tmdbId": 0, 48 | "titleSlug": "0", 49 | "certification": "string", 50 | "genres": ["string"], 51 | "tags": [0], 52 | "added": "2018-12-28T05:56:49Z", 53 | "ratings": { 54 | "imdb": { 55 | "votes": 0, 56 | "value": 0.0, 57 | "type": "string" 58 | }, 59 | "tmdb": { 60 | "votes": 0, 61 | "value": 0.0, 62 | "type": "string" 63 | }, 64 | "metacritic": { 65 | "votes": 0, 66 | "value": 0, 67 | "type": "string" 68 | }, 69 | "rottenTomatoes": { 70 | "votes": 0, 71 | "value": 0, 72 | "type": "string" 73 | } 74 | }, 75 | "movieFile": { 76 | "movieId": 0, 77 | "relativePath": "string", 78 | "path": "string", 79 | "size": 0, 80 | "dateAdded": "2020-11-26T02:00:35Z", 81 | "indexerFlags": 1, 82 | "quality": { 83 | "quality": { 84 | "id": 0, 85 | "name": "string", 86 | "source": "string", 87 | "resolution": 0, 88 | "modifier": "string" 89 | }, 90 | "revision": { 91 | "version": 0, 92 | "real": 0, 93 | "isRepack": false 94 | } 95 | }, 96 | "mediaInfo": { 97 | "audioBitrate": 0, 98 | "audioChannels": 0.0, 99 | "audioCodec": "string", 100 | "audioLanguages": "string", 101 | "audioStreamCount": 0, 102 | "videoBitDepth": 0, 103 | "videoBitrate": 0, 104 | "videoCodec": "string", 105 | "videoFps": 0.0, 106 | "resolution": "string", 107 | "runTime": "00:00:00", 108 | "scanType": "string", 109 | "subtitles": "string" 110 | }, 111 | "originalFilePath": "string", 112 | "qualityCutoffNotMet": true, 113 | "languages": [ 114 | { 115 | "id": 0, 116 | "name": "string" 117 | } 118 | ], 119 | "edition": "string", 120 | "id": 0 121 | }, 122 | "collection": { 123 | "name": "string", 124 | "tmdbId": 0, 125 | "images": [ 126 | { 127 | "coverType": "poster", 128 | "url": "string", 129 | "remoteUrl": "string" 130 | } 131 | ] 132 | }, 133 | "status": "string" 134 | } 135 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/movie-list.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "title": "string", 5 | "originalTitle": "string", 6 | "alternateTitles": [ 7 | { 8 | "sourceType": "tmdb", 9 | "movieId": 1, 10 | "title": "string", 11 | "sourceId": 0, 12 | "votes": 0, 13 | "voteCount": 0, 14 | "language": { 15 | "id": 1, 16 | "name": "string" 17 | }, 18 | "id": 1 19 | } 20 | ], 21 | "sortTitle": "string", 22 | "sizeOnDisk": 0, 23 | "overview": "string", 24 | "inCinemas": "2020-11-06T00:00:00Z", 25 | "physicalRelease": "2019-03-19T00:00:00Z", 26 | "images": [ 27 | { 28 | "coverType": "poster", 29 | "url": "string", 30 | "remoteUrl": "string" 31 | } 32 | ], 33 | "website": "string", 34 | "year": 0, 35 | "hasFile": true, 36 | "youTubeTrailerId": "string", 37 | "studio": "string", 38 | "path": "string", 39 | "rootFolderPath": "string", 40 | "qualityProfileId": 0, 41 | "monitored": true, 42 | "minimumAvailability": "string", 43 | "isAvailable": true, 44 | "folderName": "string", 45 | "runtime": 0, 46 | "cleanTitle": "string", 47 | "imdbId": "string", 48 | "tmdbId": 0, 49 | "titleSlug": "0", 50 | "certification": "string", 51 | "genres": ["string"], 52 | "tags": [0], 53 | "added": "2018-12-28T05:56:49Z", 54 | "ratings": { 55 | "imdb": { 56 | "votes": 0, 57 | "value": 0.0, 58 | "type": "string" 59 | }, 60 | "tmdb": { 61 | "votes": 0, 62 | "value": 0.0, 63 | "type": "string" 64 | }, 65 | "metacritic": { 66 | "votes": 0, 67 | "value": 0, 68 | "type": "string" 69 | }, 70 | "rottenTomatoes": { 71 | "votes": 0, 72 | "value": 0, 73 | "type": "string" 74 | } 75 | }, 76 | "movieFile": { 77 | "movieId": 0, 78 | "relativePath": "string", 79 | "path": "string", 80 | "size": 0, 81 | "dateAdded": "2020-11-26T02:00:35Z", 82 | "indexerFlags": 1, 83 | "quality": { 84 | "quality": { 85 | "id": 0, 86 | "name": "string", 87 | "source": "string", 88 | "resolution": 0, 89 | "modifier": "string" 90 | }, 91 | "revision": { 92 | "version": 0, 93 | "real": 0, 94 | "isRepack": false 95 | } 96 | }, 97 | "mediaInfo": { 98 | "audioBitrate": 0, 99 | "audioChannels": 0.0, 100 | "audioCodec": "string", 101 | "audioLanguages": "string", 102 | "audioStreamCount": 0, 103 | "videoBitDepth": 0, 104 | "videoBitrate": 0, 105 | "videoCodec": "string", 106 | "videoFps": 0.0, 107 | "resolution": "string", 108 | "runTime": "00:00:00", 109 | "scanType": "string", 110 | "subtitles": "string" 111 | }, 112 | "originalFilePath": "string", 113 | "qualityCutoffNotMet": true, 114 | "languages": [ 115 | { 116 | "id": 0, 117 | "name": "string" 118 | } 119 | ], 120 | "edition": "string", 121 | "id": 0 122 | }, 123 | "collection": { 124 | "name": "string", 125 | "tmdbId": 0, 126 | "images": [ 127 | { 128 | "coverType": "poster", 129 | "url": "string", 130 | "remoteUrl": "string" 131 | } 132 | ] 133 | }, 134 | "status": "string" 135 | } 136 | ] 137 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/movie-import.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 0, 4 | "title": "string", 5 | "originalTitle": "string", 6 | "alternateTitles": [ 7 | { 8 | "sourceType": "tmdb", 9 | "movieId": 1, 10 | "title": "string", 11 | "sourceId": 0, 12 | "votes": 0, 13 | "voteCount": 0, 14 | "language": { 15 | "id": 1, 16 | "name": "string" 17 | }, 18 | "id": 1 19 | } 20 | ], 21 | "sortTitle": "string", 22 | "sizeOnDisk": 0, 23 | "overview": "string", 24 | "inCinemas": "2020-11-06T00:00:00Z", 25 | "physicalRelease": "2019-03-19T00:00:00Z", 26 | "images": [ 27 | { 28 | "coverType": "poster", 29 | "url": "string", 30 | "remoteUrl": "string" 31 | } 32 | ], 33 | "website": "string", 34 | "year": 0, 35 | "hasFile": true, 36 | "youTubeTrailerId": "string", 37 | "studio": "string", 38 | "path": "string", 39 | "rootFolderPath": "string", 40 | "qualityProfileId": 0, 41 | "monitored": true, 42 | "minimumAvailability": "string", 43 | "isAvailable": true, 44 | "folderName": "string", 45 | "runtime": 0, 46 | "cleanTitle": "string", 47 | "imdbId": "string", 48 | "tmdbId": 0, 49 | "titleSlug": "0", 50 | "certification": "string", 51 | "genres": ["string"], 52 | "tags": [0], 53 | "added": "2018-12-28T05:56:49Z", 54 | "ratings": { 55 | "imdb": { 56 | "votes": 0, 57 | "value": 0.0, 58 | "type": "string" 59 | }, 60 | "tmdb": { 61 | "votes": 0, 62 | "value": 0.0, 63 | "type": "string" 64 | }, 65 | "metacritic": { 66 | "votes": 0, 67 | "value": 0, 68 | "type": "string" 69 | }, 70 | "rottenTomatoes": { 71 | "votes": 0, 72 | "value": 0, 73 | "type": "string" 74 | } 75 | }, 76 | "movieFile": { 77 | "movieId": 0, 78 | "relativePath": "string", 79 | "path": "string", 80 | "size": 0, 81 | "dateAdded": "2020-11-26T02:00:35Z", 82 | "indexerFlags": 1, 83 | "quality": { 84 | "quality": { 85 | "id": 0, 86 | "name": "string", 87 | "source": "string", 88 | "resolution": 0, 89 | "modifier": "string" 90 | }, 91 | "revision": { 92 | "version": 0, 93 | "real": 0, 94 | "isRepack": false 95 | } 96 | }, 97 | "mediaInfo": { 98 | "audioBitrate": 0, 99 | "audioChannels": 0.0, 100 | "audioCodec": "string", 101 | "audioLanguages": "string", 102 | "audioStreamCount": 0, 103 | "videoBitDepth": 0, 104 | "videoBitrate": 0, 105 | "videoCodec": "string", 106 | "videoFps": 0.0, 107 | "resolution": "string", 108 | "runTime": "00:00:00", 109 | "scanType": "string", 110 | "subtitles": "string" 111 | }, 112 | "originalFilePath": "string", 113 | "qualityCutoffNotMet": true, 114 | "languages": [ 115 | { 116 | "id": 0, 117 | "name": "string" 118 | } 119 | ], 120 | "edition": "string", 121 | "id": 0 122 | }, 123 | "collection": { 124 | "name": "string", 125 | "tmdbId": 0, 126 | "images": [ 127 | { 128 | "coverType": "poster", 129 | "url": "string", 130 | "remoteUrl": "string" 131 | } 132 | ] 133 | }, 134 | "status": "string" 135 | } 136 | ] 137 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/artist.json: -------------------------------------------------------------------------------- 1 | { 2 | "status": "string", 3 | "ended": true, 4 | "artistName": "string", 5 | "foreignArtistId": "string", 6 | "tadbId": 0, 7 | "discogsId": 0, 8 | "overview": "string", 9 | "artistType": "string", 10 | "disambiguation": "string", 11 | "links": [ 12 | { 13 | "url": "string", 14 | "name": "string" 15 | } 16 | ], 17 | "nextAlbum": { 18 | "artistMetadataId": 0, 19 | "foreignAlbumId": "string", 20 | "oldForeignAlbumIds": ["string"], 21 | "title": "string", 22 | "overview": "string", 23 | "disambiguation": "string", 24 | "releaseDate": "2011-08-23T00:00:00Z", 25 | "images": [ 26 | { 27 | "url": "string", 28 | "coverType": "poster", 29 | "extension": "string" 30 | } 31 | ], 32 | "links": [ 33 | { 34 | "url": "string", 35 | "name": "string" 36 | } 37 | ], 38 | "genres": ["string"], 39 | "albumType": "string", 40 | "secondaryTypes": [ 41 | { 42 | "id": 0, 43 | "name": "string" 44 | } 45 | ], 46 | "ratings": { 47 | "votes": 0, 48 | "value": 0.0 49 | }, 50 | "cleanTitle": "string", 51 | "profileId": 0, 52 | "monitored": true, 53 | "anyReleaseOk": true, 54 | "lastInfoSync": "2020-12-27T10:52:27.2200393Z", 55 | "added": "0001-01-01T00:00:00Z", 56 | "addOptions": { 57 | "addType": "automatic", 58 | "searchForNewAlbum": false 59 | }, 60 | "artistMetadata": { 61 | "isLoaded": false 62 | }, 63 | "albumReleases": { 64 | "isLoaded": false 65 | }, 66 | "artist": { 67 | "isLoaded": false 68 | }, 69 | "id": 0 70 | }, 71 | "lastAlbum": { 72 | "artistMetadataId": 0, 73 | "foreignAlbumId": "string", 74 | "oldForeignAlbumIds": ["string"], 75 | "title": "string", 76 | "overview": "string", 77 | "disambiguation": "string", 78 | "releaseDate": "2010-08-23T00:00:00Z", 79 | "images": [ 80 | { 81 | "url": "string", 82 | "coverType": "poster", 83 | "extension": "string" 84 | } 85 | ], 86 | "links": [ 87 | { 88 | "url": "string", 89 | "name": "string" 90 | } 91 | ], 92 | "genres": ["string"], 93 | "albumType": "string", 94 | "secondaryTypes": [ 95 | { 96 | "id": 0, 97 | "name": "string" 98 | } 99 | ], 100 | "ratings": { 101 | "votes": 0, 102 | "value": 0.0 103 | }, 104 | "cleanTitle": "string", 105 | "profileId": 0, 106 | "monitored": true, 107 | "anyReleaseOk": true, 108 | "lastInfoSync": "2021-12-27T10:52:27.2200393Z", 109 | "added": "0001-01-01T00:00:00Z", 110 | "addOptions": { 111 | "addType": "automatic", 112 | "searchForNewAlbum": false 113 | }, 114 | "artistMetadata": { 115 | "isLoaded": false 116 | }, 117 | "albumReleases": { 118 | "isLoaded": false 119 | }, 120 | "artist": { 121 | "isLoaded": false 122 | }, 123 | "id": 0 124 | }, 125 | "images": [ 126 | { 127 | "url": "string", 128 | "coverType": "poster", 129 | "extension": "string" 130 | } 131 | ], 132 | "path": "string", 133 | "qualityProfileId": 0, 134 | "metadataProfileId": 0, 135 | "monitored": true, 136 | "rootFolderPath": "string", 137 | "genres": ["string"], 138 | "cleanName": "string", 139 | "sortName": "string", 140 | "tags": [0], 141 | "added": "2021-08-21T15:35:54.398878Z", 142 | "ratings": { 143 | "votes": 0, 144 | "value": 0.0 145 | }, 146 | "statistics": { 147 | "albumCount": 0, 148 | "trackFileCount": 0, 149 | "trackCount": 0, 150 | "totalTrackCount": 0, 151 | "sizeOnDisk": 0, 152 | "percentOfTracks": 0.0 153 | }, 154 | "id": 0 155 | } 156 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/wanted-missing.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 10, 4 | "sortKey": "Books.Id", 5 | "sortDirection": "default", 6 | "totalRecords": 0, 7 | "records": [ 8 | { 9 | "title": "string", 10 | "authorTitle": "string", 11 | "seriesTitle": "string", 12 | "disambiguation": "string", 13 | "overview": "string", 14 | "authorId": 0, 15 | "foreignBookId": "string", 16 | "titleSlug": "0", 17 | "monitored": true, 18 | "anyEditionOk": true, 19 | "ratings": { 20 | "votes": 0, 21 | "value": 0.0, 22 | "popularity": 0.0 23 | }, 24 | "releaseDate": "2021-12-11T09:30:28.339Z", 25 | "pageCount": 0, 26 | "genres": ["string"], 27 | "author": { 28 | "authorMetadataId": 0, 29 | "status": "string", 30 | "ended": false, 31 | "authorName": "string", 32 | "authorNameLastFirst": "string", 33 | "foreignAuthorId": "string", 34 | "titleSlug": "0", 35 | "overview": "string", 36 | "links": [ 37 | { 38 | "url": "string", 39 | "name": "string" 40 | } 41 | ], 42 | "images": [ 43 | { 44 | "url": "string", 45 | "coverType": "poster", 46 | "extension": "string" 47 | } 48 | ], 49 | "path": "string", 50 | "qualityProfileId": 0, 51 | "metadataProfileId": 0, 52 | "monitored": true, 53 | "monitorNewItems": "string", 54 | "genres": ["string"], 55 | "cleanName": "string", 56 | "sortName": "string", 57 | "sortNameLastFirst": "string", 58 | "tags": [0], 59 | "added": "2021-12-06T22:23:55Z", 60 | "ratings": { 61 | "votes": 0, 62 | "value": 0.0, 63 | "popularity": 0.0 64 | }, 65 | "statistics": { 66 | "bookFileCount": 0, 67 | "bookCount": 0, 68 | "availableBookCount": 0, 69 | "totalBookCount": 0, 70 | "sizeOnDisk": 0, 71 | "percentOfBooks": 0.0 72 | }, 73 | "id": 0 74 | }, 75 | "images": [ 76 | { 77 | "url": "string", 78 | "coverType": "poster", 79 | "extension": "string" 80 | } 81 | ], 82 | "links": [ 83 | { 84 | "url": "string", 85 | "name": "string" 86 | } 87 | ], 88 | "statistics": { 89 | "bookFileCount": 0, 90 | "bookCount": 0, 91 | "totalBookCount": 0, 92 | "sizeOnDisk": 0, 93 | "percentOfBooks": 0.0 94 | }, 95 | "added": "2021-12-06T22:23:58Z", 96 | "editions": [ 97 | { 98 | "bookId": 0, 99 | "foreignEditionId": "string", 100 | "titleSlug": "0", 101 | "asin": "string", 102 | "title": "string", 103 | "language": "string", 104 | "overview": "string", 105 | "format": "string", 106 | "isEbook": true, 107 | "disambiguation": "string", 108 | "publisher": "string", 109 | "pageCount": 0, 110 | "releaseDate": "2017-03-15T00:00:00Z", 111 | "images": [ 112 | { 113 | "url": "string", 114 | "coverType": "poster", 115 | "extension": "string" 116 | } 117 | ], 118 | "links": [ 119 | { 120 | "url": "string", 121 | "name": "string" 122 | } 123 | ], 124 | "ratings": { 125 | "votes": 0, 126 | "value": 0.0, 127 | "popularity": 0.0 128 | }, 129 | "monitored": false, 130 | "manualAdd": false, 131 | "grabbed": false, 132 | "id": 0 133 | } 134 | ], 135 | "grabbed": false, 136 | "id": 0 137 | } 138 | ] 139 | } -------------------------------------------------------------------------------- /aiopyarr/models/base.py: -------------------------------------------------------------------------------- 1 | """PyArr base model.""" 2 | 3 | from __future__ import annotations 4 | 5 | from dataclasses import dataclass 6 | from datetime import date, datetime 7 | from enum import Enum 8 | from typing import Any 9 | 10 | import ciso8601 11 | 12 | from ..const import ATTR_DATA 13 | from .const import ( 14 | CONVERT_TO_BOOL, 15 | CONVERT_TO_DATE, 16 | CONVERT_TO_DATETIME, 17 | CONVERT_TO_ENUM, 18 | CONVERT_TO_FLOAT, 19 | CONVERT_TO_INTEGER, 20 | ProtocolType, 21 | ) 22 | 23 | 24 | def get_datetime( 25 | _input: datetime | str | None, utc: bool = False 26 | ) -> datetime | str | int | None: 27 | """Convert input to datetime object.""" 28 | if isinstance(_input, str): 29 | if _input.isnumeric(): 30 | return int(_input) 31 | if utc: 32 | return ciso8601.parse_datetime(_input) 33 | return ciso8601.parse_datetime_as_naive(_input) 34 | return _input 35 | 36 | 37 | def get_date(_input: datetime | str | None) -> date | None: 38 | """Convert input to date object.""" 39 | if (result := get_datetime(_input)) and isinstance(result, datetime): 40 | return result.date() 41 | return None 42 | 43 | 44 | def get_enum_value(val: str) -> str | Enum: 45 | """Convert input to the correct enum.""" 46 | for protocol in ProtocolType: 47 | if ( 48 | val.isnumeric() 49 | and protocol.value == int(val) 50 | or protocol.name.lower() == val 51 | ): 52 | return protocol 53 | return val 54 | 55 | 56 | def toraw(obj): 57 | """Convert object to dict.""" 58 | if isinstance(obj, dict): 59 | return {k: toraw(v) for k, v in obj.items()} 60 | if hasattr(obj, "__iter__") and not isinstance(obj, str): 61 | return [toraw(v) for v in obj] 62 | if hasattr(obj, "attributes"): 63 | return obj.attributes 64 | if isinstance(obj, datetime): 65 | return f"{obj.isoformat()}Z" 66 | return obj 67 | 68 | 69 | def generate_data( 70 | data: dict[str, Any] | list[dict[str, Any]], datatype: Any = None 71 | ) -> Any: 72 | """Generate data.""" 73 | if datatype is None: 74 | return data 75 | 76 | if isinstance(data, list): 77 | return [datatype(item) for item in data] 78 | 79 | return datatype(data) 80 | 81 | 82 | @dataclass(init=False) 83 | class BaseModel: 84 | """BaseModel.""" 85 | 86 | def __init__( 87 | self, 88 | data: dict[str, Any] | list[dict[str, Any]], 89 | datatype: Any = None, 90 | ) -> None: 91 | """Init.""" 92 | self.basedata = None 93 | if isinstance(data, dict): 94 | for key, value in data.items(): 95 | if key == ATTR_DATA: 96 | value = generate_data(value, datatype) 97 | elif key in CONVERT_TO_DATETIME: 98 | if key == "airDateUtc": 99 | value = get_datetime(value, utc=True) 100 | else: 101 | value = get_datetime(value) 102 | elif key in CONVERT_TO_DATE: 103 | value = get_date(value) 104 | elif key in CONVERT_TO_ENUM: 105 | value = get_enum_value(value) 106 | elif key in CONVERT_TO_FLOAT and value is not None: 107 | value = float(value) 108 | elif key in CONVERT_TO_INTEGER and value is not None: 109 | try: 110 | value = int(value) 111 | except ValueError: 112 | pass 113 | elif key in CONVERT_TO_BOOL: 114 | value = False if value == "False" else bool(value) 115 | self.__setattr__(key, value) 116 | 117 | self.__post_init__() 118 | 119 | def __post_init__(self): 120 | """Post init.""" 121 | 122 | @property 123 | def attributes(self): 124 | """Return attributes of the object.""" 125 | return { 126 | k: ( 127 | v 128 | if isinstance(v, bool) 129 | else str(v) if k in CONVERT_TO_INTEGER else toraw(v) 130 | ) 131 | for k, v in self.__dict__.items() 132 | if k != ATTR_DATA 133 | } 134 | -------------------------------------------------------------------------------- /tests/fixtures/radarr/parse.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "string", 3 | "parsedMovieInfo": { 4 | "movieTitles": ["string"], 5 | "originalTitle": "string", 6 | "releaseTitle": "string", 7 | "simpleReleaseTitle": "string", 8 | "quality": { 9 | "quality": { 10 | "id": 0, 11 | "name": "string", 12 | "source": "string", 13 | "resolution": 0, 14 | "modifier": "string" 15 | }, 16 | "revision": { 17 | "version": 0, 18 | "real": 0, 19 | "isRepack": false 20 | } 21 | }, 22 | "languages": [ 23 | { 24 | "id": 0, 25 | "name": "string" 26 | } 27 | ], 28 | "releaseHash": "", 29 | "edition": "", 30 | "year": 0, 31 | "imdbId": "", 32 | "tmdbId": 0, 33 | "extraInfo": {}, 34 | "movieTitle": "string", 35 | "primaryMovieTitle": "string" 36 | }, 37 | "movie": { 38 | "title": "string", 39 | "originalTitle": "string", 40 | "alternateTitles": [ 41 | { 42 | "sourceType": "string", 43 | "movieId": 0, 44 | "title": "string", 45 | "sourceId": 0, 46 | "votes": 0, 47 | "voteCount": 0, 48 | "language": { 49 | "id": 0, 50 | "name": "string" 51 | }, 52 | "id": 0 53 | } 54 | ], 55 | "secondaryYearSourceId": 0, 56 | "sortTitle": "string", 57 | "sizeOnDisk": 0, 58 | "status": "string", 59 | "overview": "string", 60 | "inCinemas": "2000-04-25T00:00:00Z", 61 | "physicalRelease": "2000-07-08T00:00:00Z", 62 | "digitalRelease": "2000-02-01T00:00:00Z", 63 | "images": [ 64 | { 65 | "coverType": "poster", 66 | "url": "string" 67 | } 68 | ], 69 | "website": "string", 70 | "year": 0, 71 | "hasFile": true, 72 | "youTubeTrailerId": "string", 73 | "studio": "string", 74 | "path": "string", 75 | "qualityProfileId": 0, 76 | "monitored": false, 77 | "minimumAvailability": "string", 78 | "isAvailable": true, 79 | "folderName": "string", 80 | "runtime": 0, 81 | "cleanTitle": "string", 82 | "imdbId": "string", 83 | "tmdbId": 0, 84 | "titleSlug": "0", 85 | "certification": "string", 86 | "genres": ["string"], 87 | "tags": [0], 88 | "added": "2020-11-28T06:34:25Z", 89 | "ratings": { 90 | "imdb": { 91 | "votes": 0, 92 | "value": 0.0, 93 | "type": "string" 94 | }, 95 | "tmdb": { 96 | "votes": 0, 97 | "value": 0.0, 98 | "type": "string" 99 | }, 100 | "metacritic": { 101 | "votes": 0, 102 | "value": 0, 103 | "type": "string" 104 | }, 105 | "rottenTomatoes": { 106 | "votes": 0, 107 | "value": 0, 108 | "type": "string" 109 | } 110 | }, 111 | "movieFile": { 112 | "movieId": 0, 113 | "relativePath": "string", 114 | "path": "string", 115 | "size": 0, 116 | "dateAdded": "2020-02-23T12:00:46Z", 117 | "indexerFlags": 0, 118 | "quality": { 119 | "quality": { 120 | "id": 0, 121 | "name": "string", 122 | "source": "string", 123 | "resolution": 0, 124 | "modifier": "string" 125 | }, 126 | "revision": { 127 | "version": 0, 128 | "real": 0, 129 | "isRepack": false 130 | } 131 | }, 132 | "mediaInfo": { 133 | "audioBitrate": 0, 134 | "audioChannels": 0.0, 135 | "audioCodec": "string", 136 | "audioLanguages": "string", 137 | "audioStreamCount": 0, 138 | "videoBitDepth": 0, 139 | "videoBitrate": 0, 140 | "videoCodec": "string", 141 | "videoDynamicRangeType": "string", 142 | "videoFps": 0.0, 143 | "resolution": "string", 144 | "runTime": "00:00:00", 145 | "scanType": "string", 146 | "subtitles": "string" 147 | }, 148 | "qualityCutoffNotMet": false, 149 | "languages": [ 150 | { 151 | "id": 0, 152 | "name": "string" 153 | } 154 | ], 155 | "edition": "string", 156 | "id": 0 157 | }, 158 | "collection": { 159 | "name": "string", 160 | "tmdbId": 0, 161 | "images": [ 162 | { 163 | "coverType": "poster", 164 | "remoteUrl": "string", 165 | "url": "string" 166 | } 167 | ] 168 | }, 169 | "id": 0 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/queue-details.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "authorId": 0, 4 | "bookId": 0, 5 | "book": { 6 | "title": "string", 7 | "authorTitle": "string", 8 | "seriesTitle": "string", 9 | "disambiguation": "string", 10 | "overview": "string", 11 | "authorId": 0, 12 | "foreignBookId": "0", 13 | "titleSlug": "0", 14 | "monitored": true, 15 | "anyEditionOk": true, 16 | "ratings": { 17 | "votes": 0, 18 | "value": 0.0, 19 | "popularity": 0.0 20 | }, 21 | "releaseDate": "2021-11-22T00:00:00Z", 22 | "pageCount": 0, 23 | "genres": ["string"], 24 | "author": { 25 | "authorMetadataId": 0, 26 | "status": "string", 27 | "ended": false, 28 | "authorName": "string", 29 | "authorNameLastFirst": "string", 30 | "foreignAuthorId": "0", 31 | "titleSlug": "0", 32 | "overview": "string", 33 | "links": [ 34 | { 35 | "url": "string", 36 | "name": "string" 37 | } 38 | ], 39 | "images": [ 40 | { 41 | "url": "string", 42 | "coverType": "poster", 43 | "extension": "string" 44 | } 45 | ], 46 | "path": "string", 47 | "qualityProfileId": 0, 48 | "metadataProfileId": 0, 49 | "monitored": true, 50 | "monitorNewItems": "string", 51 | "genres": ["string"], 52 | "cleanName": "string", 53 | "sortName": "string", 54 | "sortNameLastFirst": "string", 55 | "tags": [0], 56 | "added": "2021-12-06T23:38:03Z", 57 | "ratings": { 58 | "votes": 0, 59 | "value": 0.0, 60 | "popularity": 0.0 61 | }, 62 | "statistics": { 63 | "bookFileCount": 0, 64 | "bookCount": 0, 65 | "availableBookCount": 0, 66 | "totalBookCount": 0, 67 | "sizeOnDisk": 0, 68 | "percentOfBooks": 0.0 69 | }, 70 | "id": 0 71 | }, 72 | "images": [ 73 | { 74 | "url": "string", 75 | "coverType": "poster", 76 | "extension": "string" 77 | } 78 | ], 79 | "links": [ 80 | { 81 | "url": "string", 82 | "name": "string" 83 | } 84 | ], 85 | "added": "2021-12-06T23:53:58Z", 86 | "editions": [ 87 | { 88 | "bookId": 0, 89 | "foreignEditionId": "0", 90 | "titleSlug": "0", 91 | "isbn13": "0", 92 | "title": "string", 93 | "language": "string", 94 | "overview": "string", 95 | "format": "string", 96 | "isEbook": false, 97 | "disambiguation": "string", 98 | "publisher": "string", 99 | "pageCount": 0, 100 | "releaseDate": "2021-11-25T00:00:00Z", 101 | "images": [ 102 | { 103 | "url": "string", 104 | "coverType": "poster", 105 | "extension": "string" 106 | } 107 | ], 108 | "links": [ 109 | { 110 | "url": "string", 111 | "name": "string" 112 | } 113 | ], 114 | "ratings": { 115 | "votes": 0, 116 | "value": 0.0, 117 | "popularity": 0.0 118 | }, 119 | "monitored": false, 120 | "manualAdd": false, 121 | "grabbed": false, 122 | "id": 0 123 | } 124 | ], 125 | "grabbed": false, 126 | "id": 0 127 | }, 128 | "quality": { 129 | "quality": { 130 | "id": 0, 131 | "name": "string" 132 | }, 133 | "revision": { 134 | "version": 0, 135 | "real": 0, 136 | "isRepack": false 137 | } 138 | }, 139 | "size": 0, 140 | "title": "string", 141 | "sizeleft": 0, 142 | "timeleft": "00:00:00", 143 | "estimatedCompletionTime": "2020-02-07T11:27:27Z", 144 | "status": "string", 145 | "trackedDownloadStatus": "string", 146 | "trackedDownloadState": "string", 147 | "statusMessages": [ 148 | { 149 | "title": "string", 150 | "messages": ["string"] 151 | } 152 | ], 153 | "downloadId": "string", 154 | "protocol": "unknown", 155 | "downloadClient": "string", 156 | "indexer": "string", 157 | "outputPath": "string", 158 | "downloadForced": false, 159 | "id": 0 160 | } 161 | ] 162 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/history.json: -------------------------------------------------------------------------------- 1 | { 2 | "page": 1, 3 | "pageSize": 1, 4 | "sortKey": "date", 5 | "sortDirection": "descending", 6 | "totalRecords": 1, 7 | "records": [ 8 | { 9 | "albumId": 0, 10 | "artistId": 0, 11 | "trackId": 0, 12 | "sourceTitle": "string", 13 | "quality": { 14 | "quality": { 15 | "id": 0, 16 | "name": "string" 17 | }, 18 | "revision": { 19 | "version": 0, 20 | "real": 0, 21 | "isRepack": false 22 | } 23 | }, 24 | "qualityCutoffNotMet": false, 25 | "date": "2020-02-16T14:03:43.6224911Z", 26 | "downloadId": "string", 27 | "eventType": "grabbed", 28 | "data": { 29 | "indexer": "string", 30 | "nzbInfoUrl": "string", 31 | "releaseGroup": "string", 32 | "age": "0", 33 | "ageHours": "0.0", 34 | "ageMinutes": "0.0", 35 | "publishedDate": "2020-02-16T06:11:00Z", 36 | "downloadClient": "string", 37 | "size": "0", 38 | "downloadUrl": "string", 39 | "guid": "string", 40 | "protocol": "0", 41 | "downloadForced": "False", 42 | "torrentInfoHash": "string" 43 | }, 44 | "album": { 45 | "title": "string", 46 | "disambiguation": "string", 47 | "overview": "string", 48 | "artistId": 0, 49 | "foreignAlbumId": "string", 50 | "monitored": true, 51 | "anyReleaseOk": true, 52 | "profileId": 0, 53 | "duration": 0, 54 | "albumType": "string", 55 | "secondaryTypes": [ 56 | { 57 | "id": 0, 58 | "name": "string" 59 | } 60 | ], 61 | "mediumCount": 0, 62 | "ratings": { 63 | "votes": 0, 64 | "value": 0.0 65 | }, 66 | "releaseDate": "2015-11-13T00:00:00Z", 67 | "releases": [ 68 | { 69 | "id": 0, 70 | "albumId": 0, 71 | "foreignReleaseId": "string", 72 | "title": "string", 73 | "status": "string", 74 | "duration": 0, 75 | "trackCount": 0, 76 | "media": [ 77 | { 78 | "mediumNumber": 0, 79 | "mediumName": "string", 80 | "mediumFormat": "string" 81 | } 82 | ], 83 | "mediumCount": 0, 84 | "disambiguation": "string", 85 | "country": ["string"], 86 | "label": ["string"], 87 | "format": "string", 88 | "monitored": false 89 | } 90 | ], 91 | "genres": ["string"], 92 | "media": [ 93 | { 94 | "mediumNumber": 0, 95 | "mediumName": "string", 96 | "mediumFormat": "string" 97 | } 98 | ], 99 | "artist": { 100 | "status": "ended", 101 | "ended": false, 102 | "artistName": "string", 103 | "foreignArtistId": "string", 104 | "tadbId": 0, 105 | "discogsId": 0, 106 | "overview": "string", 107 | "artistType": "string", 108 | "disambiguation": "string", 109 | "links": [ 110 | { 111 | "url": "string", 112 | "name": "string" 113 | } 114 | ], 115 | "images": [ 116 | { 117 | "url": "string", 118 | "coverType": "poster", 119 | "extension": "string" 120 | } 121 | ], 122 | "path": "string", 123 | "qualityProfileId": 0, 124 | "metadataProfileId": 0, 125 | "monitored": true, 126 | "genres": ["string"], 127 | "cleanName": "string", 128 | "sortName": "string", 129 | "tags": [0], 130 | "added": "2021-08-21T15:56:31.9225975Z", 131 | "ratings": { 132 | "votes": 0, 133 | "value": 0.0 134 | }, 135 | "statistics": { 136 | "albumCount": 0, 137 | "trackFileCount": 0, 138 | "trackCount": 0, 139 | "totalTrackCount": 0, 140 | "sizeOnDisk": 0, 141 | "percentOfTracks": 0.0 142 | }, 143 | "id": 0 144 | }, 145 | "images": [ 146 | { 147 | "url": "string", 148 | "coverType": "poster", 149 | "extension": "string" 150 | } 151 | ], 152 | "links": [ 153 | { 154 | "url": "string", 155 | "name": "string" 156 | } 157 | ], 158 | "id": 0 159 | }, 160 | "artist": { 161 | "status": "ended", 162 | "ended": false, 163 | "artistName": "string", 164 | "foreignArtistId": "string", 165 | "tadbId": 0, 166 | "discogsId": 0, 167 | "overview": "string", 168 | "artistType": "string", 169 | "disambiguation": "string", 170 | "links": [ 171 | { 172 | "url": "string", 173 | "name": "string" 174 | } 175 | ], 176 | "images": [ 177 | { 178 | "url": "string", 179 | "coverType": "poster", 180 | "extension": "string" 181 | } 182 | ], 183 | "path": "string", 184 | "qualityProfileId": 0, 185 | "metadataProfileId": 0, 186 | "monitored": true, 187 | "genres": ["string"], 188 | "cleanName": "string", 189 | "sortName": "string", 190 | "tags": [0], 191 | "added": "2021-08-21T15:56:31.9225975Z", 192 | "ratings": { 193 | "votes": 0, 194 | "value": 0.0 195 | }, 196 | "statistics": { 197 | "albumCount": 0, 198 | "trackFileCount": 0, 199 | "trackCount": 0, 200 | "totalTrackCount": 0, 201 | "sizeOnDisk": 0, 202 | "percentOfTracks": 0.0 203 | }, 204 | "id": 0 205 | }, 206 | "id": 0 207 | } 208 | ] 209 | } 210 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/queue-details.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "artistId": 0, 4 | "albumId": 0, 5 | "artist": { 6 | "status": "string", 7 | "ended": false, 8 | "artistName": "string", 9 | "foreignArtistId": "string", 10 | "tadbId": 0, 11 | "discogsId": 0, 12 | "overview": "string", 13 | "artistType": "string", 14 | "disambiguation": "string", 15 | "links": [ 16 | { 17 | "url": "string", 18 | "name": "string" 19 | } 20 | ], 21 | "images": [ 22 | { 23 | "url": "string", 24 | "coverType": "poster", 25 | "extension": "string" 26 | } 27 | ], 28 | "path": "string", 29 | "qualityProfileId": 0, 30 | "metadataProfileId": 0, 31 | "monitored": true, 32 | "genres": ["string"], 33 | "cleanName": "string", 34 | "sortName": "string", 35 | "tags": [0], 36 | "added": "2021-08-21T15:56:31.9225975Z", 37 | "ratings": { 38 | "votes": 0, 39 | "value": 0.0 40 | }, 41 | "statistics": { 42 | "albumCount": 0, 43 | "trackFileCount": 0, 44 | "trackCount": 0, 45 | "totalTrackCount": 0, 46 | "sizeOnDisk": 0, 47 | "percentOfTracks": 0.0 48 | }, 49 | "id": 0 50 | }, 51 | "album": { 52 | "title": "string", 53 | "disambiguation": "string", 54 | "overview": "string", 55 | "artistId": 0, 56 | "foreignAlbumId": "string", 57 | "monitored": true, 58 | "anyReleaseOk": true, 59 | "profileId": 0, 60 | "duration": 0, 61 | "albumType": "string", 62 | "secondaryTypes": [ 63 | { 64 | "id": 0, 65 | "name": "string" 66 | } 67 | ], 68 | "mediumCount": 0, 69 | "ratings": { 70 | "votes": 0, 71 | "value": 0.0 72 | }, 73 | "releaseDate": "2010-11-12T00:00:00Z", 74 | "releases": [ 75 | { 76 | "id": 0, 77 | "albumId": 0, 78 | "foreignReleaseId": "string", 79 | "title": "string", 80 | "status": "string", 81 | "duration": 0, 82 | "trackCount": 0, 83 | "media": [ 84 | { 85 | "mediumNumber": 0, 86 | "mediumName": "string", 87 | "mediumFormat": "string" 88 | } 89 | ], 90 | "mediumCount": 0, 91 | "disambiguation": "string", 92 | "country": ["string"], 93 | "label": ["string"], 94 | "format": "string", 95 | "monitored": false 96 | } 97 | ], 98 | "genres": ["string"], 99 | "media": [ 100 | { 101 | "mediumNumber": 0, 102 | "mediumName": "string", 103 | "mediumFormat": "string" 104 | } 105 | ], 106 | "artist": { 107 | "status": "string", 108 | "ended": false, 109 | "artistName": "string", 110 | "foreignArtistId": "string", 111 | "tadbId": 0, 112 | "discogsId": 0, 113 | "overview": "string", 114 | "artistType": "string", 115 | "disambiguation": "string", 116 | "links": [ 117 | { 118 | "url": "string", 119 | "name": "string" 120 | } 121 | ], 122 | "images": [ 123 | { 124 | "url": "string", 125 | "coverType": "poster", 126 | "extension": "string" 127 | } 128 | ], 129 | "path": "string", 130 | "qualityProfileId": 0, 131 | "metadataProfileId": 0, 132 | "monitored": true, 133 | "genres": ["string"], 134 | "cleanName": "string", 135 | "sortName": "string", 136 | "tags": [0], 137 | "added": "2021-08-21T15:56:31.9225975Z", 138 | "ratings": { 139 | "votes": 0, 140 | "value": 0.0 141 | }, 142 | "statistics": { 143 | "albumCount": 0, 144 | "trackFileCount": 0, 145 | "trackCount": 0, 146 | "totalTrackCount": 0, 147 | "sizeOnDisk": 0, 148 | "percentOfTracks": 0.0 149 | }, 150 | "id": 0 151 | }, 152 | "images": [ 153 | { 154 | "url": "string", 155 | "coverType": "poster", 156 | "extension": "string" 157 | } 158 | ], 159 | "links": [ 160 | { 161 | "url": "string", 162 | "name": "string" 163 | } 164 | ], 165 | "id": 0 166 | }, 167 | "quality": { 168 | "quality": { 169 | "id": 0, 170 | "name": "string" 171 | }, 172 | "revision": { 173 | "version": 0, 174 | "real": 0, 175 | "isRepack": false 176 | } 177 | }, 178 | "size": 0.0, 179 | "title": "string", 180 | "sizeleft": 0.0, 181 | "timeleft": "00:00:00", 182 | "estimatedCompletionTime": "2020-02-16T23:49:45.1437278Z", 183 | "status": "string", 184 | "trackedDownloadStatus": "string", 185 | "trackedDownloadState": "string", 186 | "statusMessages": [ 187 | { 188 | "title": "string", 189 | "messages": ["string"] 190 | } 191 | ], 192 | "downloadId": "string", 193 | "protocol": "unknown", 194 | "downloadClient": "string", 195 | "indexer": "string", 196 | "outputPath": "string", 197 | "downloadForced": false, 198 | "id": 0 199 | } 200 | ] 201 | -------------------------------------------------------------------------------- /tests/fixtures/lidarr/manualimport.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "path": "string", 4 | "name": "string", 5 | "size": 0, 6 | "artist": { 7 | "status": "continuing", 8 | "ended": false, 9 | "artistName": "string", 10 | "foreignArtistId": "string", 11 | "tadbId": 0, 12 | "discogsId": 0, 13 | "overview": "string", 14 | "artistType": "Person", 15 | "disambiguation": "string", 16 | "links": [ 17 | { 18 | "url": "string", 19 | "name": "discogs" 20 | } 21 | ], 22 | "images": [ 23 | { 24 | "url": "string", 25 | "coverType": "banner", 26 | "extension": "string" 27 | } 28 | ], 29 | "path": "string", 30 | "qualityProfileId": 0, 31 | "metadataProfileId": 0, 32 | "monitored": true, 33 | "genres": ["string"], 34 | "cleanName": "string", 35 | "sortName": "string", 36 | "tags": [0], 37 | "added": "2020-02-20T14:06:21.5413564Z", 38 | "ratings": { 39 | "votes": 0, 40 | "value": 0.0 41 | }, 42 | "statistics": { 43 | "albumCount": 0, 44 | "trackFileCount": 0, 45 | "trackCount": 0, 46 | "totalTrackCount": 0, 47 | "sizeOnDisk": 0, 48 | "percentOfTracks": 0.0 49 | }, 50 | "id": 0 51 | }, 52 | "album": { 53 | "title": "string", 54 | "disambiguation": "string", 55 | "overview": "string", 56 | "artistId": 0, 57 | "foreignAlbumId": "string", 58 | "monitored": true, 59 | "anyReleaseOk": true, 60 | "profileId": 0, 61 | "duration": 0, 62 | "albumType": "string", 63 | "secondaryTypes": [ 64 | { 65 | "id": 0, 66 | "name": "string" 67 | } 68 | ], 69 | "mediumCount": 0, 70 | "ratings": { 71 | "votes": 0, 72 | "value": 0.0 73 | }, 74 | "releaseDate": "2019-07-25T00:00:00Z", 75 | "releases": [ 76 | { 77 | "id": 0, 78 | "albumId": 0, 79 | "foreignReleaseId": "string", 80 | "title": "string", 81 | "status": "Official", 82 | "duration": 0, 83 | "trackCount": 0, 84 | "media": [ 85 | { 86 | "mediumNumber": 0, 87 | "mediumName": "string", 88 | "mediumFormat": "string" 89 | } 90 | ], 91 | "mediumCount": 0, 92 | "disambiguation": "string", 93 | "country": ["string"], 94 | "label": ["string"], 95 | "format": "string", 96 | "monitored": false 97 | } 98 | ], 99 | "genres": ["string"], 100 | "media": [ 101 | { 102 | "mediumNumber": 0, 103 | "mediumName": "string", 104 | "mediumFormat": "string" 105 | } 106 | ], 107 | "artist": { 108 | "status": "continuing", 109 | "ended": false, 110 | "artistName": "string", 111 | "foreignArtistId": "string", 112 | "tadbId": 0, 113 | "discogsId": 0, 114 | "overview": "string", 115 | "artistType": "Person", 116 | "disambiguation": "string", 117 | "links": [ 118 | { 119 | "url": "string", 120 | "name": "discogs" 121 | } 122 | ], 123 | "images": [ 124 | { 125 | "url": "string", 126 | "coverType": "banner", 127 | "extension": "string" 128 | } 129 | ], 130 | "path": "string", 131 | "qualityProfileId": 0, 132 | "metadataProfileId": 0, 133 | "monitored": true, 134 | "genres": ["string"], 135 | "cleanName": "string", 136 | "sortName": "string", 137 | "tags": [0], 138 | "added": "2020-02-20T14:06:21.5413564Z", 139 | "ratings": { 140 | "votes": 0, 141 | "value": 0.0 142 | }, 143 | "statistics": { 144 | "albumCount": 0, 145 | "trackFileCount": 0, 146 | "trackCount": 0, 147 | "totalTrackCount": 0, 148 | "sizeOnDisk": 0, 149 | "percentOfTracks": 0.0 150 | }, 151 | "id": 0 152 | }, 153 | "images": [ 154 | { 155 | "url": "string", 156 | "coverType": "cover", 157 | "extension": "string" 158 | } 159 | ], 160 | "links": [ 161 | { 162 | "url": "string", 163 | "name": "string" 164 | } 165 | ], 166 | "id": 0 167 | }, 168 | "albumReleaseId": 0, 169 | "tracks": [ 170 | { 171 | "artistId": 0, 172 | "trackFileId": 0, 173 | "albumId": 0, 174 | "explicit": false, 175 | "absoluteTrackNumber": 0, 176 | "trackNumber": "0", 177 | "title": "string", 178 | "duration": 0, 179 | "mediumNumber": 0, 180 | "hasFile": false, 181 | "ratings": { 182 | "votes": 0, 183 | "value": 0.0 184 | }, 185 | "id": 0 186 | } 187 | ], 188 | "quality": { 189 | "quality": { 190 | "id": 0, 191 | "name": "string" 192 | }, 193 | "revision": { 194 | "version": 0, 195 | "real": 0, 196 | "isRepack": false 197 | } 198 | }, 199 | "qualityWeight": 0, 200 | "downloadId": "string", 201 | "rejections": [ 202 | { 203 | "reason": "string", 204 | "type": "permanent" 205 | } 206 | ], 207 | "audioTags": { 208 | "title": "string", 209 | "cleanTitle": "string", 210 | "artistTitle": "string", 211 | "albumTitle": "string", 212 | "artistTitleInfo": { 213 | "title": "string", 214 | "year": 0 215 | }, 216 | "discNumber": 0, 217 | "discCount": 0, 218 | "year": 0, 219 | "duration": "00:00:00.4800000", 220 | "quality": { 221 | "quality": { 222 | "id": 0, 223 | "name": "FLAC" 224 | }, 225 | "revision": { 226 | "version": 0, 227 | "real": 0, 228 | "isRepack": false 229 | } 230 | }, 231 | "mediaInfo": { 232 | "audioFormat": "string", 233 | "audioBitrate": 0, 234 | "audioChannels": 0, 235 | "audioBits": 0, 236 | "audioSampleRate": 0 237 | }, 238 | "trackNumbers": [0] 239 | }, 240 | "additionalFile": false, 241 | "replaceExistingFiles": true, 242 | "disableReleaseSwitching": false, 243 | "id": 0 244 | } 245 | ] 246 | -------------------------------------------------------------------------------- /tests/fixtures/readarr/manualimport.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "path": "string", 4 | "name": "string", 5 | "size": 0, 6 | "author": { 7 | "authorMetadataId": 0, 8 | "status": "continuing", 9 | "ended": false, 10 | "authorName": "string", 11 | "authorNameLastFirst": "string", 12 | "foreignAuthorId": "0", 13 | "titleSlug": "0", 14 | "overview": "string", 15 | "links": [ 16 | { 17 | "url": "string", 18 | "name": "string" 19 | } 20 | ], 21 | "images": [ 22 | { 23 | "url": "string", 24 | "coverType": "poster", 25 | "extension": "string" 26 | } 27 | ], 28 | "path": "string", 29 | "qualityProfileId": 0, 30 | "metadataProfileId": 0, 31 | "monitored": true, 32 | "monitorNewItems": "all", 33 | "genres": ["string"], 34 | "cleanName": "string", 35 | "sortName": "string", 36 | "sortNameLastFirst": "string", 37 | "tags": [0], 38 | "added": "2021-12-06T23:38:03Z", 39 | "ratings": { 40 | "votes": 0, 41 | "value": 0.0, 42 | "popularity": 0.0 43 | }, 44 | "statistics": { 45 | "bookFileCount": 0, 46 | "bookCount": 0, 47 | "availableBookCount": 0, 48 | "totalBookCount": 0, 49 | "sizeOnDisk": 0, 50 | "percentOfBooks": 0 51 | }, 52 | "id": 0 53 | }, 54 | "book": { 55 | "title": "string", 56 | "authorTitle": "string", 57 | "seriesTitle": "string", 58 | "disambiguation": "string", 59 | "overview": "string", 60 | "authorId": 0, 61 | "foreignBookId": "string", 62 | "titleSlug": "0", 63 | "monitored": true, 64 | "anyEditionOk": true, 65 | "ratings": { 66 | "votes": 0, 67 | "value": 0.0, 68 | "popularity": 0.0 69 | }, 70 | "releaseDate": "2020-09-21T00:00:00Z", 71 | "pageCount": 0, 72 | "genres": ["string"], 73 | "author": { 74 | "authorMetadataId": 0, 75 | "status": "continuing", 76 | "ended": false, 77 | "authorName": "string", 78 | "authorNameLastFirst": "string", 79 | "foreignAuthorId": "0", 80 | "titleSlug": "0", 81 | "overview": "string", 82 | "links": [ 83 | { 84 | "url": "string", 85 | "name": "Goodreads" 86 | } 87 | ], 88 | "images": [ 89 | { 90 | "url": "string", 91 | "coverType": "poster", 92 | "extension": "string" 93 | } 94 | ], 95 | "path": "string", 96 | "qualityProfileId": 0, 97 | "metadataProfileId": 0, 98 | "monitored": true, 99 | "monitorNewItems": "all", 100 | "genres": ["string"], 101 | "cleanName": "string", 102 | "sortName": "string", 103 | "sortNameLastFirst": "string", 104 | "tags": [0], 105 | "added": "2021-12-06T23:38:03Z", 106 | "ratings": { 107 | "votes": 0, 108 | "value": 0.0, 109 | "popularity": 0.0 110 | }, 111 | "statistics": { 112 | "bookFileCount": 0, 113 | "bookCount": 0, 114 | "availableBookCount": 0, 115 | "totalBookCount": 0, 116 | "sizeOnDisk": 0, 117 | "percentOfBooks": 0 118 | }, 119 | "id": 0 120 | }, 121 | "images": [ 122 | { 123 | "url": "string", 124 | "coverType": "cover", 125 | "extension": "string" 126 | } 127 | ], 128 | "links": [ 129 | { 130 | "url": "string", 131 | "name": "string" 132 | } 133 | ], 134 | "added": "2021-12-06T23:53:58Z", 135 | "editions": [ 136 | { 137 | "bookId": 0, 138 | "foreignEditionId": "0", 139 | "titleSlug": "0", 140 | "isbn13": "0", 141 | "asin": "string", 142 | "title": "string", 143 | "overview": "string", 144 | "format": "string", 145 | "isEbook": false, 146 | "disambiguation": "string", 147 | "publisher": "string", 148 | "pageCount": 0, 149 | "releaseDate": "2020-08-27T00:00:00Z", 150 | "images": [ 151 | { 152 | "url": "string", 153 | "coverType": "cover", 154 | "extension": "string" 155 | } 156 | ], 157 | "links": [ 158 | { 159 | "url": "string", 160 | "name": "string" 161 | } 162 | ], 163 | "ratings": { 164 | "votes": 0, 165 | "value": 0.0, 166 | "popularity": 0.0 167 | }, 168 | "monitored": false, 169 | "manualAdd": false, 170 | "grabbed": false, 171 | "id": 0 172 | } 173 | ], 174 | "grabbed": false, 175 | "id": 0 176 | }, 177 | "foreignEditionId": "0", 178 | "quality": { 179 | "quality": { 180 | "id": 0, 181 | "name": "string" 182 | }, 183 | "revision": { 184 | "version": 0, 185 | "real": 0, 186 | "isRepack": false 187 | } 188 | }, 189 | "qualityWeight": 0, 190 | "downloadId": "string", 191 | "rejections": [ 192 | { 193 | "reason": "string", 194 | "type": "permanent" 195 | } 196 | ], 197 | "audioTags": { 198 | "authors": ["string"], 199 | "authorTitle": "string", 200 | "bookTitle": "string", 201 | "isbn": "0", 202 | "discNumber": 0, 203 | "discCount": 0, 204 | "year": 0, 205 | "publisher": "string", 206 | "disambiguation": "string", 207 | "duration": "00:00:00", 208 | "quality": { 209 | "quality": { 210 | "id": 0, 211 | "name": "EPUB" 212 | }, 213 | "revision": { 214 | "version": 0, 215 | "real": 0, 216 | "isRepack": false 217 | } 218 | }, 219 | "trackNumbers": [0], 220 | "language": "en" 221 | }, 222 | "additionalFile": false, 223 | "replaceExistingFiles": true, 224 | "disableReleaseSwitching": false, 225 | "id": 0 226 | } 227 | ] 228 | --------------------------------------------------------------------------------