@financial-times/cp-content-pipeline-schema 3.18.0 → 3.19.0
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/CHANGELOG.md +7 -0
- package/lib/generated/index.d.ts +9 -0
- package/lib/helpers/qualityWorkaround.d.ts +2 -0
- package/lib/helpers/qualityWorkaround.js +37 -0
- package/lib/helpers/qualityWorkaround.js.map +1 -0
- package/lib/model/Clip.d.ts +1 -17
- package/lib/model/Clip.js +27 -8
- package/lib/model/Clip.js.map +1 -1
- package/lib/model/Clip.test.js +77 -0
- package/lib/model/Clip.test.js.map +1 -1
- package/lib/model/schemas/capi/article.d.ts +18 -0
- package/lib/model/schemas/capi/audio.d.ts +14 -0
- package/lib/model/schemas/capi/base-schema.d.ts +42 -0
- package/lib/model/schemas/capi/base-schema.js +2 -0
- package/lib/model/schemas/capi/base-schema.js.map +1 -1
- package/lib/model/schemas/capi/content-package.d.ts +14 -0
- package/lib/model/schemas/capi/custom-code-component.d.ts +18 -0
- package/lib/model/schemas/capi/index.d.ts +96 -0
- package/lib/model/schemas/capi/live-blog-package.d.ts +18 -0
- package/lib/model/schemas/capi/placeholder.d.ts +18 -0
- package/lib/model/schemas/capi/video.d.ts +14 -0
- package/lib/resolvers/clip.d.ts +4 -9
- package/lib/resolvers/clip.js +3 -0
- package/lib/resolvers/clip.js.map +1 -1
- package/lib/resolvers/index.d.ts +4 -9
- package/lib/types/clip.d.ts +21 -0
- package/lib/types/clip.js +3 -0
- package/lib/types/clip.js.map +1 -0
- package/package.json +1 -1
- package/queries/article.graphql +3 -0
- package/src/generated/index.ts +9 -0
- package/src/helpers/qualityWorkaround.ts +44 -0
- package/src/model/Clip.test.ts +95 -0
- package/src/model/Clip.ts +33 -27
- package/src/model/schemas/capi/base-schema.ts +2 -0
- package/src/resolvers/clip.ts +3 -0
- package/src/types/clip.ts +23 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/typedefs/clip.graphql +9 -0
|
@@ -853,6 +853,8 @@ export declare const liveBlogPackageSchema: import("zod").ZodObject<Pick<{
|
|
|
853
853
|
pixelHeight: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
854
854
|
pixelWidth: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
855
855
|
videoCodec: import("zod").ZodOptional<import("zod").ZodString>;
|
|
856
|
+
quality: import("zod").ZodOptional<import("zod").ZodString>;
|
|
857
|
+
dppx: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
856
858
|
}, "strip", import("zod").ZodTypeAny, {
|
|
857
859
|
binaryUrl?: string | undefined;
|
|
858
860
|
pixelWidth?: number | undefined;
|
|
@@ -861,6 +863,8 @@ export declare const liveBlogPackageSchema: import("zod").ZodObject<Pick<{
|
|
|
861
863
|
duration?: number | undefined;
|
|
862
864
|
mediaType?: string | undefined;
|
|
863
865
|
videoCodec?: string | undefined;
|
|
866
|
+
quality?: string | undefined;
|
|
867
|
+
dppx?: number | undefined;
|
|
864
868
|
}, {
|
|
865
869
|
binaryUrl?: string | undefined;
|
|
866
870
|
pixelWidth?: number | undefined;
|
|
@@ -869,6 +873,8 @@ export declare const liveBlogPackageSchema: import("zod").ZodObject<Pick<{
|
|
|
869
873
|
duration?: number | undefined;
|
|
870
874
|
mediaType?: string | undefined;
|
|
871
875
|
videoCodec?: string | undefined;
|
|
876
|
+
quality?: string | undefined;
|
|
877
|
+
dppx?: number | undefined;
|
|
872
878
|
}>, "many">;
|
|
873
879
|
poster: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
874
880
|
id: import("zod").ZodString;
|
|
@@ -1046,6 +1052,8 @@ export declare const liveBlogPackageSchema: import("zod").ZodObject<Pick<{
|
|
|
1046
1052
|
duration?: number | undefined;
|
|
1047
1053
|
mediaType?: string | undefined;
|
|
1048
1054
|
videoCodec?: string | undefined;
|
|
1055
|
+
quality?: string | undefined;
|
|
1056
|
+
dppx?: number | undefined;
|
|
1049
1057
|
}[];
|
|
1050
1058
|
format?: "standardInline" | "mobile" | undefined;
|
|
1051
1059
|
poster?: {
|
|
@@ -1093,6 +1101,8 @@ export declare const liveBlogPackageSchema: import("zod").ZodObject<Pick<{
|
|
|
1093
1101
|
duration?: number | undefined;
|
|
1094
1102
|
mediaType?: string | undefined;
|
|
1095
1103
|
videoCodec?: string | undefined;
|
|
1104
|
+
quality?: string | undefined;
|
|
1105
|
+
dppx?: number | undefined;
|
|
1096
1106
|
}[];
|
|
1097
1107
|
format?: "standardInline" | "mobile" | undefined;
|
|
1098
1108
|
poster?: {
|
|
@@ -1179,6 +1189,8 @@ export declare const liveBlogPackageSchema: import("zod").ZodObject<Pick<{
|
|
|
1179
1189
|
duration?: number | undefined;
|
|
1180
1190
|
mediaType?: string | undefined;
|
|
1181
1191
|
videoCodec?: string | undefined;
|
|
1192
|
+
quality?: string | undefined;
|
|
1193
|
+
dppx?: number | undefined;
|
|
1182
1194
|
}[];
|
|
1183
1195
|
format?: "standardInline" | "mobile" | undefined;
|
|
1184
1196
|
poster?: {
|
|
@@ -1247,6 +1259,8 @@ export declare const liveBlogPackageSchema: import("zod").ZodObject<Pick<{
|
|
|
1247
1259
|
duration?: number | undefined;
|
|
1248
1260
|
mediaType?: string | undefined;
|
|
1249
1261
|
videoCodec?: string | undefined;
|
|
1262
|
+
quality?: string | undefined;
|
|
1263
|
+
dppx?: number | undefined;
|
|
1250
1264
|
}[];
|
|
1251
1265
|
format?: "standardInline" | "mobile" | undefined;
|
|
1252
1266
|
poster?: {
|
|
@@ -1705,6 +1719,8 @@ export declare const liveBlogPackageSchema: import("zod").ZodObject<Pick<{
|
|
|
1705
1719
|
duration?: number | undefined;
|
|
1706
1720
|
mediaType?: string | undefined;
|
|
1707
1721
|
videoCodec?: string | undefined;
|
|
1722
|
+
quality?: string | undefined;
|
|
1723
|
+
dppx?: number | undefined;
|
|
1708
1724
|
}[];
|
|
1709
1725
|
format?: "standardInline" | "mobile" | undefined;
|
|
1710
1726
|
poster?: {
|
|
@@ -1966,6 +1982,8 @@ export declare const liveBlogPackageSchema: import("zod").ZodObject<Pick<{
|
|
|
1966
1982
|
duration?: number | undefined;
|
|
1967
1983
|
mediaType?: string | undefined;
|
|
1968
1984
|
videoCodec?: string | undefined;
|
|
1985
|
+
quality?: string | undefined;
|
|
1986
|
+
dppx?: number | undefined;
|
|
1969
1987
|
}[];
|
|
1970
1988
|
format?: "standardInline" | "mobile" | undefined;
|
|
1971
1989
|
poster?: {
|
|
@@ -853,6 +853,8 @@ export declare const placeholderSchema: import("zod").ZodObject<Pick<{
|
|
|
853
853
|
pixelHeight: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
854
854
|
pixelWidth: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
855
855
|
videoCodec: import("zod").ZodOptional<import("zod").ZodString>;
|
|
856
|
+
quality: import("zod").ZodOptional<import("zod").ZodString>;
|
|
857
|
+
dppx: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
856
858
|
}, "strip", import("zod").ZodTypeAny, {
|
|
857
859
|
binaryUrl?: string | undefined;
|
|
858
860
|
pixelWidth?: number | undefined;
|
|
@@ -861,6 +863,8 @@ export declare const placeholderSchema: import("zod").ZodObject<Pick<{
|
|
|
861
863
|
duration?: number | undefined;
|
|
862
864
|
mediaType?: string | undefined;
|
|
863
865
|
videoCodec?: string | undefined;
|
|
866
|
+
quality?: string | undefined;
|
|
867
|
+
dppx?: number | undefined;
|
|
864
868
|
}, {
|
|
865
869
|
binaryUrl?: string | undefined;
|
|
866
870
|
pixelWidth?: number | undefined;
|
|
@@ -869,6 +873,8 @@ export declare const placeholderSchema: import("zod").ZodObject<Pick<{
|
|
|
869
873
|
duration?: number | undefined;
|
|
870
874
|
mediaType?: string | undefined;
|
|
871
875
|
videoCodec?: string | undefined;
|
|
876
|
+
quality?: string | undefined;
|
|
877
|
+
dppx?: number | undefined;
|
|
872
878
|
}>, "many">;
|
|
873
879
|
poster: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
874
880
|
id: import("zod").ZodString;
|
|
@@ -1046,6 +1052,8 @@ export declare const placeholderSchema: import("zod").ZodObject<Pick<{
|
|
|
1046
1052
|
duration?: number | undefined;
|
|
1047
1053
|
mediaType?: string | undefined;
|
|
1048
1054
|
videoCodec?: string | undefined;
|
|
1055
|
+
quality?: string | undefined;
|
|
1056
|
+
dppx?: number | undefined;
|
|
1049
1057
|
}[];
|
|
1050
1058
|
format?: "standardInline" | "mobile" | undefined;
|
|
1051
1059
|
poster?: {
|
|
@@ -1093,6 +1101,8 @@ export declare const placeholderSchema: import("zod").ZodObject<Pick<{
|
|
|
1093
1101
|
duration?: number | undefined;
|
|
1094
1102
|
mediaType?: string | undefined;
|
|
1095
1103
|
videoCodec?: string | undefined;
|
|
1104
|
+
quality?: string | undefined;
|
|
1105
|
+
dppx?: number | undefined;
|
|
1096
1106
|
}[];
|
|
1097
1107
|
format?: "standardInline" | "mobile" | undefined;
|
|
1098
1108
|
poster?: {
|
|
@@ -1179,6 +1189,8 @@ export declare const placeholderSchema: import("zod").ZodObject<Pick<{
|
|
|
1179
1189
|
duration?: number | undefined;
|
|
1180
1190
|
mediaType?: string | undefined;
|
|
1181
1191
|
videoCodec?: string | undefined;
|
|
1192
|
+
quality?: string | undefined;
|
|
1193
|
+
dppx?: number | undefined;
|
|
1182
1194
|
}[];
|
|
1183
1195
|
format?: "standardInline" | "mobile" | undefined;
|
|
1184
1196
|
poster?: {
|
|
@@ -1247,6 +1259,8 @@ export declare const placeholderSchema: import("zod").ZodObject<Pick<{
|
|
|
1247
1259
|
duration?: number | undefined;
|
|
1248
1260
|
mediaType?: string | undefined;
|
|
1249
1261
|
videoCodec?: string | undefined;
|
|
1262
|
+
quality?: string | undefined;
|
|
1263
|
+
dppx?: number | undefined;
|
|
1250
1264
|
}[];
|
|
1251
1265
|
format?: "standardInline" | "mobile" | undefined;
|
|
1252
1266
|
poster?: {
|
|
@@ -1728,6 +1742,8 @@ export declare const placeholderSchema: import("zod").ZodObject<Pick<{
|
|
|
1728
1742
|
duration?: number | undefined;
|
|
1729
1743
|
mediaType?: string | undefined;
|
|
1730
1744
|
videoCodec?: string | undefined;
|
|
1745
|
+
quality?: string | undefined;
|
|
1746
|
+
dppx?: number | undefined;
|
|
1731
1747
|
}[];
|
|
1732
1748
|
format?: "standardInline" | "mobile" | undefined;
|
|
1733
1749
|
poster?: {
|
|
@@ -2012,6 +2028,8 @@ export declare const placeholderSchema: import("zod").ZodObject<Pick<{
|
|
|
2012
2028
|
duration?: number | undefined;
|
|
2013
2029
|
mediaType?: string | undefined;
|
|
2014
2030
|
videoCodec?: string | undefined;
|
|
2031
|
+
quality?: string | undefined;
|
|
2032
|
+
dppx?: number | undefined;
|
|
2015
2033
|
}[];
|
|
2016
2034
|
format?: "standardInline" | "mobile" | undefined;
|
|
2017
2035
|
poster?: {
|
|
@@ -853,6 +853,8 @@ export declare const videoSchema: import("zod").ZodObject<Pick<{
|
|
|
853
853
|
pixelHeight: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
854
854
|
pixelWidth: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
855
855
|
videoCodec: import("zod").ZodOptional<import("zod").ZodString>;
|
|
856
|
+
quality: import("zod").ZodOptional<import("zod").ZodString>;
|
|
857
|
+
dppx: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
856
858
|
}, "strip", import("zod").ZodTypeAny, {
|
|
857
859
|
binaryUrl?: string | undefined;
|
|
858
860
|
pixelWidth?: number | undefined;
|
|
@@ -861,6 +863,8 @@ export declare const videoSchema: import("zod").ZodObject<Pick<{
|
|
|
861
863
|
duration?: number | undefined;
|
|
862
864
|
mediaType?: string | undefined;
|
|
863
865
|
videoCodec?: string | undefined;
|
|
866
|
+
quality?: string | undefined;
|
|
867
|
+
dppx?: number | undefined;
|
|
864
868
|
}, {
|
|
865
869
|
binaryUrl?: string | undefined;
|
|
866
870
|
pixelWidth?: number | undefined;
|
|
@@ -869,6 +873,8 @@ export declare const videoSchema: import("zod").ZodObject<Pick<{
|
|
|
869
873
|
duration?: number | undefined;
|
|
870
874
|
mediaType?: string | undefined;
|
|
871
875
|
videoCodec?: string | undefined;
|
|
876
|
+
quality?: string | undefined;
|
|
877
|
+
dppx?: number | undefined;
|
|
872
878
|
}>, "many">;
|
|
873
879
|
poster: import("zod").ZodOptional<import("zod").ZodObject<{
|
|
874
880
|
id: import("zod").ZodString;
|
|
@@ -1046,6 +1052,8 @@ export declare const videoSchema: import("zod").ZodObject<Pick<{
|
|
|
1046
1052
|
duration?: number | undefined;
|
|
1047
1053
|
mediaType?: string | undefined;
|
|
1048
1054
|
videoCodec?: string | undefined;
|
|
1055
|
+
quality?: string | undefined;
|
|
1056
|
+
dppx?: number | undefined;
|
|
1049
1057
|
}[];
|
|
1050
1058
|
format?: "standardInline" | "mobile" | undefined;
|
|
1051
1059
|
poster?: {
|
|
@@ -1093,6 +1101,8 @@ export declare const videoSchema: import("zod").ZodObject<Pick<{
|
|
|
1093
1101
|
duration?: number | undefined;
|
|
1094
1102
|
mediaType?: string | undefined;
|
|
1095
1103
|
videoCodec?: string | undefined;
|
|
1104
|
+
quality?: string | undefined;
|
|
1105
|
+
dppx?: number | undefined;
|
|
1096
1106
|
}[];
|
|
1097
1107
|
format?: "standardInline" | "mobile" | undefined;
|
|
1098
1108
|
poster?: {
|
|
@@ -1179,6 +1189,8 @@ export declare const videoSchema: import("zod").ZodObject<Pick<{
|
|
|
1179
1189
|
duration?: number | undefined;
|
|
1180
1190
|
mediaType?: string | undefined;
|
|
1181
1191
|
videoCodec?: string | undefined;
|
|
1192
|
+
quality?: string | undefined;
|
|
1193
|
+
dppx?: number | undefined;
|
|
1182
1194
|
}[];
|
|
1183
1195
|
format?: "standardInline" | "mobile" | undefined;
|
|
1184
1196
|
poster?: {
|
|
@@ -1247,6 +1259,8 @@ export declare const videoSchema: import("zod").ZodObject<Pick<{
|
|
|
1247
1259
|
duration?: number | undefined;
|
|
1248
1260
|
mediaType?: string | undefined;
|
|
1249
1261
|
videoCodec?: string | undefined;
|
|
1262
|
+
quality?: string | undefined;
|
|
1263
|
+
dppx?: number | undefined;
|
|
1250
1264
|
}[];
|
|
1251
1265
|
format?: "standardInline" | "mobile" | undefined;
|
|
1252
1266
|
poster?: {
|
package/lib/resolvers/clip.d.ts
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
declare const resolvers: {
|
|
2
2
|
Clip: {
|
|
3
3
|
__resolveType: () => string;
|
|
4
|
-
dataSource: (clip: import("../model/Clip").Clip) =>
|
|
5
|
-
binaryUrl: string;
|
|
6
|
-
mediaType: string;
|
|
7
|
-
audioCodec?: string;
|
|
8
|
-
duration?: number;
|
|
9
|
-
pixelHeight?: number;
|
|
10
|
-
pixelWidth?: number;
|
|
11
|
-
videoCodec?: string;
|
|
12
|
-
}[];
|
|
4
|
+
dataSource: (clip: import("../model/Clip").Clip) => import("../types/clip").ClipSource[];
|
|
13
5
|
type: (clip: import("../model/Clip").Clip) => string;
|
|
14
6
|
format: (clip: import("../model/Clip").Clip) => "mobile" | "standard-inline";
|
|
15
7
|
poster: (clip: import("../model/Clip").Clip) => string;
|
|
@@ -23,6 +15,9 @@ declare const resolvers: {
|
|
|
23
15
|
pixelWidth: (parent: import("../generated").ClipSource) => number | null;
|
|
24
16
|
pixelHeight: (parent: import("../generated").ClipSource) => number | null;
|
|
25
17
|
videoCodec: (parent: import("../generated").ClipSource) => string | null;
|
|
18
|
+
quality: (parent: import("../generated").ClipSource) => string | null;
|
|
19
|
+
dppx: (parent: import("../generated").ClipSource) => number | null;
|
|
20
|
+
previousSourceWidth: (parent: import("../generated").ClipSource) => number | null;
|
|
26
21
|
};
|
|
27
22
|
};
|
|
28
23
|
export default resolvers;
|
package/lib/resolvers/clip.js
CHANGED
|
@@ -17,6 +17,9 @@ const resolvers = {
|
|
|
17
17
|
pixelWidth: (parent) => parent.pixelWidth ?? null,
|
|
18
18
|
pixelHeight: (parent) => parent.pixelHeight ?? null,
|
|
19
19
|
videoCodec: (parent) => parent.videoCodec ?? null,
|
|
20
|
+
quality: (parent) => parent.quality ?? null,
|
|
21
|
+
dppx: (parent) => parent.dppx ?? null,
|
|
22
|
+
previousSourceWidth: (parent) => parent.previousSourceWidth ?? null,
|
|
20
23
|
},
|
|
21
24
|
};
|
|
22
25
|
exports.default = resolvers;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clip.js","sourceRoot":"","sources":["../../src/resolvers/clip.ts"],"names":[],"mappings":";;AAEA,MAAM,SAAS,GAAG;IAChB,IAAI,EAAE;QACJ,aAAa,EAAE,GAAG,EAAE,CAAC,MAAM;QAC3B,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE;QACvC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE;QAC3B,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE;QAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE;QAC/B,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE;KACxB;IAED,UAAU,EAAE;QACV,UAAU,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI;QACjD,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS;QACvC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI;QAC7C,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS;QACvC,UAAU,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI;QACjD,WAAW,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,IAAI,IAAI;QACnD,UAAU,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI;
|
|
1
|
+
{"version":3,"file":"clip.js","sourceRoot":"","sources":["../../src/resolvers/clip.ts"],"names":[],"mappings":";;AAEA,MAAM,SAAS,GAAG;IAChB,IAAI,EAAE;QACJ,aAAa,EAAE,GAAG,EAAE,CAAC,MAAM;QAC3B,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE;QACvC,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE;QAC3B,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE;QAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE;QAC/B,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE;KACxB;IAED,UAAU,EAAE;QACV,UAAU,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI;QACjD,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS;QACvC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,IAAI,IAAI;QAC7C,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS;QACvC,UAAU,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI;QACjD,WAAW,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,IAAI,IAAI;QACnD,UAAU,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI;QACjD,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,IAAI,IAAI;QAC3C,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI;QACrC,mBAAmB,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,mBAAmB,IAAI,IAAI;KACpE;CACiE,CAAA;AAEpE,kBAAe,SAAS,CAAA"}
|
package/lib/resolvers/index.d.ts
CHANGED
|
@@ -344,15 +344,7 @@ declare const resolvers: {
|
|
|
344
344
|
MetaLink: import("../generated").MetaLinkResolvers;
|
|
345
345
|
Clip: {
|
|
346
346
|
__resolveType: () => string;
|
|
347
|
-
dataSource: (clip: import("../model/Clip").Clip) =>
|
|
348
|
-
binaryUrl: string;
|
|
349
|
-
mediaType: string;
|
|
350
|
-
audioCodec?: string;
|
|
351
|
-
duration?: number;
|
|
352
|
-
pixelHeight?: number;
|
|
353
|
-
pixelWidth?: number;
|
|
354
|
-
videoCodec?: string;
|
|
355
|
-
}[];
|
|
347
|
+
dataSource: (clip: import("../model/Clip").Clip) => import("../types/clip").ClipSource[];
|
|
356
348
|
type: (clip: import("../model/Clip").Clip) => string;
|
|
357
349
|
format: (clip: import("../model/Clip").Clip) => "mobile" | "standard-inline";
|
|
358
350
|
poster: (clip: import("../model/Clip").Clip) => string;
|
|
@@ -366,6 +358,9 @@ declare const resolvers: {
|
|
|
366
358
|
pixelWidth: (parent: import("../generated").ClipSource) => number | null;
|
|
367
359
|
pixelHeight: (parent: import("../generated").ClipSource) => number | null;
|
|
368
360
|
videoCodec: (parent: import("../generated").ClipSource) => string | null;
|
|
361
|
+
quality: (parent: import("../generated").ClipSource) => string | null;
|
|
362
|
+
dppx: (parent: import("../generated").ClipSource) => number | null;
|
|
363
|
+
previousSourceWidth: (parent: import("../generated").ClipSource) => number | null;
|
|
369
364
|
};
|
|
370
365
|
Image: {
|
|
371
366
|
__resolveType: import("../generated").TypeResolveFn<"ImageDesktop" | "ImageLandscape" | "ImageMobile" | "ImagePortrait" | "ImageSquare" | "ImageSquareFTEdit" | "ImageStandard" | "ImageStandardInline" | "ImageWide", import("../model/Image").Image, import("..").QueryContext>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { LiteralUnionScalarValues } from '../resolvers/literal-union';
|
|
2
|
+
import { ClipFormat } from '../resolvers/scalars';
|
|
3
|
+
export type ClipSource = {
|
|
4
|
+
binaryUrl: string;
|
|
5
|
+
mediaType: string;
|
|
6
|
+
audioCodec?: string;
|
|
7
|
+
duration?: number;
|
|
8
|
+
pixelHeight?: number;
|
|
9
|
+
pixelWidth?: number;
|
|
10
|
+
videoCodec?: string;
|
|
11
|
+
quality?: string;
|
|
12
|
+
dppx?: number;
|
|
13
|
+
previousSourceWidth?: number;
|
|
14
|
+
};
|
|
15
|
+
export interface ClipVideo {
|
|
16
|
+
id(): string;
|
|
17
|
+
type(): string;
|
|
18
|
+
format(): LiteralUnionScalarValues<typeof ClipFormat>;
|
|
19
|
+
poster(): string;
|
|
20
|
+
dataSource(): ClipSource[];
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clip.js","sourceRoot":"","sources":["../../src/types/clip.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
package/queries/article.graphql
CHANGED
package/src/generated/index.ts
CHANGED
|
@@ -377,6 +377,8 @@ export type ClipSource = {
|
|
|
377
377
|
readonly audioCodec?: Maybe<Scalars['String']['output']>;
|
|
378
378
|
/** The url of the clip source, eg. 'https://next-media-api.ft.com/renditions/16868569859480/0x0.mp3'. */
|
|
379
379
|
readonly binaryUrl: Scalars['String']['output'];
|
|
380
|
+
/** The encoding settings intention for the video media */
|
|
381
|
+
readonly dppx?: Maybe<Scalars['Int']['output']>;
|
|
380
382
|
/** The duration of the clip in milliseconds. */
|
|
381
383
|
readonly duration?: Maybe<Scalars['Int']['output']>;
|
|
382
384
|
/** The media type eg. video/mp4. */
|
|
@@ -385,6 +387,10 @@ export type ClipSource = {
|
|
|
385
387
|
readonly pixelHeight?: Maybe<Scalars['Int']['output']>;
|
|
386
388
|
/** The width of the clip in pixels. */
|
|
387
389
|
readonly pixelWidth?: Maybe<Scalars['Int']['output']>;
|
|
390
|
+
/** The width of the source immediately narrower than this one. */
|
|
391
|
+
readonly previousSourceWidth?: Maybe<Scalars['Int']['output']>;
|
|
392
|
+
/** The encoding quality of the video media */
|
|
393
|
+
readonly quality?: Maybe<Scalars['String']['output']>;
|
|
388
394
|
/** The video encoding format of the source, eg. h264. */
|
|
389
395
|
readonly videoCodec?: Maybe<Scalars['String']['output']>;
|
|
390
396
|
};
|
|
@@ -2413,10 +2419,13 @@ export type ClipSetResolvers<ContextType = QueryContext, ParentType extends Reso
|
|
|
2413
2419
|
export type ClipSourceResolvers<ContextType = QueryContext, ParentType extends ResolversParentTypes['ClipSource'] = ResolversParentTypes['ClipSource']> = ResolversObject<{
|
|
2414
2420
|
audioCodec: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2415
2421
|
binaryUrl: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2422
|
+
dppx: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
|
2416
2423
|
duration: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
|
2417
2424
|
mediaType: Resolver<ResolversTypes['String'], ParentType, ContextType>;
|
|
2418
2425
|
pixelHeight: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
|
2419
2426
|
pixelWidth: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
|
2427
|
+
previousSourceWidth: Resolver<Maybe<ResolversTypes['Int']>, ParentType, ContextType>;
|
|
2428
|
+
quality: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2420
2429
|
videoCodec: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
|
|
2421
2430
|
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
|
|
2422
2431
|
}>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// until we get quality setting data through from CAPI, we infer the quality and ddpx from the details in the filename
|
|
2
|
+
// once we have quality data from CAPI, we can remove this module
|
|
3
|
+
import type { ClipSource } from '../types/clip'
|
|
4
|
+
|
|
5
|
+
// possible output sizes are set in https://github.com/Financial-Times/ip-ovide/tree/main/common/n-zencoder/src/profiles
|
|
6
|
+
const dimensions = new Map([
|
|
7
|
+
[1920, 1080],
|
|
8
|
+
[1280, 720],
|
|
9
|
+
[960, 540],
|
|
10
|
+
[640, 360],
|
|
11
|
+
[480, 270],
|
|
12
|
+
])
|
|
13
|
+
|
|
14
|
+
// utility to extract width from filename pattern like /640x360.mp4
|
|
15
|
+
function getDimensionsFromFilename(url: string): number[] {
|
|
16
|
+
// regex: get the first set of digits after a "/" and before "x", followed by 1+ digits, followed by ".mp" and 1 digit, then end of string.
|
|
17
|
+
const widthRegex = /\/(\d+)x(\d+)\.mp\d$/i
|
|
18
|
+
const widthMatch = url.match(widthRegex)
|
|
19
|
+
return [
|
|
20
|
+
parseInt(widthMatch?.[1] ?? '0', 10),
|
|
21
|
+
parseInt(widthMatch?.[2] ?? '0', 10),
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default function qualityWorkaround(
|
|
26
|
+
dataSource: ClipSource[]
|
|
27
|
+
): ClipSource[] {
|
|
28
|
+
return dataSource.map((source) => {
|
|
29
|
+
const originalDimensions = getDimensionsFromFilename(source.binaryUrl ?? '')
|
|
30
|
+
if (originalDimensions[0] === 0) {
|
|
31
|
+
// this will apply to audio files
|
|
32
|
+
return source
|
|
33
|
+
}
|
|
34
|
+
const smallerDimension = Math.min(...originalDimensions)
|
|
35
|
+
source.quality = `${
|
|
36
|
+
Array.from(dimensions.values()).includes(smallerDimension)
|
|
37
|
+
? smallerDimension
|
|
38
|
+
: dimensions.get(smallerDimension)
|
|
39
|
+
}p`
|
|
40
|
+
|
|
41
|
+
source.dppx = smallerDimension >= 1080 ? 2 : 1
|
|
42
|
+
return source
|
|
43
|
+
})
|
|
44
|
+
}
|
package/src/model/Clip.test.ts
CHANGED
|
@@ -4,6 +4,42 @@ const mockContext = {
|
|
|
4
4
|
systemCode: 'cp-content-pipeline',
|
|
5
5
|
} as unknown as QueryContext
|
|
6
6
|
|
|
7
|
+
const mockDataSources = [
|
|
8
|
+
{
|
|
9
|
+
audioCodec: 'mp3',
|
|
10
|
+
binaryUrl: 'https://clips.ft.com/someguid/0x0.mp3',
|
|
11
|
+
duration: 21720,
|
|
12
|
+
mediaType: 'audio/mpeg',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
audioCodec: 'aac',
|
|
16
|
+
binaryUrl: 'https://clips.ft.com/someguid/640x360.mp4',
|
|
17
|
+
duration: 21684,
|
|
18
|
+
mediaType: 'video/mp4',
|
|
19
|
+
pixelHeight: 360,
|
|
20
|
+
pixelWidth: 640,
|
|
21
|
+
videoCodec: 'h264',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
audioCodec: 'aac',
|
|
25
|
+
binaryUrl: 'https://clips.ft.com/someguid/1280x720.mp4',
|
|
26
|
+
duration: 21684,
|
|
27
|
+
mediaType: 'video/mp4',
|
|
28
|
+
pixelHeight: 720,
|
|
29
|
+
pixelWidth: 1280,
|
|
30
|
+
videoCodec: 'h264',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
audioCodec: 'aac',
|
|
34
|
+
binaryUrl: 'https://clips.ft.com/someguid/1920x1080.mp4',
|
|
35
|
+
duration: 21684,
|
|
36
|
+
mediaType: 'video/mp4',
|
|
37
|
+
pixelHeight: 720,
|
|
38
|
+
pixelWidth: 1280,
|
|
39
|
+
videoCodec: 'h264',
|
|
40
|
+
},
|
|
41
|
+
]
|
|
42
|
+
|
|
7
43
|
describe('Clip', () => {
|
|
8
44
|
describe('poster', () => {
|
|
9
45
|
it('uses the Origami Image Service when a url is provided', () => {
|
|
@@ -170,4 +206,63 @@ describe('Clip', () => {
|
|
|
170
206
|
expect(clip.poster()).toContain('width=1200')
|
|
171
207
|
})
|
|
172
208
|
})
|
|
209
|
+
describe('datasources', () => {
|
|
210
|
+
it('creates a quality value and dppx value based on the width in the URL when quality is not provided', () => {
|
|
211
|
+
const clip = new Clip(
|
|
212
|
+
{
|
|
213
|
+
id: 'http://api.ft.com/things/1234',
|
|
214
|
+
type: 'http://www.ft.com/ontology/content/Clip',
|
|
215
|
+
dataSource: mockDataSources,
|
|
216
|
+
},
|
|
217
|
+
mockContext
|
|
218
|
+
)
|
|
219
|
+
const dataSource = clip.dataSource()
|
|
220
|
+
expect(dataSource).toHaveLength(4)
|
|
221
|
+
expect(dataSource?.[0]?.quality).toBe('1080p')
|
|
222
|
+
expect(dataSource?.[0]?.dppx).toBe(2)
|
|
223
|
+
expect(dataSource?.[1]?.quality).toBe('720p')
|
|
224
|
+
expect(dataSource?.[2]?.quality).toBe('360p')
|
|
225
|
+
expect(dataSource?.[3]?.quality).toBeUndefined()
|
|
226
|
+
})
|
|
227
|
+
it('orders the data sources by width and then dppx', () => {
|
|
228
|
+
const clip = new Clip(
|
|
229
|
+
{
|
|
230
|
+
id: 'http://api.ft.com/things/1234',
|
|
231
|
+
type: 'http://www.ft.com/ontology/content/Clip',
|
|
232
|
+
dataSource: mockDataSources,
|
|
233
|
+
},
|
|
234
|
+
mockContext
|
|
235
|
+
)
|
|
236
|
+
const dataSource = clip.dataSource()
|
|
237
|
+
expect(dataSource).toHaveLength(4)
|
|
238
|
+
expect(dataSource?.[0]?.binaryUrl).toBe(
|
|
239
|
+
'https://clips.ft.com/someguid/1920x1080.mp4'
|
|
240
|
+
)
|
|
241
|
+
expect(dataSource?.[1]?.binaryUrl).toBe(
|
|
242
|
+
'https://clips.ft.com/someguid/1280x720.mp4'
|
|
243
|
+
)
|
|
244
|
+
expect(dataSource?.[2]?.binaryUrl).toBe(
|
|
245
|
+
'https://clips.ft.com/someguid/640x360.mp4'
|
|
246
|
+
)
|
|
247
|
+
expect(dataSource?.[3]?.binaryUrl).toBe(
|
|
248
|
+
'https://clips.ft.com/someguid/0x0.mp3'
|
|
249
|
+
)
|
|
250
|
+
})
|
|
251
|
+
it('sets the previousSourceWidth to the next most narrow source', () => {
|
|
252
|
+
const clip = new Clip(
|
|
253
|
+
{
|
|
254
|
+
id: 'http://api.ft.com/things/1234',
|
|
255
|
+
type: 'http://www.ft.com/ontology/content/Clip',
|
|
256
|
+
dataSource: mockDataSources,
|
|
257
|
+
},
|
|
258
|
+
mockContext
|
|
259
|
+
)
|
|
260
|
+
const dataSource = clip.dataSource()
|
|
261
|
+
expect(dataSource).toHaveLength(4)
|
|
262
|
+
expect(dataSource?.[0]?.previousSourceWidth).toBe(640)
|
|
263
|
+
expect(dataSource?.[1]?.previousSourceWidth).toBe(640)
|
|
264
|
+
expect(dataSource?.[2]?.previousSourceWidth).toBeUndefined()
|
|
265
|
+
expect(dataSource?.[3]?.previousSourceWidth).toBeUndefined()
|
|
266
|
+
})
|
|
267
|
+
})
|
|
173
268
|
})
|
package/src/model/Clip.ts
CHANGED
|
@@ -6,25 +6,9 @@ import {
|
|
|
6
6
|
} from '../resolvers/literal-union'
|
|
7
7
|
import { ClipFormat } from '../resolvers/scalars'
|
|
8
8
|
import imageServiceUrl from '../helpers/imageService'
|
|
9
|
+
import qualityWorkaround from '../helpers/qualityWorkaround'
|
|
9
10
|
import { QueryContext } from '..'
|
|
10
|
-
|
|
11
|
-
type ClipSource = {
|
|
12
|
-
binaryUrl: string
|
|
13
|
-
mediaType: string
|
|
14
|
-
audioCodec?: string
|
|
15
|
-
duration?: number
|
|
16
|
-
pixelHeight?: number
|
|
17
|
-
pixelWidth?: number
|
|
18
|
-
videoCodec?: string
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface ClipVideo {
|
|
22
|
-
id(): string
|
|
23
|
-
type(): string
|
|
24
|
-
format(): LiteralUnionScalarValues<typeof ClipFormat>
|
|
25
|
-
poster(): string
|
|
26
|
-
dataSource(): ClipSource[]
|
|
27
|
-
}
|
|
11
|
+
import type { ClipSource, ClipVideo } from '../types/clip'
|
|
28
12
|
|
|
29
13
|
export class Clip implements ClipVideo {
|
|
30
14
|
constructor(
|
|
@@ -60,18 +44,40 @@ export class Clip implements ClipVideo {
|
|
|
60
44
|
return 'standard-inline'
|
|
61
45
|
}
|
|
62
46
|
|
|
47
|
+
// apply 'quality' workaround
|
|
48
|
+
// order sources by width and then quality
|
|
49
|
+
// get the min width from the first previous more narrow item
|
|
63
50
|
dataSource() {
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
51
|
+
// create a copy of the array so we don't modify the original data
|
|
52
|
+
let dataSource = this.clip.dataSource.map((src) => ({
|
|
53
|
+
...src,
|
|
54
|
+
})) as ClipSource[]
|
|
55
|
+
dataSource = qualityWorkaround(dataSource as ClipSource[])
|
|
56
|
+
dataSource = dataSource.sort((a, b) => {
|
|
57
|
+
const widthDiff = (b.pixelWidth ?? 0) - (a.pixelWidth ?? 0)
|
|
58
|
+
if (widthDiff !== 0) {
|
|
59
|
+
return widthDiff
|
|
71
60
|
}
|
|
72
|
-
return 0
|
|
61
|
+
return (b?.dppx ?? 0) - (a?.dppx ?? 0)
|
|
73
62
|
})
|
|
74
|
-
|
|
63
|
+
|
|
64
|
+
// get the min width from the previous item if it has pixelWidth set, checking it is not the same as pixelWidth as the current item
|
|
65
|
+
for (let i = 0; i < dataSource.length; i++) {
|
|
66
|
+
let next = 1
|
|
67
|
+
let minWidth = dataSource[i + next]?.pixelWidth
|
|
68
|
+
while (
|
|
69
|
+
typeof minWidth !== 'undefined' &&
|
|
70
|
+
minWidth === dataSource[i]?.pixelWidth
|
|
71
|
+
) {
|
|
72
|
+
next++
|
|
73
|
+
minWidth = dataSource[i + next]?.pixelWidth
|
|
74
|
+
}
|
|
75
|
+
if (minWidth && dataSource[i]) {
|
|
76
|
+
dataSource[i]!.previousSourceWidth = minWidth
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return dataSource
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
poster() {
|
|
@@ -103,6 +103,8 @@ const ClipSource = z.object({
|
|
|
103
103
|
pixelHeight: z.number().optional(),
|
|
104
104
|
pixelWidth: z.number().optional(),
|
|
105
105
|
videoCodec: z.string().optional(),
|
|
106
|
+
quality: z.string().optional(),
|
|
107
|
+
dppx: z.number().optional(),
|
|
106
108
|
})
|
|
107
109
|
|
|
108
110
|
export const Clip = z.object({
|
package/src/resolvers/clip.ts
CHANGED
|
@@ -18,6 +18,9 @@ const resolvers = {
|
|
|
18
18
|
pixelWidth: (parent) => parent.pixelWidth ?? null,
|
|
19
19
|
pixelHeight: (parent) => parent.pixelHeight ?? null,
|
|
20
20
|
videoCodec: (parent) => parent.videoCodec ?? null,
|
|
21
|
+
quality: (parent) => parent.quality ?? null,
|
|
22
|
+
dppx: (parent) => parent.dppx ?? null,
|
|
23
|
+
previousSourceWidth: (parent) => parent.previousSourceWidth ?? null,
|
|
21
24
|
},
|
|
22
25
|
} satisfies { Clip: ClipResolvers; ClipSource: ClipSourceResolvers }
|
|
23
26
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { LiteralUnionScalarValues } from '../resolvers/literal-union'
|
|
2
|
+
import { ClipFormat } from '../resolvers/scalars'
|
|
3
|
+
|
|
4
|
+
export type ClipSource = {
|
|
5
|
+
binaryUrl: string
|
|
6
|
+
mediaType: string
|
|
7
|
+
audioCodec?: string
|
|
8
|
+
duration?: number
|
|
9
|
+
pixelHeight?: number
|
|
10
|
+
pixelWidth?: number
|
|
11
|
+
videoCodec?: string
|
|
12
|
+
quality?: string
|
|
13
|
+
dppx?: number
|
|
14
|
+
previousSourceWidth?: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ClipVideo {
|
|
18
|
+
id(): string
|
|
19
|
+
type(): string
|
|
20
|
+
format(): LiteralUnionScalarValues<typeof ClipFormat>
|
|
21
|
+
poster(): string
|
|
22
|
+
dataSource(): ClipSource[]
|
|
23
|
+
}
|