@mediaryorg/contracts 1.0.10 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/proto/paths.d.ts +8 -0
- package/dist/proto/paths.js +8 -0
- package/generated/auth/v1/auth.ts +21 -0
- package/generated/collection/v1/collection.ts +235 -0
- package/generated/favorite/v1/favorite.ts +146 -0
- package/generated/image/v1/image.ts +140 -0
- package/generated/library/v1/library.ts +216 -0
- package/generated/media/v1/media.ts +294 -0
- package/generated/media_request/v1/media_request.ts +223 -0
- package/generated/recommendation/v1/recommendation.ts +126 -0
- package/generated/search/v1/search.ts +103 -0
- package/package.json +3 -3
- package/proto/auth/v1/auth.proto +9 -0
- package/proto/collection/v1/collection.proto +120 -0
- package/proto/favorite/v1/favorite.proto +79 -0
- package/proto/image/v1/image.proto +73 -0
- package/proto/library/v1/library.proto +144 -0
- package/proto/media/v1/media.proto +197 -0
- package/proto/media_request/v1/media_request.proto +142 -0
- package/proto/recommendation/v1/recommendation.proto +60 -0
- package/proto/search/v1/search.proto +62 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
syntax = "proto3";
|
|
2
|
+
|
|
3
|
+
package image.v1;
|
|
4
|
+
|
|
5
|
+
service ImageService {
|
|
6
|
+
// Returns the binary image (proxied + optionally resized).
|
|
7
|
+
// Useful when the gateway streams it back to the client. Size hints are
|
|
8
|
+
// advisory; the implementation may ignore them.
|
|
9
|
+
rpc ProxyImage(ProxyImageRequest) returns (ProxyImageResponse);
|
|
10
|
+
|
|
11
|
+
// Resolves a TMDB poster path (e.g. "/abc.jpg") to an absolute URL.
|
|
12
|
+
rpc GetTmdbPosterUrl(GetTmdbPosterUrlRequest) returns (GetTmdbPosterUrlResponse);
|
|
13
|
+
|
|
14
|
+
// Uploads a single image to object storage (R2) and returns its public URL.
|
|
15
|
+
rpc UploadImage(UploadImageRequest) returns (UploadImageResponse);
|
|
16
|
+
|
|
17
|
+
// Removes an image from object storage by its public URL or key.
|
|
18
|
+
rpc DeleteImage(DeleteImageRequest) returns (DeleteImageResponse);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// TMDB poster size variants accepted by the legacy controller.
|
|
22
|
+
enum TmdbPosterSize {
|
|
23
|
+
TMDB_POSTER_SIZE_UNSPECIFIED = 0;
|
|
24
|
+
TMDB_POSTER_SIZE_W92 = 1;
|
|
25
|
+
TMDB_POSTER_SIZE_W154 = 2;
|
|
26
|
+
TMDB_POSTER_SIZE_W185 = 3;
|
|
27
|
+
TMDB_POSTER_SIZE_W342 = 4;
|
|
28
|
+
TMDB_POSTER_SIZE_W500 = 5;
|
|
29
|
+
TMDB_POSTER_SIZE_W780 = 6;
|
|
30
|
+
TMDB_POSTER_SIZE_ORIGINAL = 7;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
message ProxyImageRequest {
|
|
34
|
+
string url = 1;
|
|
35
|
+
optional int32 width = 2;
|
|
36
|
+
optional int32 height = 3;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
message ProxyImageResponse {
|
|
40
|
+
bytes data = 1;
|
|
41
|
+
string content_type = 2;
|
|
42
|
+
int64 content_length = 3;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
message GetTmdbPosterUrlRequest {
|
|
46
|
+
string poster_path = 1;
|
|
47
|
+
TmdbPosterSize size = 2;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
message GetTmdbPosterUrlResponse {
|
|
51
|
+
string url = 1;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
message UploadImageRequest {
|
|
55
|
+
// Raw file bytes. Allowed mime types: image/jpeg, image/png, image/webp.
|
|
56
|
+
// Max size: 4 MiB.
|
|
57
|
+
bytes data = 1;
|
|
58
|
+
string original_name = 2;
|
|
59
|
+
string mime_type = 3;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
message UploadImageResponse {
|
|
63
|
+
string url = 1;
|
|
64
|
+
string original_name = 2;
|
|
65
|
+
int64 size = 3;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
message DeleteImageRequest {
|
|
69
|
+
// Either a full URL (with public-host prefix) or just the storage key.
|
|
70
|
+
string url_or_key = 1;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
message DeleteImageResponse {}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
syntax = "proto3";
|
|
2
|
+
|
|
3
|
+
package library.v1;
|
|
4
|
+
|
|
5
|
+
import "media/v1/media.proto";
|
|
6
|
+
|
|
7
|
+
service LibraryService {
|
|
8
|
+
rpc AddFromSearch(AddFromSearchRequest) returns (AddFromSearchResponse);
|
|
9
|
+
rpc GetUserLibrary(GetUserLibraryRequest) returns (GetUserLibraryResponse);
|
|
10
|
+
rpc GetLibraryItem(GetLibraryItemRequest) returns (GetLibraryItemResponse);
|
|
11
|
+
rpc UpdateLibraryItem(UpdateLibraryItemRequest) returns (UpdateLibraryItemResponse);
|
|
12
|
+
rpc RemoveFromLibrary(RemoveFromLibraryRequest) returns (RemoveFromLibraryResponse);
|
|
13
|
+
rpc IsInLibrary(IsInLibraryRequest) returns (IsInLibraryResponse);
|
|
14
|
+
rpc GetGenres(GetGenresRequest) returns (GetGenresResponse);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Mirrors prisma enum Status (PLANNED/IN_PROGRESS/COMPLETED/DROPPED).
|
|
18
|
+
enum LibraryItemStatus {
|
|
19
|
+
LIBRARY_ITEM_STATUS_UNSPECIFIED = 0;
|
|
20
|
+
LIBRARY_ITEM_STATUS_PLANNED = 1;
|
|
21
|
+
LIBRARY_ITEM_STATUS_IN_PROGRESS = 2;
|
|
22
|
+
LIBRARY_ITEM_STATUS_COMPLETED = 3;
|
|
23
|
+
LIBRARY_ITEM_STATUS_DROPPED = 4;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
message LibraryItem {
|
|
27
|
+
string id = 1;
|
|
28
|
+
string user_id = 2;
|
|
29
|
+
string media_id = 3;
|
|
30
|
+
LibraryItemStatus status = 4;
|
|
31
|
+
optional double rating = 5;
|
|
32
|
+
optional string notes = 6;
|
|
33
|
+
string added_at = 7;
|
|
34
|
+
string updated_at = 8;
|
|
35
|
+
// Embedded media snapshot for the read paths (denormalized view).
|
|
36
|
+
media.v1.Media media = 9;
|
|
37
|
+
// Pre-aggregated counts to avoid extra round-trips on the gateway side.
|
|
38
|
+
int32 library_count = 10;
|
|
39
|
+
int32 reviews_count = 11;
|
|
40
|
+
bool is_favorite = 12;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Compact search hit accepted by AddFromSearch (mirrors AddFromSearchDto.searchResult).
|
|
44
|
+
message LibrarySearchHit {
|
|
45
|
+
string id = 1;
|
|
46
|
+
string title = 2;
|
|
47
|
+
optional string subtitle = 3;
|
|
48
|
+
optional string image_url = 4;
|
|
49
|
+
optional string year = 5;
|
|
50
|
+
string type = 6;
|
|
51
|
+
string source = 7;
|
|
52
|
+
optional string external_id = 8;
|
|
53
|
+
optional double rating = 9;
|
|
54
|
+
repeated string genres = 10;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
message AddFromSearchRequest {
|
|
58
|
+
string user_id = 1;
|
|
59
|
+
LibrarySearchHit search_result = 2;
|
|
60
|
+
optional LibraryItemStatus status = 3;
|
|
61
|
+
optional double rating = 4;
|
|
62
|
+
optional string notes = 5;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
message AddFromSearchResponse {
|
|
66
|
+
LibraryItem item = 1;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
message GetUserLibraryRequest {
|
|
70
|
+
string user_id = 1;
|
|
71
|
+
repeated LibraryItemStatus statuses = 2;
|
|
72
|
+
optional string collection_name = 3;
|
|
73
|
+
optional media.v1.MediaSource source = 4;
|
|
74
|
+
optional string search = 5;
|
|
75
|
+
repeated string genres = 6;
|
|
76
|
+
optional double min_rating = 7;
|
|
77
|
+
optional double max_rating = 8;
|
|
78
|
+
optional int32 min_year = 9;
|
|
79
|
+
optional int32 max_year = 10;
|
|
80
|
+
int32 page = 11;
|
|
81
|
+
int32 limit = 12;
|
|
82
|
+
// addedAt | rating | title | releaseYear | favorites
|
|
83
|
+
string sort_by = 13;
|
|
84
|
+
string sort_order = 14;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
message LibraryPaginationMeta {
|
|
88
|
+
int32 page = 1;
|
|
89
|
+
int32 limit = 2;
|
|
90
|
+
int32 total = 3;
|
|
91
|
+
int32 total_pages = 4;
|
|
92
|
+
bool has_next_page = 5;
|
|
93
|
+
bool has_prev_page = 6;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
message GetUserLibraryResponse {
|
|
97
|
+
repeated LibraryItem data = 1;
|
|
98
|
+
LibraryPaginationMeta meta = 2;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
message GetLibraryItemRequest {
|
|
102
|
+
string user_id = 1;
|
|
103
|
+
string media_id = 2;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
message GetLibraryItemResponse {
|
|
107
|
+
LibraryItem item = 1;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
message UpdateLibraryItemRequest {
|
|
111
|
+
string user_id = 1;
|
|
112
|
+
string media_id = 2;
|
|
113
|
+
optional LibraryItemStatus status = 3;
|
|
114
|
+
optional double rating = 4;
|
|
115
|
+
optional string notes = 5;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
message UpdateLibraryItemResponse {
|
|
119
|
+
LibraryItem item = 1;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
message RemoveFromLibraryRequest {
|
|
123
|
+
string user_id = 1;
|
|
124
|
+
string media_id = 2;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
message RemoveFromLibraryResponse {}
|
|
128
|
+
|
|
129
|
+
message IsInLibraryRequest {
|
|
130
|
+
string user_id = 1;
|
|
131
|
+
string media_id = 2;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
message IsInLibraryResponse {
|
|
135
|
+
bool is_in_library = 1;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
message GetGenresRequest {
|
|
139
|
+
string user_id = 1;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
message GetGenresResponse {
|
|
143
|
+
repeated string genres = 1;
|
|
144
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
syntax = "proto3";
|
|
2
|
+
|
|
3
|
+
package media.v1;
|
|
4
|
+
|
|
5
|
+
service MediaService {
|
|
6
|
+
rpc CreateMedia(CreateMediaRequest) returns (CreateMediaResponse);
|
|
7
|
+
rpc GetMediaById(GetMediaByIdRequest) returns (GetMediaByIdResponse);
|
|
8
|
+
rpc GetMediaByExternalId(GetMediaByExternalIdRequest) returns (GetMediaByExternalIdResponse);
|
|
9
|
+
rpc FindDuplicates(FindDuplicatesRequest) returns (FindDuplicatesResponse);
|
|
10
|
+
rpc GetMediaStats(GetMediaStatsRequest) returns (GetMediaStatsResponse);
|
|
11
|
+
rpc UpdateMedia(UpdateMediaRequest) returns (UpdateMediaResponse);
|
|
12
|
+
rpc DeleteMedia(DeleteMediaRequest) returns (DeleteMediaResponse);
|
|
13
|
+
rpc ListMedia(ListMediaRequest) returns (ListMediaResponse);
|
|
14
|
+
|
|
15
|
+
// Cross-service: ensure media exists for a search hit.
|
|
16
|
+
// Used by library/favorites/recommendation flows so they don't need to write
|
|
17
|
+
// into the media database directly.
|
|
18
|
+
rpc EnsureMediaFromSearch(EnsureMediaFromSearchRequest) returns (EnsureMediaFromSearchResponse);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// MediaSource mirrors prisma enum MediaSource.
|
|
22
|
+
enum MediaSource {
|
|
23
|
+
MEDIA_SOURCE_UNSPECIFIED = 0;
|
|
24
|
+
MEDIA_SOURCE_TMDB = 1;
|
|
25
|
+
MEDIA_SOURCE_GOOGLE_BOOKS = 2;
|
|
26
|
+
MEDIA_SOURCE_MAL = 3;
|
|
27
|
+
MEDIA_SOURCE_CUSTOM = 4;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// MediaData is the structured payload kept as JSON in the legacy database.
|
|
31
|
+
// `custom_fields_json` contains the type-specific customFields object as a
|
|
32
|
+
// JSON-encoded string so callers can extend without breaking the schema.
|
|
33
|
+
message MediaData {
|
|
34
|
+
string title = 1;
|
|
35
|
+
string original_title = 2;
|
|
36
|
+
string description = 3;
|
|
37
|
+
int32 year = 4;
|
|
38
|
+
string poster_url = 5;
|
|
39
|
+
repeated string genres = 6;
|
|
40
|
+
double rating = 7;
|
|
41
|
+
string release_date = 8;
|
|
42
|
+
string country = 9;
|
|
43
|
+
string original_language = 10;
|
|
44
|
+
string custom_fields_json = 11;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
message Media {
|
|
48
|
+
string id = 1;
|
|
49
|
+
MediaSource source = 2;
|
|
50
|
+
string external_id = 3;
|
|
51
|
+
MediaData media_data = 4;
|
|
52
|
+
string searchable_title = 5;
|
|
53
|
+
string external_ids_json = 6;
|
|
54
|
+
string collection_id = 7;
|
|
55
|
+
string added_by_id = 8;
|
|
56
|
+
string created_at = 9;
|
|
57
|
+
string updated_at = 10;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
message MediaCounts {
|
|
61
|
+
int32 library = 1;
|
|
62
|
+
int32 reviews = 2;
|
|
63
|
+
int32 favorites = 3;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
message MediaWithCounts {
|
|
67
|
+
Media media = 1;
|
|
68
|
+
MediaCounts counts = 2;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
message CreateMediaRequest {
|
|
72
|
+
MediaSource source = 1;
|
|
73
|
+
string external_id = 2;
|
|
74
|
+
MediaData media_data = 3;
|
|
75
|
+
string collection_id = 4;
|
|
76
|
+
string added_by_id = 5;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
message CreateMediaResponse {
|
|
80
|
+
Media media = 1;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
message GetMediaByIdRequest {
|
|
84
|
+
string media_id = 1;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
message RatingDistributionEntry {
|
|
88
|
+
double rating = 1;
|
|
89
|
+
int32 count = 2;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
message GetMediaByIdResponse {
|
|
93
|
+
Media media = 1;
|
|
94
|
+
MediaCounts counts = 2;
|
|
95
|
+
double average_rating = 3;
|
|
96
|
+
int32 total_reviews = 4;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
message GetMediaByExternalIdRequest {
|
|
100
|
+
MediaSource source = 1;
|
|
101
|
+
string external_id = 2;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
message GetMediaByExternalIdResponse {
|
|
105
|
+
Media media = 1;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
message FindDuplicatesRequest {
|
|
109
|
+
string title = 1;
|
|
110
|
+
string external_id = 2;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
message FindDuplicatesResponse {
|
|
114
|
+
repeated Media items = 1;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
message GetMediaStatsRequest {
|
|
118
|
+
string media_id = 1;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
message MediaStats {
|
|
122
|
+
double average_rating = 1;
|
|
123
|
+
int32 total_reviews = 2;
|
|
124
|
+
repeated RatingDistributionEntry rating_distribution = 3;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
message GetMediaStatsResponse {
|
|
128
|
+
Media media = 1;
|
|
129
|
+
MediaStats stats = 2;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
message UpdateMediaRequest {
|
|
133
|
+
string media_id = 1;
|
|
134
|
+
optional MediaSource source = 2;
|
|
135
|
+
optional string external_id = 3;
|
|
136
|
+
optional MediaData media_data = 4;
|
|
137
|
+
optional string collection_id = 5;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
message UpdateMediaResponse {
|
|
141
|
+
Media media = 1;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
message DeleteMediaRequest {
|
|
145
|
+
string media_id = 1;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
message DeleteMediaResponse {}
|
|
149
|
+
|
|
150
|
+
message ListMediaRequest {
|
|
151
|
+
optional string search = 1;
|
|
152
|
+
optional string collection_id = 2;
|
|
153
|
+
optional MediaSource source = 3;
|
|
154
|
+
optional int32 year = 4;
|
|
155
|
+
int32 page = 5;
|
|
156
|
+
int32 limit = 6;
|
|
157
|
+
string sort_by = 7;
|
|
158
|
+
string sort_order = 8;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
message PaginationMeta {
|
|
162
|
+
int32 page = 1;
|
|
163
|
+
int32 limit = 2;
|
|
164
|
+
int32 total = 3;
|
|
165
|
+
int32 total_pages = 4;
|
|
166
|
+
bool has_next_page = 5;
|
|
167
|
+
bool has_prev_page = 6;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
message ListMediaResponse {
|
|
171
|
+
repeated MediaWithCounts data = 1;
|
|
172
|
+
PaginationMeta meta = 2;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
message EnsureMediaFromSearchRequest {
|
|
176
|
+
// Source identifier coming from search-service results
|
|
177
|
+
// ("tmdb", "google_books", "mangadex", ..., or "local" for already-existing)
|
|
178
|
+
string source = 1;
|
|
179
|
+
// External id within that source (or media id when source == "local")
|
|
180
|
+
string external_id = 2;
|
|
181
|
+
// Search media type label ("movie"|"series"|"book"|"anime"|"manga"|"manhwa"|"game"|"kdrama")
|
|
182
|
+
string media_type = 3;
|
|
183
|
+
string title = 4;
|
|
184
|
+
string subtitle = 5;
|
|
185
|
+
string image_url = 6;
|
|
186
|
+
string year = 7;
|
|
187
|
+
optional double rating = 8;
|
|
188
|
+
repeated string genres = 9;
|
|
189
|
+
// User who initiated the action (becomes addedBy on creation).
|
|
190
|
+
string requested_by_user_id = 10;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
message EnsureMediaFromSearchResponse {
|
|
194
|
+
Media media = 1;
|
|
195
|
+
// True when the media row was just created, false when reused.
|
|
196
|
+
bool created = 2;
|
|
197
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
syntax = "proto3";
|
|
2
|
+
|
|
3
|
+
package media_request.v1;
|
|
4
|
+
|
|
5
|
+
import "media/v1/media.proto";
|
|
6
|
+
|
|
7
|
+
service MediaRequestService {
|
|
8
|
+
// User-facing endpoints.
|
|
9
|
+
rpc CreateMediaRequest(CreateMediaRequestRequest) returns (CreateMediaRequestResponse);
|
|
10
|
+
rpc GetUserRequests(GetUserRequestsRequest) returns (GetUserRequestsResponse);
|
|
11
|
+
rpc GetMediaRequestById(GetMediaRequestByIdRequest) returns (GetMediaRequestByIdResponse);
|
|
12
|
+
|
|
13
|
+
// Admin / moderator endpoints.
|
|
14
|
+
rpc ListMediaRequests(ListMediaRequestsRequest) returns (ListMediaRequestsResponse);
|
|
15
|
+
rpc UpdateMediaRequest(UpdateMediaRequestRequest) returns (UpdateMediaRequestResponse);
|
|
16
|
+
rpc ModerateMediaRequest(ModerateMediaRequestRequest) returns (ModerateMediaRequestResponse);
|
|
17
|
+
rpc DeleteMediaRequest(DeleteMediaRequestRequest) returns (DeleteMediaRequestResponse);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Mirrors prisma enum ModerationType.
|
|
21
|
+
enum ModerationStatus {
|
|
22
|
+
MODERATION_STATUS_UNSPECIFIED = 0;
|
|
23
|
+
MODERATION_STATUS_PENDING = 1;
|
|
24
|
+
MODERATION_STATUS_APPROVED = 2;
|
|
25
|
+
MODERATION_STATUS_REJECTED = 3;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Lightweight reference to the user who acted on the request.
|
|
29
|
+
// Populated by the gateway / user-service when needed; kept self-contained.
|
|
30
|
+
message MediaRequestUserRef {
|
|
31
|
+
string id = 1;
|
|
32
|
+
string display_name = 2;
|
|
33
|
+
string picture = 3;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
message MediaRequest {
|
|
37
|
+
string id = 1;
|
|
38
|
+
ModerationStatus status = 2;
|
|
39
|
+
media.v1.MediaData media_data = 3;
|
|
40
|
+
string searchable_title = 4;
|
|
41
|
+
string external_ids_json = 5;
|
|
42
|
+
string comment = 6;
|
|
43
|
+
string moderator_note = 7;
|
|
44
|
+
string duplicate_check_status = 8;
|
|
45
|
+
string possible_duplicates_json = 9;
|
|
46
|
+
string requested_by_id = 10;
|
|
47
|
+
MediaRequestUserRef requested_by = 11;
|
|
48
|
+
string moderated_by_id = 12;
|
|
49
|
+
MediaRequestUserRef moderated_by = 13;
|
|
50
|
+
string approved_media_id = 14;
|
|
51
|
+
string collection_id = 15;
|
|
52
|
+
string created_at = 16;
|
|
53
|
+
string updated_at = 17;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
message CreateMediaRequestRequest {
|
|
57
|
+
string user_id = 1;
|
|
58
|
+
// Currently only CUSTOM is accepted (TMDB content goes through search flow).
|
|
59
|
+
optional media.v1.MediaSource source = 2;
|
|
60
|
+
media.v1.MediaData media_data = 3;
|
|
61
|
+
string collection_id = 4;
|
|
62
|
+
optional string comment = 5;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
message CreateMediaRequestResponse {
|
|
66
|
+
MediaRequest request = 1;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
message GetUserRequestsRequest {
|
|
70
|
+
string user_id = 1;
|
|
71
|
+
optional ModerationStatus status = 2;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
message GetUserRequestsResponse {
|
|
75
|
+
repeated MediaRequest items = 1;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
message GetMediaRequestByIdRequest {
|
|
79
|
+
string request_id = 1;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
message GetMediaRequestByIdResponse {
|
|
83
|
+
MediaRequest request = 1;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
message ListMediaRequestsRequest {
|
|
87
|
+
optional ModerationStatus status = 1;
|
|
88
|
+
optional media.v1.MediaSource source = 2;
|
|
89
|
+
optional string collection_id = 3;
|
|
90
|
+
optional string search = 4;
|
|
91
|
+
optional string requested_by_id = 5;
|
|
92
|
+
optional string moderated_by_id = 6;
|
|
93
|
+
optional int32 year = 7;
|
|
94
|
+
int32 page = 8;
|
|
95
|
+
int32 limit = 9;
|
|
96
|
+
string sort_by = 10;
|
|
97
|
+
string sort_order = 11;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
message MediaRequestPaginationMeta {
|
|
101
|
+
int32 page = 1;
|
|
102
|
+
int32 limit = 2;
|
|
103
|
+
int32 total = 3;
|
|
104
|
+
int32 total_pages = 4;
|
|
105
|
+
bool has_next_page = 5;
|
|
106
|
+
bool has_prev_page = 6;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
message ListMediaRequestsResponse {
|
|
110
|
+
repeated MediaRequest data = 1;
|
|
111
|
+
MediaRequestPaginationMeta meta = 2;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
message UpdateMediaRequestRequest {
|
|
115
|
+
string request_id = 1;
|
|
116
|
+
// Only PENDING requests can be updated.
|
|
117
|
+
optional media.v1.MediaData media_data = 2;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
message UpdateMediaRequestResponse {
|
|
121
|
+
MediaRequest request = 1;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
message ModerateMediaRequestRequest {
|
|
125
|
+
string request_id = 1;
|
|
126
|
+
string moderator_id = 2;
|
|
127
|
+
// APPROVED or REJECTED.
|
|
128
|
+
ModerationStatus status = 3;
|
|
129
|
+
optional string moderator_note = 4;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
message ModerateMediaRequestResponse {
|
|
133
|
+
MediaRequest request = 1;
|
|
134
|
+
// Populated when status == APPROVED. References media in media-service.
|
|
135
|
+
string approved_media_id = 2;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
message DeleteMediaRequestRequest {
|
|
139
|
+
string request_id = 1;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
message DeleteMediaRequestResponse {}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
syntax = "proto3";
|
|
2
|
+
|
|
3
|
+
package recommendation.v1;
|
|
4
|
+
|
|
5
|
+
import "search/v1/search.proto";
|
|
6
|
+
|
|
7
|
+
service RecommendationService {
|
|
8
|
+
// Bundles all four blocks shown on the home screen (trending / topRated /
|
|
9
|
+
// recommendedForYou / fromYourLibrary). When `user_id` is empty only the
|
|
10
|
+
// global blocks are populated.
|
|
11
|
+
rpc GetHomeRecommendations(GetHomeRecommendationsRequest) returns (GetHomeRecommendationsResponse);
|
|
12
|
+
|
|
13
|
+
// Force-refresh the global cached blocks (used by the cron job).
|
|
14
|
+
rpc RefreshGlobalBlocks(RefreshGlobalBlocksRequest) returns (RefreshGlobalBlocksResponse);
|
|
15
|
+
|
|
16
|
+
// Force-refresh a personal block for a given user.
|
|
17
|
+
rpc RefreshUserRecommendations(RefreshUserRecommendationsRequest) returns (RefreshUserRecommendationsResponse);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
message MediaRecommendationItem {
|
|
21
|
+
search.v1.SearchMediaType media_type = 1;
|
|
22
|
+
string title = 2;
|
|
23
|
+
optional int32 year = 3;
|
|
24
|
+
repeated string genres = 4;
|
|
25
|
+
optional double rating = 5;
|
|
26
|
+
// Absolute HTTPS poster URL (already passed through ImageService).
|
|
27
|
+
string poster_url = 6;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
message RecommendationBlock {
|
|
31
|
+
bool visible = 1;
|
|
32
|
+
repeated MediaRecommendationItem items = 2;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
message GetHomeRecommendationsRequest {
|
|
36
|
+
// Empty string => anonymous request (only trending + topRated are filled).
|
|
37
|
+
string user_id = 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
message GetHomeRecommendationsResponse {
|
|
41
|
+
RecommendationBlock trending = 1;
|
|
42
|
+
RecommendationBlock top_rated = 2;
|
|
43
|
+
RecommendationBlock recommended_for_you = 3;
|
|
44
|
+
RecommendationBlock from_your_library = 4;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
message RefreshGlobalBlocksRequest {}
|
|
48
|
+
|
|
49
|
+
message RefreshGlobalBlocksResponse {
|
|
50
|
+
int32 trending_count = 1;
|
|
51
|
+
int32 top_rated_count = 2;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
message RefreshUserRecommendationsRequest {
|
|
55
|
+
string user_id = 1;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
message RefreshUserRecommendationsResponse {
|
|
59
|
+
int32 items_count = 1;
|
|
60
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
syntax = "proto3";
|
|
2
|
+
|
|
3
|
+
package search.v1;
|
|
4
|
+
|
|
5
|
+
service SearchService {
|
|
6
|
+
// Cross-source search aggregator (TMDB, Google Books, AniList, MangaDex, IGDB,
|
|
7
|
+
// RAWG, Open Library + local db). Mirrors GET /search?query=&mediaType=.
|
|
8
|
+
rpc Search(SearchRequest) returns (SearchResponse);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Logical media types accepted by `mediaType` filter and returned in `type`.
|
|
12
|
+
enum SearchMediaType {
|
|
13
|
+
SEARCH_MEDIA_TYPE_UNSPECIFIED = 0;
|
|
14
|
+
SEARCH_MEDIA_TYPE_MOVIE = 1;
|
|
15
|
+
SEARCH_MEDIA_TYPE_SERIES = 2;
|
|
16
|
+
SEARCH_MEDIA_TYPE_BOOK = 3;
|
|
17
|
+
SEARCH_MEDIA_TYPE_ANIME = 4;
|
|
18
|
+
SEARCH_MEDIA_TYPE_MANGA = 5;
|
|
19
|
+
SEARCH_MEDIA_TYPE_MANHWA = 6;
|
|
20
|
+
SEARCH_MEDIA_TYPE_GAME = 7;
|
|
21
|
+
SEARCH_MEDIA_TYPE_KDRAMA = 8;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Origin of the result. "local" means the row already lives in our database.
|
|
25
|
+
enum SearchSource {
|
|
26
|
+
SEARCH_SOURCE_UNSPECIFIED = 0;
|
|
27
|
+
SEARCH_SOURCE_TMDB = 1;
|
|
28
|
+
SEARCH_SOURCE_GOOGLE_BOOKS = 2;
|
|
29
|
+
SEARCH_SOURCE_OPEN_LIBRARY = 3;
|
|
30
|
+
SEARCH_SOURCE_ANILIST = 4;
|
|
31
|
+
SEARCH_SOURCE_MANGADEX = 5;
|
|
32
|
+
SEARCH_SOURCE_IGDB = 6;
|
|
33
|
+
SEARCH_SOURCE_RAWG = 7;
|
|
34
|
+
SEARCH_SOURCE_LOCAL = 8;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
message SearchResult {
|
|
38
|
+
// External id (or local Media.id when source == LOCAL).
|
|
39
|
+
string id = 1;
|
|
40
|
+
string title = 2;
|
|
41
|
+
string subtitle = 3;
|
|
42
|
+
string description = 4;
|
|
43
|
+
string image_url = 5;
|
|
44
|
+
// Year as string because some sources return imprecise dates ("2020-..").
|
|
45
|
+
string year = 6;
|
|
46
|
+
optional double rating = 7;
|
|
47
|
+
repeated string genres = 8;
|
|
48
|
+
SearchMediaType type = 9;
|
|
49
|
+
SearchSource source = 10;
|
|
50
|
+
string external_id = 11;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
message SearchRequest {
|
|
54
|
+
string query = 1;
|
|
55
|
+
optional SearchMediaType media_type = 2;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
message SearchResponse {
|
|
59
|
+
repeated SearchResult results = 1;
|
|
60
|
+
int32 total_results = 2;
|
|
61
|
+
bool has_more = 3;
|
|
62
|
+
}
|