diff --git a/client/index.js b/client/index.js
index 1772e99b..cea0775f 100644
--- a/client/index.js
+++ b/client/index.js
@@ -2,7 +2,7 @@ import React from "react";
 import ReactDOM from "react-dom";
 import Router from "./router";
 
-import { Config, Log } from "./model/";
+import { Config, Log, Chromecast } from "./model/";
 import { http_get, setup_cache } from "./helpers/";
 import load from "little-loader";
 
@@ -150,17 +150,5 @@ function setup_chromecast() {
 	} else if (location.hostname === "localhost" || location.hostname === "127.0.0.1") {
         return Promise.resolve();
     }
-    return new Promise((done) => {
-        const script = document.createElement("script");
-        script.src = "https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1";
-        script.onerror = () => done()
-        window["__onGCastApiAvailable"] = function(isAvailable) {
-            if (isAvailable) cast.framework.CastContext.getInstance().setOptions({
-                receiverApplicationId: chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,
-                autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED,
-            });
-            done();
-        };
-        document.head.appendChild(script)
-    });
+    return Chromecast.init();
 }
diff --git a/client/model/chromecast.js b/client/model/chromecast.js
new file mode 100644
index 00000000..0147f665
--- /dev/null
+++ b/client/model/chromecast.js
@@ -0,0 +1,73 @@
+"use strict";
+
+import { Session } from "./session";
+import { currentShare, objectGet } from "../helpers/";
+
+class ChromecastManager {
+    init() {
+        return new Promise((done) => {
+            const script = document.createElement("script");
+            script.src = "https://www.gstatic.com/cv/js/sender/v1/cast_sender.js?loadCastFramework=1";
+            script.onerror = () => done()
+            window["__onGCastApiAvailable"] = function(isAvailable) {
+                if (isAvailable) cast.framework.CastContext.getInstance().setOptions({
+                    receiverApplicationId: chrome.cast.media.DEFAULT_MEDIA_RECEIVER_APP_ID,
+                    autoJoinPolicy: chrome.cast.AutoJoinPolicy.ORIGIN_SCOPED,
+                });
+                done();
+            };
+            document.head.appendChild(script)
+        });
+    }
+
+    origin() {
+        return location.origin;
+    };
+
+    createLink(apiPath) {
+        const shareID = currentShare();
+        if (shareID) {
+            const target = new URL(this.origin() + apiPath);
+            target.searchParams.append("share", shareID);
+            return target.toString();
+        }
+        const target = new URL(this.origin() + apiPath)
+        return target.toString();
+    }
+
+    createRequest(mediaInfo) {
+        let prior = Promise.resolve();
+        if (!Session.authorization) prior = Session.currentUser();
+        return prior.then(() => {
+            if (!Session.authorization) throw new Error("Invalid account");
+            // TODO: it would be much much nicer to set the authorization from an HTTP header
+            // but this would require to create a custom web receiver app, setup accounts on
+            // google, etc,... Until that happens, we're setting the authorization within the
+            // url. Once we have that app, the authorisation will come from a customData field
+            // of a chrome.cast.media.LoadRequest
+            const target = new URL(mediaInfo.contentId);
+            target.searchParams.append("authorization", Session.authorization);
+            mediaInfo.contentId = target.toString();
+            return new chrome.cast.media.LoadRequest(mediaInfo);
+        });
+    }
+
+    context() {
+        if (!objectGet(window.chrome, ["cast", "isAvailable"])) {
+            return;
+        }
+        return cast.framework.CastContext.getInstance();
+    }
+    session() {
+        const context = this.context();
+        if (!context) return;
+        return context.getCurrentSession();
+    }
+    media() {
+        const session = this.session();
+        if (!session) return;
+        return session.getMediaSession();
+    }
+}
+
+export const Chromecast = new ChromecastManager();
diff --git a/client/model/index.js b/client/model/index.js
index e298d3b5..8ffc4cdf 100644
--- a/client/model/index.js
+++ b/client/model/index.js
@@ -6,3 +6,4 @@ export { Log } from "./log";
 export { Admin } from "./admin";
 export { Audit } from "./audit";
 export { Tags } from "./tags";
+export { Chromecast } from "./chromecast";
diff --git a/client/model/session.js b/client/model/session.js
index acf8c8be..97d44b7b 100644
--- a/client/model/session.js
+++ b/client/model/session.js
@@ -4,7 +4,10 @@ class SessionManager {
     currentUser() {
         const shareID = currentShare();
         return http_get("/api/session" + (shareID && `?share=${shareID}`))
-            .then((data) => data.result)
+            .then((data) => {
+                this.authorization = data.result.authorization;
+                return data.result;
+            })
             .catch((err) => {
                 if (err.code === "Unauthorized") {
                     if (location.pathname.indexOf("/files/") !== -1 || location.pathname.indexOf("/view/") !== -1) {
@@ -41,6 +44,7 @@ class SessionManager {
 
     logout() {
         const url = "/api/session";
+        this.authorization = null;
         return http_delete(url)
             .then((data) => data.result);
     }
diff --git a/client/pages/viewerpage.js b/client/pages/viewerpage.js
index 8c4462d1..5e1949d3 100644
--- a/client/pages/viewerpage.js
+++ b/client/pages/viewerpage.js
@@ -4,7 +4,7 @@ import Path from "path";
 
 import "./viewerpage.scss";
 import "./error.scss";
-import { Files } from "../model/";
+import { Files, Chromecast } from "../model/";
 import {
     BreadCrumb, Bundle, NgIf, Loader, EventReceiver, LoggedInOnly, ErrorPage,
 } from "../components/";
@@ -137,12 +137,11 @@ export function ViewerPageComponent({ error, subscribe, unsubscribe, match, loca
 
     useEffect(() => {
         return () => {
-            if (!objectGet(window.chrome, ["cast", "isAvailable"])) {
-                return
-            }
-            cast.framework.CastContext.getInstance().endCurrentSession();
+            const context = Chromecast.context();
+            if (!context) return;
+            context.endCurrentSession();
         };
-    }, [])
+    }, []);
 
     return (
         
diff --git a/client/pages/viewerpage/audioplayer.js b/client/pages/viewerpage/audioplayer.js
index d1507824..afd8a8bd 100644
--- a/client/pages/viewerpage/audioplayer.js
+++ b/client/pages/viewerpage/audioplayer.js
@@ -1,9 +1,12 @@
 import React, { useState, useEffect, useRef } from "react";
 import WaveSurfer from "wavesurfer.js";
+import filepath from "path";
 
 import { MenuBar } from "./menubar";
 import { NgIf, Icon } from "../../components/";
-import { settings_get, settings_put } from "../../helpers/";
+import { settings_get, settings_put, notify, getMimeType, basename } from "../../helpers/";
+import { Chromecast } from "../../model/";
+import { t } from "../../locales/";
 import "./audioplayer.scss";
 
 export function AudioPlayer({ filename, data }) {
@@ -11,6 +14,7 @@ export function AudioPlayer({ filename, data }) {
     const [isLoading, setIsLoading] = useState(true);
     const [purcentLoading, setPurcentLoading] = useState(0);
     const [volume, setVolume] = useState(settings_get("volume") === null ? 50 : settings_get("volume"));
+    const [isChromecast, setIsChromecast] = useState(false);
     const [error, setError] = useState(null);
     const wavesurfer = useRef(null);
 
@@ -29,13 +33,14 @@ export function AudioPlayer({ filename, data }) {
         let $currentTime = document.getElementById("currentTime");
         let $totalDuration = document.getElementById("totalDuration");
         wavesurfer.current.on("ready", () => {
+            setPurcentLoading(100);
             setIsLoading(false);
             wavesurfer.current.setVolume(volume / 100);
             $totalDuration.innerHTML = formatTimecode(wavesurfer.current.getDuration());
         });
         wavesurfer.current.on("audioprocess", () => {
             $currentTime.innerHTML = formatTimecode(wavesurfer.current.getCurrentTime());
-        })
+        });
         wavesurfer.current.on("loading", (n) => {
             setPurcentLoading(n);
         });
@@ -43,15 +48,113 @@ export function AudioPlayer({ filename, data }) {
             setIsLoading(false);
             setError(err);
         });
+        wavesurfer.current.on("seek", (s) => {
+            const media = Chromecast.media();
+            if (!media) return;
+            const seekRequest = new chrome.cast.media.SeekRequest();
+            seekRequest.currentTime = parseInt(s*wavesurfer.current.getDuration());
+            media.seek(seekRequest);
+        });
+
         return () => wavesurfer.current.destroy();
     }, []);
 
     useEffect(() => {
-        if(wavesurfer.current === null) return;
         window.addEventListener("keypress", onKeyPressHandler);
         return () => window.removeEventListener("keypress", onKeyPressHandler);
     }, [isPlaying])
 
+    const chromecastSetup = (event) => {
+        switch (event.sessionState) {
+        case cast.framework.SessionState.SESSION_STARTING:
+            setIsChromecast(true);
+            setIsLoading(true);
+            break;
+        case cast.framework.SessionState.SESSION_START_FAILED:
+            notify.send(t("Cannot establish a connection"), "error");
+            setIsChromecast(false);
+            setIsLoading(false);
+            break;
+        case cast.framework.SessionState.SESSION_STARTED:
+            chromecastHandler()
+            break;
+        case cast.framework.SessionState.SESSION_ENDING:
+            wavesurfer.current.setMute(false);
+            setIsChromecast(false);
+            break;
+        }
+    };
+
+    const chromecastHandler = () => {
+        setIsLoading(true);
+        const link = Chromecast.createLink(data);
+        const media = new chrome.cast.media.MediaInfo(
+            link,
+            getMimeType(data),
+        );
+        media.metadata = new chrome.cast.media.MusicTrackMediaMetadata()
+        media.metadata.title = filename.substr(0, filename.lastIndexOf(filepath.extname(filename)));
+        media.metadata.subtitle = CONFIG.name;
+        media.metadata.albumName = CONFIG.name;
+        media.metadata.images = [
+            new chrome.cast.Image(origin + "/assets/icons/music.png"),
+        ];
+        const session = Chromecast.session();
+        if (!session) return;
+
+        Chromecast.createRequest(media)
+            .then((req) => {
+                req.currentTime = parseInt(wavesurfer.current.getCurrentTime());
+                return session.loadMedia(req)
+            })
+            .then(() => {
+                setIsPlaying(true);
+                setIsLoading(false);
+                wavesurfer.current.play();
+                wavesurfer.current.setMute(true);
+
+                const media = Chromecast.media();
+                if (!media) return;
+                wavesurfer.current.seekTo(media.getEstimatedTime() / wavesurfer.current.getDuration());
+                media.addUpdateListener(chromecastAlive);
+            }).catch((err) => {
+                console.error(err);
+                notify.send(t("Cannot establish a connection"), "error");
+                setIsChromecast(false);
+                setIsLoading(false);
+            });
+    }
+
+    const chromecastAlive = (isAlive) => {
+        if (isAlive) return;
+        const session = Chromecast.session();
+        if (session) {
+            session.endSession();
+            wavesurfer.current.setMute(false);
+        }
+    };
+
+    useEffect(() => {
+        const context = Chromecast.context();
+        if (!context) return;
+        chromecastAlive(false);
+        document.getElementById("chromecast-target").append(document.createElement("google-cast-launcher"));        
+        context.addEventListener(
+            cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
+            chromecastSetup,
+        );
+        return () => {
+            context.removeEventListener(
+                cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
+                chromecastSetup,
+            );
+            const media = Chromecast.media();
+            if (!media) return
+            media.removeUpdateListener(chromecastAlive);
+            chromecastAlive(false);
+        };
+    }, []);
+
     const onKeyPressHandler = (e) => {
         if(e.code !== "Space") {
             return
@@ -62,22 +165,33 @@ export function AudioPlayer({ filename, data }) {
     const onPlay = (e) => {
         e.preventDefault();
         e.stopPropagation();
-        wavesurfer.current.play();
         setIsPlaying(true);
+        if (wavesurfer.current) wavesurfer.current.play();
+        if (isChromecast) {
+            const media = Chromecast.media();
+            if (media) media.play();
+        }
     };
 
     const onPause = (e) => {
         e.preventDefault();
         e.stopPropagation();
-        wavesurfer.current.pause();
         setIsPlaying(false);
+        if (wavesurfer.current) wavesurfer.current.pause();
+        if (isChromecast) {
+            const media = Chromecast.media();
+            if (media) media.pause();
+        }
     };
 
     const onVolumeChange = (e) => {
         const v = Number(e.target.value);
         settings_put("volume", v);
         setVolume(v);
-        wavesurfer.current.setVolume(v / 100);
+        if (isChromecast) {
+            const session = Chromecast.session()
+            if (session) session.setVolume(v / 100);
+        } else wavesurfer.current.setVolume(v / 100);
     };
     const onVolumeClick = () => {
         onVolumeChange({ target: { value: 0 }});
@@ -95,13 +209,23 @@ export function AudioPlayer({ filename, data }) {
                 
                     
                         
-                            
-                            
-                            {purcentLoading}%
+                            {
+                                isChromecast ? (
+                                    
+                                        
+                                    
+                                ) : (
+                                    
+                                        
+                                        {purcentLoading}%
+                                        
+                                    
+                                )
+                            }
                         
                         
                         
-                            
+                            
                                 {
                                     isPlaying ? (
                                         
@@ -119,7 +243,7 @@ export function AudioPlayer({ filename, data }) {
 
                             
                                 00:00:00
-                                /
+                                /
                                 00:00:00
                             
                         
diff --git a/client/pages/viewerpage/audioplayer.scss b/client/pages/viewerpage/audioplayer.scss
index ee960d6c..9dd34531 100644
--- a/client/pages/viewerpage/audioplayer.scss
+++ b/client/pages/viewerpage/audioplayer.scss
@@ -90,16 +90,23 @@
                 top: 0;
                 left: 0;
             }
+            .component_icon[alt="loading"] {
+                position: absolute;
+                margin: 50px 0px
+            }
+            .chromecast_loader .component_icon[alt="loading"] {
+                margin: 0;
+                right: 20px;
+                top: 20px;
+                width: 30px;
+            }
+
             .percent {
                 position: absolute;
                 margin: 100px 0px;
                 width: 120px;
                 font-size: 1.4rem;
             }
-            .component_icon[alt="loading"] {
-                position: absolute;
-                margin: 50px 0px
-            }
         }
     }
 }
diff --git a/client/pages/viewerpage/imageviewer.js b/client/pages/viewerpage/imageviewer.js
index 00090333..d61847f5 100644
--- a/client/pages/viewerpage/imageviewer.js
+++ b/client/pages/viewerpage/imageviewer.js
@@ -4,8 +4,8 @@ import ReactCSSTransitionGroup from "react-addons-css-transition-group";
 
 import { MenuBar } from "./menubar";
 import { Bundle, Icon, NgIf, Loader, EventEmitter, EventReceiver } from "../../components/";
-import { alert, randomString, objectGet, notify, getMimeType, currentShare } from "../../helpers/";
-import { Session } from "../../model/";
+import { alert, randomString, notify, getMimeType, currentShare } from "../../helpers/";
+import { Chromecast } from "../../model/";
 import { Pager } from "./pager";
 import { t } from "../../locales/";
 import "./imageviewer.scss";
@@ -67,45 +67,30 @@ export function ImageViewerComponent({ filename, data, path, subscribe, unsubscr
     };
 
     const chromecastHandler = (event) => {
-        const cSession = cast.framework.CastContext.getInstance().getCurrentSession()
-        if (!cSession) return;
+        const session = Chromecast.session();
+        if (!session) return;
 
-        const createLink = () => {
-            const shareID = currentShare();
-            const origin = location.origin;
-            if (shareID) {
-                const target = new URL(origin + data);
-                target.searchParams.append("share", shareID);
-                return Promise.resolve(target.toString());
-            }
-            return Session.currentUser().then(({ authorization }) => {
-                const target = new URL(origin + data);
-                target.searchParams.append("authorization", authorization);
-                return target.toString()
+        const link = Chromecast.createLink(data);
+        const media = new chrome.cast.media.MediaInfo(
+            link,
+            getMimeType(filename),
+        );
+        media.metadata = new chrome.cast.media.PhotoMediaMetadata();
+        media.metadata.title = filename;
+        media.metadata.images = [
+            new chrome.cast.Image(Chromecast.origin() + "/assets/icons/photo.png"),
+        ];
+        Chromecast.createRequest(media)
+            .then((req) => session.loadMedia(req))
+            .catch((err) => {
+                console.error(err)
+                notify.send(t("Cannot establish a connection"), "error");
             });
-        };
-
-        return createLink().then((link) => {
-            const media = new chrome.cast.media.MediaInfo(
-                link,
-                getMimeType(filename),
-            );
-            media.metadata = new chrome.cast.media.PhotoMediaMetadata();
-            media.metadata.title = filename;
-            media.metadata.images = [
-                new chrome.cast.Image(origin + "/assets/icons/photo.png"),
-            ];
-            return cSession.loadMedia(new chrome.cast.media.LoadRequest(media));
-        }).catch((err) => {
-            notify.send(err && err.message, "error");
-        });
     };
 
     useEffect(() => {
-        if (!objectGet(window.chrome, ["cast", "isAvailable"])) {
-            return;
-        }
-        const context = cast.framework.CastContext.getInstance();
+        const context = Chromecast.context();
+        if (!context) return;
         document.getElementById("chromecast-target").append(document.createElement("google-cast-launcher"));
         context.addEventListener(
             cast.framework.CastContextEventType.SESSION_STATE_CHANGED,
@@ -157,8 +142,9 @@ export function ImageViewerComponent({ filename, data, path, subscribe, unsubscr
             
                 
diff --git a/client/pages/viewerpage/imageviewer.scss b/client/pages/viewerpage/imageviewer.scss
index e05c6403..ce743e31 100644
--- a/client/pages/viewerpage/imageviewer.scss
+++ b/client/pages/viewerpage/imageviewer.scss
@@ -26,8 +26,10 @@
         background: #525659;
         overflow: hidden;
         padding: 15px 10px 65px 10px;
+        &.component_image_no_pager { padding-bottom: 15px; }
         @media screen and (max-height: 410px) {
             padding: 5px 0px 40px 10px;
+            &.component_image_no_pager { padding-bottom: 5px; }
             .component_pager .wrapper{
                 > span{padding: 2px 5px;}
                 padding: 5px 0;
diff --git a/client/pages/viewerpage/videoplayer.scss b/client/pages/viewerpage/videoplayer.scss
index 900d7983..53859754 100644
--- a/client/pages/viewerpage/videoplayer.scss
+++ b/client/pages/viewerpage/videoplayer.scss
@@ -34,7 +34,8 @@
     max-height: 500px;
 
     .vjs-control-bar{
-        background: rgba(255,255,255,0.2);
+        background: black;
+        background: linear-gradient(transparent 20%, #00000099);
     }
     .vjs-load-progress div{
         background: var(--primary);