@playpilot/tpi 1.1.1 → 1.4.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/dist/link-injections.js +7 -7
- package/package.json +3 -1
- package/src/lib/api.js +5 -1
- package/src/lib/data/translations.js +238 -0
- package/src/lib/enums/Language.js +4 -0
- package/src/lib/linkInjection.js +11 -4
- package/src/lib/localization.js +36 -0
- package/src/lib/meta.js +82 -0
- package/src/lib/playlink.js +28 -0
- package/src/lib/scss/_functions.scss +3 -0
- package/src/lib/scss/global.scss +75 -0
- package/src/lib/{variables.css → scss/variables.scss} +8 -2
- package/src/lib/url.js +7 -0
- package/src/main.js +3 -1
- package/src/routes/+layout.svelte +25 -21
- package/src/routes/+page.svelte +20 -18
- package/src/routes/components/AfterArticlePlaylinks.svelte +18 -16
- package/src/routes/components/ContextMenu.svelte +9 -9
- package/src/routes/components/Description.svelte +7 -7
- package/src/routes/components/Editorial/Alert.svelte +4 -4
- package/src/routes/components/Editorial/DragHandle.svelte +27 -26
- package/src/routes/components/Editorial/Editor.svelte +64 -48
- package/src/routes/components/Editorial/EditorItem.svelte +44 -44
- package/src/routes/components/Editorial/ManualInjection.svelte +23 -20
- package/src/routes/components/Editorial/PlaylinkTypeSelect.svelte +22 -22
- package/src/routes/components/Editorial/Search/TitleSearch.svelte +22 -22
- package/src/routes/components/Editorial/Switch.svelte +21 -20
- package/src/routes/components/Editorial/TextInput.svelte +12 -12
- package/src/routes/components/Genres.svelte +16 -11
- package/src/routes/components/Icons/IconChevron.svelte +1 -1
- package/src/routes/components/Modal.svelte +14 -16
- package/src/routes/components/Participants.svelte +11 -9
- package/src/routes/components/Playlinks.svelte +51 -45
- package/src/routes/components/Popover.svelte +18 -16
- package/src/routes/components/RoundButton.svelte +6 -6
- package/src/routes/components/SkeletonText.svelte +15 -15
- package/src/routes/components/Title.svelte +64 -59
- package/src/tests/lib/api.test.js +5 -0
- package/src/tests/lib/linkInjection.test.js +8 -5
- package/src/tests/lib/localization.test.js +67 -0
- package/src/tests/lib/meta.test.js +201 -0
- package/src/tests/lib/playlink.test.js +82 -0
- package/src/tests/lib/url.test.js +35 -0
- package/src/tests/routes/components/Editorial/Editor.test.js +8 -2
- package/src/tests/routes/components/Editorial/ManualInjection.test.js +51 -0
- package/src/typedefs.js +9 -1
- package/svelte.config.js +9 -0
- package/src/lib/global.css +0 -69
package/src/lib/api.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { authorize, getAuthToken, isEditorialModeEnabled } from './auth'
|
|
2
2
|
import { apiBaseUrl } from './constants'
|
|
3
3
|
import { stringToHash } from './hash'
|
|
4
|
+
import { getPageMetaData } from './meta'
|
|
5
|
+
import { getFullUrlPath } from './url'
|
|
4
6
|
|
|
5
7
|
/** @type {NodeJS.Timeout | number | null} */
|
|
6
8
|
let pollTimeout = null
|
|
@@ -19,6 +21,7 @@ export async function fetchLinkInjections(url, html, { hash = stringToHash(html)
|
|
|
19
21
|
const headers = new Headers({ 'Content-Type': 'application/json' })
|
|
20
22
|
const apiToken = getApiToken()
|
|
21
23
|
const isEditorialMode = isEditorialModeEnabled() ? await authorize() : false
|
|
24
|
+
const metadata = getPageMetaData()
|
|
22
25
|
|
|
23
26
|
if (!apiToken) throw new Error('No token was provided')
|
|
24
27
|
|
|
@@ -28,6 +31,7 @@ export async function fetchLinkInjections(url, html, { hash = stringToHash(html)
|
|
|
28
31
|
body: JSON.stringify(({
|
|
29
32
|
hash,
|
|
30
33
|
url,
|
|
34
|
+
metadata,
|
|
31
35
|
page_text: html,
|
|
32
36
|
...params,
|
|
33
37
|
})),
|
|
@@ -119,7 +123,7 @@ export async function saveLinkInjections(linkInjections, html) {
|
|
|
119
123
|
inactive: !!linkInjection.inactive,
|
|
120
124
|
}))
|
|
121
125
|
|
|
122
|
-
const response = await fetchLinkInjections(
|
|
126
|
+
const response = await fetchLinkInjections(getFullUrlPath(), html, {
|
|
123
127
|
params: {
|
|
124
128
|
private_token: getAuthToken(),
|
|
125
129
|
automation_enabled: false,
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { Language } from '$lib/enums/Language.js'
|
|
2
|
+
|
|
3
|
+
export const translations = {
|
|
4
|
+
'Where To Stream Online': {
|
|
5
|
+
[Language.English]: 'Where to stream online',
|
|
6
|
+
[Language.Swedish]: 'Var kan man streama online',
|
|
7
|
+
},
|
|
8
|
+
'Affiliate Disclaimer': {
|
|
9
|
+
[Language.English]: 'We may earn a commission if you make a purchase through these links. In collaboration with',
|
|
10
|
+
[Language.Swedish]: 'Vi kan få provision om du gör ett köp via dessa länkar. I samarbete med',
|
|
11
|
+
},
|
|
12
|
+
'An Error Occurred': {
|
|
13
|
+
[Language.English]: 'An error occurred',
|
|
14
|
+
[Language.Swedish]: 'Ett fel inträffade',
|
|
15
|
+
},
|
|
16
|
+
'Title Unavailable': {
|
|
17
|
+
[Language.English]: 'This title is not currently available to stream.',
|
|
18
|
+
[Language.Swedish]: 'Den här titeln går inte att streama just nu.',
|
|
19
|
+
},
|
|
20
|
+
'Title Unavailable Suffix': {
|
|
21
|
+
[Language.English]: 'is not currently available to stream.',
|
|
22
|
+
[Language.Swedish]: 'går inte att streama just nu.',
|
|
23
|
+
},
|
|
24
|
+
'Is Available To Stream On': {
|
|
25
|
+
[Language.English]: 'is available to stream on',
|
|
26
|
+
[Language.Swedish]: 'finns att streama på',
|
|
27
|
+
},
|
|
28
|
+
'Is Available To Stream': {
|
|
29
|
+
[Language.English]: 'is available to stream.',
|
|
30
|
+
[Language.Swedish]: 'finns att streama.',
|
|
31
|
+
},
|
|
32
|
+
'View Streaming Options': {
|
|
33
|
+
[Language.English]: 'View streaming options',
|
|
34
|
+
[Language.Swedish]: 'Se streamingalternativ',
|
|
35
|
+
},
|
|
36
|
+
'And': {
|
|
37
|
+
[Language.English]: 'and',
|
|
38
|
+
[Language.Swedish]: 'och',
|
|
39
|
+
},
|
|
40
|
+
'Minutes': {
|
|
41
|
+
[Language.English]: 'minutes',
|
|
42
|
+
[Language.Swedish]: 'minuter',
|
|
43
|
+
},
|
|
44
|
+
'Type: movie': {
|
|
45
|
+
[Language.English]: 'Movie',
|
|
46
|
+
[Language.Swedish]: 'Film',
|
|
47
|
+
},
|
|
48
|
+
'Type: series': {
|
|
49
|
+
[Language.English]: 'Series',
|
|
50
|
+
[Language.Swedish]: 'Serie',
|
|
51
|
+
},
|
|
52
|
+
'Stream': {
|
|
53
|
+
[Language.English]: 'Stream',
|
|
54
|
+
[Language.Swedish]: 'Strömma',
|
|
55
|
+
},
|
|
56
|
+
'Buy': {
|
|
57
|
+
[Language.English]: 'Buy',
|
|
58
|
+
[Language.Swedish]: 'Köp',
|
|
59
|
+
},
|
|
60
|
+
'Rent': {
|
|
61
|
+
[Language.English]: 'Rent',
|
|
62
|
+
[Language.Swedish]: 'Hyra',
|
|
63
|
+
},
|
|
64
|
+
'Rent Or Buy': {
|
|
65
|
+
[Language.English]: 'Rent or Buy',
|
|
66
|
+
[Language.Swedish]: 'Hyra',
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
// Genres
|
|
70
|
+
'All': {
|
|
71
|
+
[Language.English]: 'All',
|
|
72
|
+
[Language.Swedish]: 'Alla',
|
|
73
|
+
},
|
|
74
|
+
'Unscripted': {
|
|
75
|
+
[Language.English]: 'Unscripted',
|
|
76
|
+
[Language.Swedish]: 'Oscripterat',
|
|
77
|
+
},
|
|
78
|
+
'Independent': {
|
|
79
|
+
[Language.English]: 'Independent',
|
|
80
|
+
[Language.Swedish]: 'Indie',
|
|
81
|
+
},
|
|
82
|
+
'Game Show': {
|
|
83
|
+
[Language.English]: 'Game Show',
|
|
84
|
+
[Language.Swedish]: 'Gameshow',
|
|
85
|
+
},
|
|
86
|
+
'Film Noir': {
|
|
87
|
+
[Language.English]: 'Film Noir',
|
|
88
|
+
[Language.Swedish]: 'Film noir',
|
|
89
|
+
},
|
|
90
|
+
'Entertainment': {
|
|
91
|
+
[Language.English]: 'Entertainment',
|
|
92
|
+
[Language.Swedish]: 'Underhållning',
|
|
93
|
+
},
|
|
94
|
+
'News': {
|
|
95
|
+
[Language.English]: 'News',
|
|
96
|
+
[Language.Swedish]: 'Nyheter',
|
|
97
|
+
},
|
|
98
|
+
'Short': {
|
|
99
|
+
[Language.English]: 'Short',
|
|
100
|
+
[Language.Swedish]: 'Kortfilm',
|
|
101
|
+
},
|
|
102
|
+
'Bollywood': {
|
|
103
|
+
[Language.English]: 'Bollywood',
|
|
104
|
+
[Language.Swedish]: 'Bollywood',
|
|
105
|
+
},
|
|
106
|
+
'Concert': {
|
|
107
|
+
[Language.English]: 'Concert',
|
|
108
|
+
[Language.Swedish]: 'Konsert',
|
|
109
|
+
},
|
|
110
|
+
'Arthouse': {
|
|
111
|
+
[Language.English]: 'Arthouse',
|
|
112
|
+
[Language.Swedish]: 'Arthouse',
|
|
113
|
+
},
|
|
114
|
+
'Alternate Version': {
|
|
115
|
+
[Language.English]: 'Alternate Version',
|
|
116
|
+
[Language.Swedish]: 'Alternativ version',
|
|
117
|
+
},
|
|
118
|
+
'Reality TV': {
|
|
119
|
+
[Language.English]: 'Reality TV',
|
|
120
|
+
[Language.Swedish]: 'Reality-TV',
|
|
121
|
+
},
|
|
122
|
+
'Culture': {
|
|
123
|
+
[Language.English]: 'Culture',
|
|
124
|
+
[Language.Swedish]: 'Kultur',
|
|
125
|
+
},
|
|
126
|
+
'Drama': {
|
|
127
|
+
[Language.English]: 'Drama',
|
|
128
|
+
[Language.Swedish]: 'Drama',
|
|
129
|
+
},
|
|
130
|
+
'Crime': {
|
|
131
|
+
[Language.English]: 'Crime',
|
|
132
|
+
[Language.Swedish]: 'Kriminal',
|
|
133
|
+
},
|
|
134
|
+
'Western': {
|
|
135
|
+
[Language.English]: 'Western',
|
|
136
|
+
[Language.Swedish]: 'Western',
|
|
137
|
+
},
|
|
138
|
+
'Thriller': {
|
|
139
|
+
[Language.English]: 'Thriller',
|
|
140
|
+
[Language.Swedish]: 'Thriller',
|
|
141
|
+
},
|
|
142
|
+
'Animation': {
|
|
143
|
+
[Language.English]: 'Animation',
|
|
144
|
+
[Language.Swedish]: 'Animerat',
|
|
145
|
+
},
|
|
146
|
+
'Mystery': {
|
|
147
|
+
[Language.English]: 'Mystery',
|
|
148
|
+
[Language.Swedish]: 'Mysterium',
|
|
149
|
+
},
|
|
150
|
+
'Science': {
|
|
151
|
+
[Language.English]: 'Science',
|
|
152
|
+
[Language.Swedish]: 'Vetenskap',
|
|
153
|
+
},
|
|
154
|
+
'Nature': {
|
|
155
|
+
[Language.English]: 'Nature',
|
|
156
|
+
[Language.Swedish]: 'Natur',
|
|
157
|
+
},
|
|
158
|
+
'War': {
|
|
159
|
+
[Language.English]: 'War',
|
|
160
|
+
[Language.Swedish]: 'Krig',
|
|
161
|
+
},
|
|
162
|
+
'Sci-Fi': {
|
|
163
|
+
[Language.English]: 'Sci-Fi',
|
|
164
|
+
[Language.Swedish]: 'Sci-fi',
|
|
165
|
+
},
|
|
166
|
+
'Documentary': {
|
|
167
|
+
[Language.English]: 'Documentary',
|
|
168
|
+
[Language.Swedish]: 'Dokumentär',
|
|
169
|
+
},
|
|
170
|
+
'Stand-up': {
|
|
171
|
+
[Language.English]: 'Stand-up',
|
|
172
|
+
[Language.Swedish]: 'Standup',
|
|
173
|
+
},
|
|
174
|
+
'Talk Show': {
|
|
175
|
+
[Language.English]: 'Talk Show',
|
|
176
|
+
[Language.Swedish]: 'Talkshow',
|
|
177
|
+
},
|
|
178
|
+
'Fantasy': {
|
|
179
|
+
[Language.English]: 'Fantasy',
|
|
180
|
+
[Language.Swedish]: 'Fantasy',
|
|
181
|
+
},
|
|
182
|
+
'History': {
|
|
183
|
+
[Language.English]: 'History',
|
|
184
|
+
[Language.Swedish]: 'Historia',
|
|
185
|
+
},
|
|
186
|
+
'Adventure': {
|
|
187
|
+
[Language.English]: 'Adventure',
|
|
188
|
+
[Language.Swedish]: 'Äventyr',
|
|
189
|
+
},
|
|
190
|
+
'Action': {
|
|
191
|
+
[Language.English]: 'Action',
|
|
192
|
+
[Language.Swedish]: 'Action',
|
|
193
|
+
},
|
|
194
|
+
'Horror': {
|
|
195
|
+
[Language.English]: 'Horror',
|
|
196
|
+
[Language.Swedish]: 'Skräck',
|
|
197
|
+
},
|
|
198
|
+
'Comedy': {
|
|
199
|
+
[Language.English]: 'Comedy',
|
|
200
|
+
[Language.Swedish]: 'Komedi',
|
|
201
|
+
},
|
|
202
|
+
'Biography': {
|
|
203
|
+
[Language.English]: 'Biography',
|
|
204
|
+
[Language.Swedish]: 'Biografi',
|
|
205
|
+
},
|
|
206
|
+
'Music': {
|
|
207
|
+
[Language.English]: 'Music',
|
|
208
|
+
[Language.Swedish]: 'Musik',
|
|
209
|
+
},
|
|
210
|
+
'Sport': {
|
|
211
|
+
[Language.English]: 'Sport',
|
|
212
|
+
[Language.Swedish]: 'Sport',
|
|
213
|
+
},
|
|
214
|
+
'Romance': {
|
|
215
|
+
[Language.English]: 'Romance',
|
|
216
|
+
[Language.Swedish]: 'Romantik',
|
|
217
|
+
},
|
|
218
|
+
'Kids': {
|
|
219
|
+
[Language.English]: 'Kids',
|
|
220
|
+
[Language.Swedish]: 'Barn',
|
|
221
|
+
},
|
|
222
|
+
'Lifestyle': {
|
|
223
|
+
[Language.English]: 'Lifestyle',
|
|
224
|
+
[Language.Swedish]: 'Livsstil',
|
|
225
|
+
},
|
|
226
|
+
'Musical': {
|
|
227
|
+
[Language.English]: 'Musical',
|
|
228
|
+
[Language.Swedish]: 'Musikal',
|
|
229
|
+
},
|
|
230
|
+
'Anime': {
|
|
231
|
+
[Language.English]: 'Anime',
|
|
232
|
+
[Language.Swedish]: 'Anime',
|
|
233
|
+
},
|
|
234
|
+
'Family': {
|
|
235
|
+
[Language.English]: 'Family',
|
|
236
|
+
[Language.Swedish]: 'Familj',
|
|
237
|
+
},
|
|
238
|
+
}
|
package/src/lib/linkInjection.js
CHANGED
|
@@ -18,10 +18,10 @@ let afterArticlePlaylinkInsertedComponent = null
|
|
|
18
18
|
export function getLinkInjectionElements(parentElement) {
|
|
19
19
|
let validElements = []
|
|
20
20
|
|
|
21
|
-
let remainingChildren = [
|
|
21
|
+
let remainingChildren = [parentElement]
|
|
22
22
|
|
|
23
23
|
while (remainingChildren.length > 0) {
|
|
24
|
-
const element = /** @type {HTMLElement} */ (remainingChildren.
|
|
24
|
+
const element = /** @type {HTMLElement} */ (remainingChildren.pop())
|
|
25
25
|
|
|
26
26
|
// Ignore links, buttons, and headers
|
|
27
27
|
if (/^(A|BUTTON|SCRIPT|NOSCRIPT|STYLE|IFRAME|H[1-6])$/.test(element.tagName)) continue
|
|
@@ -33,8 +33,15 @@ export function getLinkInjectionElements(parentElement) {
|
|
|
33
33
|
|
|
34
34
|
// If this element has a text node we add it to the valid elements and stop there
|
|
35
35
|
// Otherwise we add all children to be checked in this same loop.
|
|
36
|
-
if (hasTextNode)
|
|
37
|
-
|
|
36
|
+
if (hasTextNode) {
|
|
37
|
+
validElements.push(element)
|
|
38
|
+
continue
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const children = /** @type {HTMLElement[]} */ (Array.from(element.children))
|
|
42
|
+
for (let i = children.length - 1; i >= 0; i--) {
|
|
43
|
+
remainingChildren.push(children[i])
|
|
44
|
+
}
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
return validElements
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { translations } from '$lib/data/translations'
|
|
2
|
+
import { Language } from '$lib/enums/Language'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Super basic implementation to get a string for the given key and language
|
|
6
|
+
* @param {string} key Key of the wanted translation
|
|
7
|
+
* @param {LanguageCode} language Language code
|
|
8
|
+
* @returns
|
|
9
|
+
*/
|
|
10
|
+
export function t(key, language = getLanguage()) {
|
|
11
|
+
// @ts-ignore It's ok if a key is not found.
|
|
12
|
+
return translations[key]?.[language] || key
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @returns {LanguageCode}
|
|
17
|
+
*/
|
|
18
|
+
export function getLanguage() {
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
const configLanguage = window.PlayPilotLinkInjections?.language
|
|
21
|
+
const languageCodes = /** @type {string[]} */ (Object.values(Language))
|
|
22
|
+
|
|
23
|
+
if (configLanguage) {
|
|
24
|
+
if (languageCodes.includes(configLanguage)) return configLanguage
|
|
25
|
+
|
|
26
|
+
console.warn(`PlayPilot Link Injections: ${configLanguage} is not an accepted language`)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const documentLanguage = document.querySelector('html')?.getAttribute('lang')
|
|
30
|
+
|
|
31
|
+
if (documentLanguage && languageCodes.includes(documentLanguage)) {
|
|
32
|
+
return /** @type {LanguageCode} */ (documentLanguage)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return Language.English
|
|
36
|
+
}
|
package/src/lib/meta.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { getLinkInjectionsParentElement } from './linkInjection'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns meta data to be included with injections.
|
|
5
|
+
* @returns {ArticleMetaData}
|
|
6
|
+
*/
|
|
7
|
+
export function getPageMetaData() {
|
|
8
|
+
const parent = getLinkInjectionsParentElement()
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
heading: getPageHeading(parent),
|
|
12
|
+
modified_time: getPageModifiedTime(parent),
|
|
13
|
+
published_time: getPageModifiedTime(parent),
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Returns the most relevant `h1` content on the page.
|
|
19
|
+
* A page should only ever contain exactly 1 `h1` tag, but we don't control these things.
|
|
20
|
+
* @param {HTMLElement} parent
|
|
21
|
+
* @returns {string | null}
|
|
22
|
+
*/
|
|
23
|
+
export function getPageHeading(parent) {
|
|
24
|
+
const heading = parent.querySelector('h1') || document.querySelector('h1')
|
|
25
|
+
|
|
26
|
+
return heading?.innerText.trim() || null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Returns the datetime for the article, intended to be the last time the article was modified.
|
|
31
|
+
* @param {HTMLElement} parent
|
|
32
|
+
* @returns {string | null}
|
|
33
|
+
*/
|
|
34
|
+
export function getPageModifiedTime(parent) {
|
|
35
|
+
const element =
|
|
36
|
+
/** @type {HTMLElement | null} */
|
|
37
|
+
(document.querySelector('meta[property*="modified_time"]') ||
|
|
38
|
+
parent.querySelector('[itemprop="dateModified"]') ||
|
|
39
|
+
document.querySelector('[itemprop="dateModified"]') || null)
|
|
40
|
+
|
|
41
|
+
const datetime = element?.getAttribute('content') || element?.getAttribute('datetime') || element?.innerText
|
|
42
|
+
|
|
43
|
+
if (!datetime) return null
|
|
44
|
+
|
|
45
|
+
return getDatetime(datetime)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Returns the datetime for the article, intended to be the date the article was first published
|
|
50
|
+
* @param {HTMLElement} parent
|
|
51
|
+
* @returns {string | null}
|
|
52
|
+
*/
|
|
53
|
+
export function getPagePublishedTime(parent) {
|
|
54
|
+
const element =
|
|
55
|
+
/** @type {HTMLElement | null} */
|
|
56
|
+
(document.querySelector('meta[property*="published_time"]') ||
|
|
57
|
+
parent.querySelector('[itemprop="datePublished"]') ||
|
|
58
|
+
document.querySelector('[itemprop="datePublished"]') ||
|
|
59
|
+
parent.querySelector('time') ||
|
|
60
|
+
document.querySelector('time') ||
|
|
61
|
+
parent.querySelector('[datetime]') ||
|
|
62
|
+
document.querySelector('[datetime]') || null)
|
|
63
|
+
|
|
64
|
+
const datetime = element?.getAttribute('content') || element?.getAttribute('datetime') || element?.innerText
|
|
65
|
+
|
|
66
|
+
if (!datetime) return null
|
|
67
|
+
|
|
68
|
+
return getDatetime(datetime)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get datetime string as ISO datetime string
|
|
73
|
+
* @param {string} datetime
|
|
74
|
+
* @returns {string | null}
|
|
75
|
+
*/
|
|
76
|
+
function getDatetime(datetime) {
|
|
77
|
+
try {
|
|
78
|
+
return new Date(datetime).toISOString()
|
|
79
|
+
} catch {
|
|
80
|
+
return null
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge playlinks of the same provider of BUY and RENT categories into a shared TVOD category.
|
|
3
|
+
* @param {PlaylinkData[]} playlinks
|
|
4
|
+
* @returns {PlaylinkData[]}
|
|
5
|
+
*/
|
|
6
|
+
export function mergePlaylinks(playlinks) {
|
|
7
|
+
/** @type {PlaylinkData[]} */
|
|
8
|
+
let merged = []
|
|
9
|
+
|
|
10
|
+
for (const playlink of playlinks) {
|
|
11
|
+
let newPlaylink = playlink
|
|
12
|
+
const existingPlaylink = merged.find(p => p.name === newPlaylink.name)
|
|
13
|
+
|
|
14
|
+
const category1 = existingPlaylink?.extra_info.category
|
|
15
|
+
const category2 = newPlaylink.extra_info.category
|
|
16
|
+
|
|
17
|
+
// If a playlink for the same provider already exists in a BUY or RENT category, we remove it from
|
|
18
|
+
// the array and re-add it as a new entry with the TVOD category
|
|
19
|
+
if ((category1 === 'RENT' && category2 === 'BUY') || (category1 === 'BUY' && category2 === 'RENT')) {
|
|
20
|
+
merged = merged.filter(p => p.name !== newPlaylink.name)
|
|
21
|
+
newPlaylink = { ...newPlaylink, extra_info: { ...newPlaylink.extra_info, category: 'TVOD' } }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
merged.push(newPlaylink)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return merged
|
|
28
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
@use "./_functions" as *;
|
|
2
|
+
|
|
3
|
+
[data-playpilot-injection-key] {
|
|
4
|
+
position: relative;
|
|
5
|
+
|
|
6
|
+
&.injection-highlight {
|
|
7
|
+
outline: margin(0.25) solid var(--playpilot-primary) !important;
|
|
8
|
+
outline-offset: margin(0.5) !important;
|
|
9
|
+
border-radius: margin(0.05);
|
|
10
|
+
scroll-margin: margin(5);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.playpilot-styled-scrollbar {
|
|
15
|
+
scrollbar-color: var(--playpilot-content-light) var(--playpilot-lighter);
|
|
16
|
+
scrollbar-width: thin;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.playpilot-styled-scrollbed {
|
|
20
|
+
&::-webkit-scrollbar {
|
|
21
|
+
width: margin(0.75);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&::-webkit-scrollbar-track {
|
|
25
|
+
background: var(--playpilot-light);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&::-webkit-scrollbar-thumb {
|
|
29
|
+
border: 2px solid var(--playpilot-light);
|
|
30
|
+
border-radius: margin(1);
|
|
31
|
+
background: var(--playpilot-lighter);
|
|
32
|
+
|
|
33
|
+
&:hover {
|
|
34
|
+
background: var(--playpilot-content-light);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
&:active {
|
|
38
|
+
background: var(--playpilot-text-color-alt);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/*
|
|
44
|
+
Styling for individual CSS variables, with a selector for each variable.
|
|
45
|
+
This is very verbose, but necessary in order to only use the CSS variable when they are actually set.
|
|
46
|
+
*/
|
|
47
|
+
[data-playpilot-injection-key] {
|
|
48
|
+
&[data-used-css-variables*="--playpilot-injection-font-weight"] {
|
|
49
|
+
font-weight: var(--playpilot-injection-font-weight);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
&[data-used-css-variables*="--playpilot-injection-text-color"] {
|
|
53
|
+
color: var(--playpilot-injection-text-color);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
&[data-used-css-variables*="--playpilot-injection-text-color-hover"]:hover {
|
|
57
|
+
color: var(--playpilot-injection-text-color-hover);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
&[data-used-css-variables*="--playpilot-injection-text-decoration"] {
|
|
61
|
+
text-decoration: var(--playpilot-injection-text-decoration);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
&[data-used-css-variables*="--playpilot-injection-text-decoration-hover"]:hover {
|
|
65
|
+
text-decoration: var(--playpilot-injection-text-decoration-hover);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
&[data-used-css-variables*="--playpilot-injection-background-color"] {
|
|
69
|
+
background-color: var(--playpilot-injection-background-color);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
&[data-used-css-variables*="--playpilot-injection-background-color-hover"]:hover {
|
|
73
|
+
background-color: var(--playpilot-injection-background-color-hover);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
@use "./_functions" as *;
|
|
2
|
+
|
|
1
3
|
:root {
|
|
2
4
|
--playpilot-primary: #fa548a;
|
|
3
5
|
--playpilot-dark: #101426;
|
|
@@ -9,8 +11,12 @@
|
|
|
9
11
|
--playpilot-font-family: "Poppins", sans-serif;
|
|
10
12
|
--playpilot-text-color: #fff;
|
|
11
13
|
--playpilot-text-color-alt: #c8d4de;
|
|
12
|
-
--playpilot-shadow:
|
|
13
|
-
|
|
14
|
+
--playpilot-shadow:
|
|
15
|
+
#{margin(0.2) margin(0.15) margin(0.15)} rgba(0, 0, 0, 0.2);
|
|
16
|
+
--playpilot-shadow-large:
|
|
17
|
+
#{margin(0.15) margin(0.15) margin(0.2)} rgba(0, 0, 0, 0.05),
|
|
18
|
+
#{margin(0.2) margin(0.35) margin(0.35)} rgba(0, 0, 0, 0.1),
|
|
19
|
+
#{margin(0.1) margin(0.1) margin(0.75)} rgba(0, 0, 0, 0.25);
|
|
14
20
|
--playpilot-error: #ea5a5a;
|
|
15
21
|
--playpilot-error-dark: #442533;
|
|
16
22
|
}
|
package/src/lib/url.js
ADDED
package/src/main.js
CHANGED
|
@@ -8,9 +8,10 @@ window.PlayPilotLinkInjections = {
|
|
|
8
8
|
token: '',
|
|
9
9
|
editorial_token: '',
|
|
10
10
|
selector: '',
|
|
11
|
+
language: null,
|
|
11
12
|
app: null,
|
|
12
13
|
|
|
13
|
-
initialize(config = { token: '', selector: '', editorial_token: '' }) {
|
|
14
|
+
initialize(config = { token: '', selector: '', language: null, editorial_token: '' }) {
|
|
14
15
|
if (!config.token) {
|
|
15
16
|
console.error('An API token is required.')
|
|
16
17
|
return
|
|
@@ -19,6 +20,7 @@ window.PlayPilotLinkInjections = {
|
|
|
19
20
|
this.token = config.token
|
|
20
21
|
this.editorial_token = config.editorial_token
|
|
21
22
|
this.selector = config.selector
|
|
23
|
+
this.language = config.language
|
|
22
24
|
|
|
23
25
|
if (this.app) this.destroy()
|
|
24
26
|
|
|
@@ -19,35 +19,39 @@
|
|
|
19
19
|
if (browser) window.PlayPilotLinkInjections = { token: 'ZoAL14yqzevMyQiwckbvyetOkeIUeEDN', selector: 'article' }
|
|
20
20
|
</script>
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
22
|
+
<div>
|
|
23
|
+
{#key Math.random()}
|
|
24
|
+
<article use:noClass>
|
|
25
|
+
<h1 use:noClass>Some heading</h1>
|
|
26
|
+
<time datetime="13:00">1 hour ago</time>
|
|
27
|
+
<p use:noClass>Following the success of John M. Chu's 2018 romantic-comedy Crazy Rich Asians, Quan was inspired to return to acting. He first scored a supporting role in the Netflix movie Finding 'Ohana, before securing a starring role in the absurdist comedy-drama Everything Everywhere all At Once. A critical and commercial success, the film earned $143 million against a budget of $14-25 million, and saw Quan win the Academy Award for Best Supporting Actor. Following his win, Quan struggled to choose projects he was satisfied with, passing on an action-comedy three times, before finally taking his first leading role in it, following advice from Spielberg.</p>
|
|
28
|
+
<p use:noClass>In an interview with Epire & Magazine, Quan reveals he quested starring in Love Hurts, which sees him in the leading role of a former assassin turned successful realtor, whose past returns when his brother attempts to hunt him down. The movie is in a similar vein to successful films such as The Long Kiss Goodnight and Nobody, and Quan discussed how he was reluctant to take the part due to his conditioned beliefs about how an action hero should look. But he reveals that he changed his mind following a meeting with Spielberg, who convinced him to do it.</p>
|
|
29
|
+
</article>
|
|
30
|
+
|
|
31
|
+
{#if browser}
|
|
32
|
+
{@render children()}
|
|
33
|
+
{/if}
|
|
34
|
+
{/key}
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<style lang="scss">
|
|
38
|
+
div :global {
|
|
36
39
|
article {
|
|
37
40
|
min-height: 100vh;
|
|
38
|
-
padding:
|
|
41
|
+
padding: margin(2);
|
|
39
42
|
background: #222;
|
|
40
43
|
color: white;
|
|
41
44
|
font-family: sans-serif;
|
|
42
45
|
line-height: 1.4em;
|
|
43
|
-
}
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
> :first-child {
|
|
48
|
+
margin-top: 0;
|
|
49
|
+
}
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
+
a {
|
|
52
|
+
color: hotpink;
|
|
53
|
+
font-style: italic;
|
|
54
|
+
}
|
|
51
55
|
}
|
|
52
56
|
}
|
|
53
57
|
</style>
|