Skip to content

Commit 1a39eae

Browse files
feature (chromecast): canary release with chromecast image
1 parent d53ac30 commit 1a39eae

File tree

8 files changed

+119
-88
lines changed

8 files changed

+119
-88
lines changed

public/assets/boot/ctrl_boot_frontoffice.js

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,17 +82,6 @@ async function setup_blue_death_screen() {
8282
};
8383
}
8484

85-
// async function setup_chromecast() {
86-
// if (!window.CONFIG["enable_chromecast"]) {
87-
// return Promise.resolve();
88-
// } else if (!("chrome" in window)) {
89-
// return Promise.resolve();
90-
// } else if (location.hostname === "localhost" || location.hostname === "127.0.0.1") {
91-
// return Promise.resolve();
92-
// }
93-
// return window.Chromecast.init();
94-
// }
95-
9685
async function setup_history() {
9786
window.history.replaceState({}, "");
9887
}

public/assets/model/chromecast.js

Lines changed: 50 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -6,76 +6,60 @@ export async function init() {
66
} else if (location.hostname === "localhost" || location.hostname === "127.0.0.1") {
77
return Promise.resolve();
88
}
9-
// return Chromecast.init();
9+
return Chromecast.init();
1010
}
1111

12-
// import { Session } from "./session";
13-
// import { currentShare, objectGet } from "../helpers/";
12+
export const Chromecast = new class ChromecastManager {
13+
init() {
14+
return new Promise((resolve) => {
15+
const script = document.createElement("script");
16+
script.src = "https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1";
17+
script.onerror = () => resolve(null);
18+
window["__onGCastApiAvailable"] = function(isAvailable) {
19+
if (isAvailable) window.cast.framework.CastContext.getInstance().setOptions({
20+
receiverApplicationId: window.chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,
21+
autoJoinPolicy: window.chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED,
22+
});
23+
resolve(null);
24+
};
25+
document.head.appendChild(script);
26+
});
27+
}
1428

15-
// class ChromecastManager {
16-
// init() {
17-
// return new Promise((done) => {
18-
// const script = document.createElement("script");
19-
// script.src = "https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1";
20-
// script.onerror = () => done()
21-
// window["__onGCastApiAvailable"] = function(isAvailable) {
22-
// if (isAvailable) cast.framework.CastContext.getInstance().setOptions({
23-
// receiverApplicationId: chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,
24-
// autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED,
25-
// });
26-
// done();
27-
// };
28-
// document.head.appendChild(script)
29-
// });
30-
// }
29+
createLink(apiPath) {
30+
const target = new URL(location.origin + apiPath);
31+
const shareID = new URLSearchParams(location.search).get("search");
32+
if (shareID) target.searchParams.append("share", shareID);
33+
return target.toString();
34+
}
3135

32-
// origin() {
33-
// return location.origin;
34-
// };
36+
createRequest(mediaInfo) {
37+
if (!window.BEARER_TOKEN) throw new Error("Invalid account");
38+
// TODO: it would be much much nicer to set the authorization from an HTTP header
39+
// but this would require to create a custom web receiver app, setup accounts on
40+
// google, etc,... Until that happens, we're setting the authorization within the
41+
// url. Once we have that app, the authorisation will come from a customData field
42+
// of a chrome.cast.media.LoadRequest
43+
const target = new URL(mediaInfo.contentId);
44+
target.searchParams.append("authorization", window.BEARER_TOKEN);
45+
mediaInfo.contentId = target.toString();
46+
return new window.chrome.cast.media.LoadRequest(mediaInfo);
47+
}
3548

36-
// createLink(apiPath) {
37-
// const shareID = currentShare();
38-
// if (shareID) {
39-
// const target = new URL(this.origin() + apiPath);
40-
// target.searchParams.append("share", shareID);
41-
// return target.toString();
42-
// }
43-
// const target = new URL(this.origin() + apiPath)
44-
// return target.toString();
45-
// }
49+
context() {
50+
if (!window.chrome?.cast?.isAvailable) return;
51+
return window.cast.framework.CastContext.getInstance();
52+
}
4653

47-
// createRequest(mediaInfo) {
48-
// let prior = Promise.resolve();
49-
// if (!Session.authorization) prior = Session.currentUser();
50-
// return prior.then(() => {
51-
// if (!Session.authorization) throw new Error("Invalid account");
52-
// // TODO: it would be much much nicer to set the authorization from an HTTP header
53-
// // but this would require to create a custom web receiver app, setup accounts on
54-
// // google, etc,... Until that happens, we're setting the authorization within the
55-
// // url. Once we have that app, the authorisation will come from a customData field
56-
// // of a chrome.cast.media.LoadRequest
57-
// const target = new URL(mediaInfo.contentId);
58-
// target.searchParams.append("authorization", Session.authorization);
59-
// mediaInfo.contentId = target.toString();
60-
// return new chrome.cast.media.LoadRequest(mediaInfo);
61-
// });
62-
// }
54+
session() {
55+
const context = this.context();
56+
if (!context) return;
57+
return context.getCurrentSession();
58+
}
6359

64-
// context() {
65-
// if (!objectGet(window.chrome, ["cast", "isAvailable"])) {
66-
// return;
67-
// }
68-
// return cast.framework.CastContext.getInstance();
69-
// }
70-
// session() {
71-
// const context = this.context();
72-
// if (!context) return;
73-
// return context.getCurrentSession();
74-
// }
75-
// media() {
76-
// const session = this.session();
77-
// if (!session) return;
78-
// return session.getMediaSession();
79-
// }
80-
// }
81-
// export const Chromecast = new ChromecastManager();
60+
media() {
61+
const session = this.session();
62+
if (!session) return;
63+
return session.getMediaSession();
64+
}
65+
}();

public/assets/model/session.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ export function getSession() {
88
method: "GET",
99
responseType: "json"
1010
}).pipe(
11-
rxjs.map(({ responseJSON }) => responseJSON.result)
11+
rxjs.map(({ responseJSON }) => responseJSON.result),
12+
rxjs.tap(({ authorization }) => {
13+
if (authorization) window.BEARER_TOKEN = authorization;
14+
}),
1215
);
1316
}
1417

public/assets/pages/connectpage/ctrl_form.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ export default async function(render) {
198198
return rxjs.of(null).pipe(
199199
rxjs.tap(() => toggleLoader(true)),
200200
rxjs.mergeMap(() => createSession(formData)),
201-
rxjs.tap(({ responseJSON, responseHeaders }) => {
201+
rxjs.tap(({ responseJSON }) => {
202202
let redirectURL = toHref("/files/");
203203
const GET = getURLParams();
204204
if (GET["next"]) redirectURL = GET["next"];

public/assets/pages/ctrl_logout.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default function(render) {
1313
effect(deleteSession().pipe(
1414
rxjs.mergeMap(setup_config),
1515
rxjs.tap(() => {
16-
window.CONFIG["logout"] ? location.href = window.CONFIG["logout"] : navigate(toHref("/"))
16+
window.CONFIG["logout"] ? location.href = window.CONFIG["logout"] : navigate(toHref("/"));
1717
}),
1818
rxjs.catchError(ctrlError(render)),
1919
));

public/assets/pages/viewerpage/application_image.js

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
import { createElement, createRender } from "../../lib/skeleton/index.js";
1+
import { createElement, createRender, onDestroy } from "../../lib/skeleton/index.js";
2+
import { toHref } from "../../lib/skeleton/router.js";
23
import rxjs, { effect, onLoad, onClick } from "../../lib/rx.js";
34
import { animate } from "../../lib/animate.js";
5+
import { extname } from "../../lib/path.js";
46
import { loadCSS } from "../../helpers/loader.js";
57
import { qs } from "../../lib/dom.js";
68
import { createLoader } from "../../components/loader.js";
9+
import notification from "../../components/notification.js";
710
import t from "../../locales/index.js";
811
import ctrlError from "../ctrl_error.js";
12+
import { Chromecast } from "../../model/chromecast.js";
913

1014
import { transition, getFilename, getDownloadUrl } from "./common.js";
1115

@@ -44,6 +48,7 @@ export default function(render) {
4448
buttonDownload(getFilename(), getDownloadUrl()),
4549
buttonFullscreen(qs($page, ".component_image_container")),
4650
buttonInfo({ toggle: toggleInfo }),
51+
buttonChromecast(getFilename(), getDownloadUrl()),
4752
);
4853

4954
effect(onLoad($photo).pipe(
@@ -85,6 +90,13 @@ export default function(render) {
8590
componentPager(createRender(qs($page, ".component_pager")));
8691
}
8792

93+
export function init() {
94+
return Promise.all([
95+
loadCSS(import.meta.url, "./application_image.css"),
96+
initPager(), initMetadata(),
97+
]);
98+
}
99+
88100
function buttonInfo({ toggle }) {
89101
const $el = createElement(`
90102
<span>
@@ -98,9 +110,51 @@ function buttonInfo({ toggle }) {
98110
return $el;
99111
}
100112

101-
export function init() {
102-
return Promise.all([
103-
loadCSS(import.meta.url, "./application_image.css"),
104-
initPager(), initMetadata(),
105-
]);
113+
function buttonChromecast(filename, downloadURL) {
114+
const context = Chromecast.context();
115+
if (!context) return;
116+
117+
const chromecastSetup = (event) => {
118+
switch (event.sessionState) {
119+
case window.cast.framework.SessionState.SESSION_STARTED:
120+
chromecastLoader();
121+
break;
122+
}
123+
};
124+
const chromecastLoader = () => {
125+
const session = Chromecast.session();
126+
if (!session) return;
127+
128+
const link = Chromecast.createLink("/" + toHref(downloadURL));
129+
const media = new window.chrome.cast.media.MediaInfo(
130+
link,
131+
window.CONFIG.mime[extname(filename)],
132+
);
133+
media.metadata = new window.chrome.cast.media.PhotoMediaMetadata();
134+
media.metadata.title = filename;
135+
media.metadata.images = [
136+
new window.chrome.cast.Image(location.origin + "/" + toHref("/assets/icons/photo.png")),
137+
];
138+
try {
139+
const req = Chromecast.createRequest(media);
140+
session.loadMedia(req);
141+
} catch (err) {
142+
console.error(err);
143+
notification.error(t("Cannot establish a connection"));
144+
}
145+
};
146+
147+
context.addEventListener(
148+
window.cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
149+
chromecastSetup,
150+
);
151+
onDestroy(() => context.removeEventListener(
152+
window.cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
153+
chromecastSetup,
154+
));
155+
156+
const media = Chromecast.media();
157+
if (media && media.media && media.media.mediaCategory === "IMAGE") chromecastLoader();
158+
159+
return document.createElement("google-cast-launcher");
106160
}

public/assets/pages/viewerpage/application_map.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ export default async function(render) {
3030

3131
const map = window.L.map("map");
3232

33-
3433
const fileview = [getFilename()];
3534
for (let i=0; i<fileview.length; i++) {
3635
await cat(fileview[i]).pipe(rxjs.mergeMap(async(content) => {

public/global.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
interface Window {
2-
chrome: object;
2+
chrome: any;
3+
cast: any;
34
overrides: {
45
[key: string]: any;
56
"xdg-open"?: (mime: string) => void;
67
};
78
CONFIG: Config;
9+
BEARER_TOKEN?: string;
810
}
911

1012
interface Config {

0 commit comments

Comments
 (0)