Adds Picture in Picture functionality to Safari for YouTube, Netflix, Amazon Video, Twitch, and more!
- Adds a dedicated Picture in Picture button to the video player of supported sites
- Button integrates seamlessly with the player including hover effects and tooltips
- Supports closed captions in Picture and Picture mode
- Free and open source
Get the latest release from the Safari Extension Gallery
...or live life on the edge with the latest development build (IMPORTANT: these builds do not update automatically!)
- Amazon Video
- CollegeHumor
- CuriosityStream
- Eurosport player
- Giant Bomb
- Hulu
- LittleThings
- Mashable
- Metacafe
- Mixer
- Netflix
- OCS
- Openload
- Periscope
- Plex
- Seznam Zprávy
- Stream.cz
- Streamable
- The Onion
- Twitch
- Udemy
- Vevo
- Vice
- Vid.me
- Video Aktálně
- VRV
- YouTube
You can find information about releases here
- Mac running macOS 10.12 Sierra or later
For convenience the following Node.js tools have been packaged with nexe and included in this repository:
- csso (3.1.1) for compressing CSS
- svgo (0.7.2) for compressing SVG images
- xarjs (0.2.0) for packaging Safari extension
- google-closure-compiler-js (20170806.0.0) for compiling JavaScript
However it is recommended to install the latest versions with Node.js:
npm install -g csso-cli
npm install -g svgo
npm install -g xar-js
npm install -g google-closure-compiler-js
Additionally a reimplementation of the utility PlistBuddy used for automated build numbering is provided but it is advisable to download the original as part of Xcode or from Apple's Command Line Tools
- Clone the repository
- Run
make.sh
- By default this builds the unoptimized and unpackaged development version into the
./out/
directory that can then be installed using Safari's Extension Builder - Alternatively run
./make.sh -p release
to build the optimized and packaged release version (note that packaging requires a private key)
- By default this builds the unoptimized and unpackaged development version into the
If we wanted to support example.com
with the source:
<div class="video-container">
<video src="blob:http://example.com/342b3a13-c892-54ec-84f6-281579de03ab"></video>
<div class="video-captions">
Example caption
</div>
<div class="video-controls">
<button class="control button-play">Play</button>
<button class="control button-fullscreen">Fullscreen</button>
</div>
</div>
We would start by adding a new entry in the resources
object in main.js
:
const resources = {
...
'example' : {
buttonParent: function() {
// Returns the element that will contain the button
return document.querySelector('.video-controls');
},
videoElement: function() {
// Returns the video element
return document.querySelector('.video-container video');
},
// Optional
captionElement: function() {
// Returns the element that contains the video captions
return document.querySelector('.video-captions');
},
},
};
We also need to update the extension permissions to support the new site by editing ./src/Info.plist
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Permissions</key>
<dict>
<key>Website Access</key>
<dict>
...
<key>Allowed Domains</key>
<array>
...
<string>example.com</string>
<string>*.example.com</string>
</array>
</dict>
</dict>
</dict>
</plist>
We might want to style the button so that it integrates with the page better:
const resources = {
...
'example' : {
...
// Assign a CSS class
buttonClassName: 'control',
// Scale the button
buttonScale: 0.5,
// Apply custom CSS styles
buttonStyle: /** CSS */ (`
/* Declaring CSS this way ensures it gets optimized when the extension is built */
cursor: pointer;
opacity: 0.5;
`),
// Apply a custom CSS hover style
buttonHoverStyle: /** CSS */ (`opacity: 1 !important`),
},
};
For more examples, please see the source
- Pied PíPer for the original inspiration and the Netflix icon
- Google for the Picture in Picture icon