Initial release

Basic support for Netflix, Youtube, Amazon Video, Twitch, and more
This commit is contained in:
amarcu5
2017-03-02 16:15:41 +00:00
commit 4e1b0b4c68
17 changed files with 540 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
certs/privatekey.pem
certs/cert.pem
+27
View File
@@ -0,0 +1,27 @@
# PiPer
Adds Picture in Picture functionality to Safari for Youtube, Netflix, Amazon Video, Twitch, and more!
## Installation
Get the extension [here](https://s3.amazonaws.com/piper-extension/PiPer.safariextz), open the downloaded file, and hit trust
## Supported sites
* [Youtube](http://www.youtube.com)
* [Netflix](http://www.netflix.com)
* [Amazon video](http://www.amazon.com/PrimeVideo)
* [Twitch](http://www.twitch.tv)
* [Metacafe](http://www.metacafe.com)
* [Openload](http://www.openload.co)
* [CollegeHumor](http://www.collegehumor.com)
* [Vevo](http://www.vevo.com)
* [Vid.me](http://www.vid.me)
## Acknowledgements
* [Pied PíPer](https://github.com/JoeKuhns/PiedPiPer.safariextension) for the original inspiration and the Netflix icon
* [Google](https://github.com/google/material-design-icons) for the Picture in Picture icon
## Build tools
* [svgo](https://github.com/svg/svgo) (0.7.2) for compressing SVG images
* [xarjs](https://github.com/robertknight/xar-js) (0.2.0) for packaging Safari extension
* [ccjs](https://github.com/google/closure-compiler-js) (1.6.1) for compiling JavaScript
* [plistbuddy](https://github.com/amarcu5/PiPer/tree/master/build-tools/) for automated build numbering - reimplementation of utility from [Apple's Command Line Tools](https://developer.apple.com/download/)
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+25
View File
@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEIjCCAwqgAwIBAgIIAd68xDltoBAwDQYJKoZIhvcNAQEFBQAwYjELMAkGA1UE
BhMCVVMxEzARBgNVBAoTCkFwcGxlIEluYy4xJjAkBgNVBAsTHUFwcGxlIENlcnRp
ZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1BcHBsZSBSb290IENBMB4XDTEz
MDIwNzIxNDg0N1oXDTIzMDIwNzIxNDg0N1owgZYxCzAJBgNVBAYTAlVTMRMwEQYD
VQQKDApBcHBsZSBJbmMuMSwwKgYDVQQLDCNBcHBsZSBXb3JsZHdpZGUgRGV2ZWxv
cGVyIFJlbGF0aW9uczFEMEIGA1UEAww7QXBwbGUgV29ybGR3aWRlIERldmVsb3Bl
ciBSZWxhdGlvbnMgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQDKOFSmy1aqyCQ5SOmM7uxfuH8mkbw0U3rOfGOA
YXdkXqUHI7Y5/lAtFVZYcC1+xG7BSoU+L/DehBqhV8mvexj/avoVEkkVCBmsqtsq
Mu2WY2hSFT2Miuy/axiV4AOsAX2XBWfODoWVN2rtCbauZ81RZJ/GXNG8V25nNYB2
NqSHgW44j9grFU57Jdhav06DwY3Sk9UacbVgnJ0zTlX5ElgMhrgWDcHld0WNUEi6
Ky3klIXh6MSdxmilsKP8Z35wugJZS3dCkTm59c3hTO/AO0iMpuUhXf1qarunFjVg
0uat80YpyejDi+l5wGphZxWy8P3laLxiX27Pmd3vG2P+kmWrAgMBAAGjgaYwgaMw
HQYDVR0OBBYEFIgnFwmpthhgi+zruvZHWcVSVKO3MA8GA1UdEwEB/wQFMAMBAf8w
HwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wLgYDVR0fBCcwJTAjoCGg
H4YdaHR0cDovL2NybC5hcHBsZS5jb20vcm9vdC5jcmwwDgYDVR0PAQH/BAQDAgGG
MBAGCiqGSIb3Y2QGAgEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQBPz+9Zviz1smwv
j+4ThzLoBTWobot9yWkMudkXvHcs1Gfi/ZptOllc34MBvbKuKmFysa/Nw0Uwj6OD
Dc4dR7Txk4qjdJukw5hyhzs+r0ULklS5MruQGFNrCk4QttkdUGwhgAqJTleMa1s8
Pab93vcNIx0LSiaHP7qRkkykGRIZbVf1eliHe2iK5IaMSuviSRSqpd1VAKmuu0sw
ruGgsbwpgOYJd+W+NKIByn/c4grmO7i77LpilfMFY0GCzQ87HUyVpNur+cmV6U/k
TecmmYHpvPm0KdIBembhLoz2IYrF+Hjhga6/05Cdqa3zr/04GpZnMBxRpVzscYqC
tGwPDBUf
-----END CERTIFICATE-----
+28
View File
@@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIEuzCCA6OgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJVUzET
MBEGA1UEChMKQXBwbGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlv
biBBdXRob3JpdHkxFjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwHhcNMDYwNDI1MjE0
MDM2WhcNMzUwMjA5MjE0MDM2WjBiMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQXBw
bGUgSW5jLjEmMCQGA1UECxMdQXBwbGUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkx
FjAUBgNVBAMTDUFwcGxlIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQDkkakJH5HbHkdQ6wXtXnmELes2oldMVeyLGYne+Uts9QerIjAC6Bg+
+FAJ039BqJj50cpmnCRrEdCju+QbKsMflZ56DKRHi1vUFjczy8QPTc4UadHJGXL1
XQ7Vf1+b8iUDulWPTV0N8WQ1IxVLFVkds5T39pyez1C6wVhQZ48ItCD3y6wsIG9w
tj8BMIy3Q88PnT3zK0koGsj+zrW5DtleHNbLPbU6rfQPDgCSC7EhFi501TwN22IW
q6NxkkdTVcGvL0Gz+PvjcM3mo0xFfh9Ma1CWQYnEdGILEINBhzOKgbEwWOxaBDKM
aLOPHd5lc/9nXmW8Sdh2nzMUZaF3lMktAgMBAAGjggF6MIIBdjAOBgNVHQ8BAf8E
BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUK9BpR5R2Cf70a40uQKb3
R01/CF4wHwYDVR0jBBgwFoAUK9BpR5R2Cf70a40uQKb3R01/CF4wggERBgNVHSAE
ggEIMIIBBDCCAQAGCSqGSIb3Y2QFATCB8jAqBggrBgEFBQcCARYeaHR0cHM6Ly93
d3cuYXBwbGUuY29tL2FwcGxlY2EvMIHDBggrBgEFBQcCAjCBthqBs1JlbGlhbmNl
IG9uIHRoaXMgY2VydGlmaWNhdGUgYnkgYW55IHBhcnR5IGFzc3VtZXMgYWNjZXB0
YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5kYXJkIHRlcm1zIGFuZCBj
b25kaXRpb25zIG9mIHVzZSwgY2VydGlmaWNhdGUgcG9saWN5IGFuZCBjZXJ0aWZp
Y2F0aW9uIHByYWN0aWNlIHN0YXRlbWVudHMuMA0GCSqGSIb3DQEBBQUAA4IBAQBc
NplMLXi37Yyb3PN3m/J20ncwT8EfhYOFG5k9RzfyqZtAjizUsZAS2L70c5vu0mQP
y3lPNNiiPvl4/2vIB+x9OYOLUyDTOMSxv5pPCmv/K/xZpwUJfBdAVhEedNO3iyM7
R6PVbyTi69G3cN8PReEnyvFteO3ntRcXqNx+IjXKJdXZD9Zr1KIkIxH3oayPc4Fg
xhtbCS+SsvhESPBgOJ4V9T0mZyCKM2r3DYLP3uujL/lTaltkwGMzd/c6ByxW69oP
IQ7aunMZT7XZNn/Bh1XZp5m5MkL72NVxnn6hUrcbvZNCJBIqxw8dtk2cXmPIS4AX
UKqK1drk/NAJBzewdXUh
-----END CERTIFICATE-----
Executable
+73
View File
@@ -0,0 +1,73 @@
#!/bin/sh
#
# Settings
#
EXTENSION_NAME="PiPer"
# Paths
CCJS_PATH="./build-tools/ccjs" # "ccjs"
XARJS_PATH="./build-tools/xarjs" # "xarjs"
SVGO_PATH="./build-tools/svgo" # "svgo"
PLISTBUDDY_PATH="./build-tools/plistbuddy" # "/usr/libexec/PlistBuddy"
# Certifcates
LEAF_CERT_PATH="../certs/cert.pem"
INTERMEDIATE_CERT_PATH="../certs/apple-intermediate.pem"
ROOT_CERT_PATH="../certs/apple-root.pem"
PRIVATE_KEY_PATH="../certs/privatekey.pem"
#
# Build script
#
# Set working directory to project root
cd $(cd -P -- "$(dirname -- "$0")" && pwd -P)
# Remove output folder
rm -rf out
# Make output folder
mkdir -p "out/${EXTENSION_NAME}.safariextension"
# Copy items into output folder
cp -r src/* "out/${EXTENSION_NAME}.safariextension/"
# Compress all supported images with SVGO
${SVGO_PATH} -q -f "out/${EXTENSION_NAME}.safariextension/images"
# Use closure compiler to compress javascript
${CCJS_PATH} out/${EXTENSION_NAME}.safariextension/scripts/main.js \
--compilation_level=ADVANCED_OPTIMIZATIONS \
--warning_level=VERBOSE \
--externs="out/${EXTENSION_NAME}.safariextension/scripts/externs.js" \
--new_type_inf \
--use_types_for_optimization \
> out/${EXTENSION_NAME}.safariextension/scripts/main.min.js
mv out/${EXTENSION_NAME}.safariextension/scripts/main.min.js out/${EXTENSION_NAME}.safariextension/scripts/main.js
rm out/${EXTENSION_NAME}.safariextension/scripts/externs.js
# Update version info from git
git=$(sh /etc/profile; which git)
number_of_commits=$(($("$git" rev-list HEAD --count) + 1))
git_release_version=$("$git" describe --tags --always --abbrev=0)
git_release_version=${git_release_version%%-*};
git_release_version=${git_release_version#*v};
info_plist="out/${EXTENSION_NAME}.safariextension/Info.plist"
update_plist="update.plist"
${PLISTBUDDY_PATH} -c "Set :CFBundleVersion $number_of_commits" "$info_plist"
${PLISTBUDDY_PATH} -c "Set :CFBundleShortVersionString ${git_release_version}" "$info_plist"
${PLISTBUDDY_PATH} -c "Set \":Extension Updates:0:CFBundleVersion\" $number_of_commits" "$update_plist"
${PLISTBUDDY_PATH} -c "Set \":Extension Updates:0:CFBundleShortVersionString\" ${git_release_version#*v}" "$update_plist"
# Package safari extension
cd out
if [[ ${XARJS_PATH} != /* ]]; then if ! [ command -v "${XARJS_PATH}" >/dev/null 2>&1 ]; then XARJS_PATH="../${XARJS_PATH}"; fi; fi
${XARJS_PATH} create "${EXTENSION_NAME}.safariextz" --cert "${LEAF_CERT_PATH}" --cert "${INTERMEDIATE_CERT_PATH}" --cert "${ROOT_CERT_PATH}" --private-key "${PRIVATE_KEY_PATH}" "${EXTENSION_NAME}.safariextension"
# Remove unpacked extension
if [ -f "${PRIVATE_KEY_PATH}" ]; then rm -rf "${EXTENSION_NAME}.safariextension"; fi
echo "Done."
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

+74
View File
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Author</key>
<string>Adam Marcus</string>
<key>Builder Version</key>
<string>12602.4.8</string>
<key>CFBundleDisplayName</key>
<string>PiPer</string>
<key>CFBundleIdentifier</key>
<string>com.amarcus.safari.piper</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleShortVersionString</key>
<string>0.0</string>
<key>CFBundleVersion</key>
<string>0</string>
<key>Content</key>
<dict>
<key>Scripts</key>
<dict>
<key>End</key>
<array>
<string>scripts/main.js</string>
</array>
</dict>
</dict>
<key>Description</key>
<string>Adds Picture in Picture functionality to Youtube, Netflix, Amazon Video, Twitch, and more!</string>
<key>DeveloperIdentifier</key>
<string>BQ6Q24MF9X</string>
<key>ExtensionInfoDictionaryVersion</key>
<string>1.0</string>
<key>Update Manifest URL</key>
<string>https://s3.amazonaws.com/piper-extension/update.plist</string>
<key>Website</key>
<string>https://github.com/amarcu5/PiPer/</string>
<key>Permissions</key>
<dict>
<key>Website Access</key>
<dict>
<key>Allowed Domains</key>
<array>
<string>*.amazon.co.uk</string>
<string>*.amazon.com</string>
<string>*.amazon.fr</string>
<string>*.amazon.de</string>
<string>*.amazon.ca</string>
<string>*.netflix.com</string>
<string>netflix.com</string>
<string>*.youtube.com</string>
<string>youtube.com</string>
<string>*.twitch.tv</string>
<string>twitch.tv</string>
<string>*.metacafe.com</string>
<string>metacafe.com</string>
<string>*.openload.co</string>
<string>openload.co</string>
<string>*.vevo.com</string>
<string>vevo.com</string>
<string>*.collegehumor.com</string>
<string>collegehumor.com</string>
<string>vid.me</string>
<string>*.vid.me</string>
</array>
<key>Include Secure Pages</key>
<true/>
<key>Level</key>
<string>Some</string>
</dict>
</dict>
</dict>
</plist>
+4
View File
@@ -0,0 +1,4 @@
<svg fill="#FFFFFF" height="36" viewBox="0 0 24 24" width="36" xmlns="http://www.w3.org/2000/svg">
<path d="M19 7h-8v6h8V7zm2-4H3c-1.1 0-2 .9-2 2v14c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98V5c0-1.1-.9-2-2-2zm0 16.01H3V4.98h18v14.03z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>

After

Width:  |  Height:  |  Size: 289 B

+21
View File
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 36 36" style="enable-background:new 0 0 36 36;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
.st1{fill:url(#SVGID_2_);}
</style>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="18" y1="0" x2="18" y2="36">
<stop offset="0" style="stop-color:#C9C9C9"/>
<stop offset="1" style="stop-color:#6D6D6D"/>
</linearGradient>
<path class="st0" d="M36,18c0,9.9-8.1,18-18,18S0,27.9,0,18S8.1,0,18,0S36,8.1,36,18z M18,3.1C9.8,3.1,3.1,9.8,3.1,18
S9.8,32.9,18,32.9S32.9,26.2,32.9,18S26.2,3.1,18,3.1z"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="18" y1="9.8" x2="18" y2="26.3">
<stop offset="0" style="stop-color:#C9C9C9"/>
<stop offset="1" style="stop-color:#6D6D6D"/>
</linearGradient>
<path class="st1" d="M27,26.3H9c-1.1,0-2-0.9-2-2V11.8c0-1.1,0.9-2,2-2h18c1.1,0,2,0.9,2,2v12.5C29,25.4,28.1,26.3,27,26.3z
M18.4,15v-1.9c0-1.1-0.9-2-2-2h-6.3c-1.1,0-2,0.9-2,2V15c0,1.1,0.9,2,2,2h6.3C17.5,17,18.4,16.1,18.4,15z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+14
View File
@@ -0,0 +1,14 @@
/** @const */
var safari = {};
/** @const */
safari.extension = {};
/** @type {string} */
safari.extension.baseURI;
/** @type {string} */
HTMLVideoElement.prototype.webkitPresentationMode;
/** @return {undefined} */
HTMLVideoElement.prototype.webkitSetPresentationMode = function(mode) {}
+247
View File
@@ -0,0 +1,247 @@
'use strict';
/**
* @typedef {{
* buttonImage: (string | undefined),
* buttonElementType: (string|undefined),
* buttonStyle: (string|undefined),
* buttonClassName: (string|undefined),
* buttonParent: function(): ?Element,
* buttonInsertBefore: (function(Element): ?Node|undefined),
* videoElement: function(): ?Element,
* buttonWillAppear: (function(): undefined|undefined),
* }}
*/
var PIPResource;
/** @define {boolean} */
const COMPILED = false;
function log(/** string */ message) {
!COMPILED && console.log("PIPer: " + message);
}
const BUTTON_ID = 'PIPButton';
var /** boolean */ buttonAdded = false;
var /** ?PIPResource */ currentResource = null;
const addButton = function (/** Element */ parent) {
const button = document.createElement(currentResource.buttonElementType || 'button');
button.id = BUTTON_ID;
button.title = 'Open Picture in Picture mode';
if (currentResource.buttonStyle) button.style.cssText = currentResource.buttonStyle;
if (currentResource.buttonClassName) button.className = currentResource.buttonClassName;
const image = document.createElement('img');
image.src = safari.extension.baseURI + 'images/' + (currentResource.buttonImage || 'default') + '.svg';
image.style.cssText = 'width:100%;height:100%';
button.appendChild(image);
button.addEventListener('click', function (event) {
event.preventDefault();
const video = /** @type {?HTMLVideoElement} */ (currentResource.videoElement());
if (!video) return;
video.webkitSetPresentationMode('inline' === video.webkitPresentationMode ? 'picture-in-picture' : 'inline');
});
parent.insertBefore(button, currentResource.buttonInsertBefore ? currentResource.buttonInsertBefore(parent) : null);
}
const buttonObserver = function () {
if (buttonAdded) {
if (document.getElementById(BUTTON_ID)) return;
log("Button removed");
buttonAdded = false;
}
const buttonParent = currentResource.buttonParent();
if (buttonParent) {
if (currentResource.buttonWillAppear) currentResource.buttonWillAppear();
addButton(buttonParent);
log("Button added");
buttonAdded = true;
}
};
/** @type {!IObject<string, PIPResource>} */
const resources = {
'amazon': {
buttonStyle: 'border:0;padding:0;margin:0;background-color:transparent;opacity:0.8;position:relative;left:-8px;width:2vw;height:2vw;min-width:20px;min-height:20px',
buttonParent: function () {
const e = document.getElementById('dv-web-player');
return e && e.querySelector('.hideableTopButtons');
},
buttonInsertBefore: function (/** Element */ parent) {
return parent.lastChild;
},
videoElement: function () {
const e = document.querySelector('.rendererContainer');
return e && e.querySelector('video[width="100%"]');
}
},
'youtube': {
buttonStyle: 'transform:scale(0.7)',
buttonClassName: 'ytp-button',
buttonParent: function () {
const e = document.getElementById('movie_player') || document.getElementById('player');
return e && e.querySelector('.ytp-right-controls');
},
videoElement: function () {
const e = document.getElementById('movie_player') || document.getElementById('player');
return e && e.querySelector('video.html5-main-video');
}
},
'netflix': {
buttonImage: 'netflix',
buttonElementType: 'span',
buttonStyle: 'position:absolute;padding:4px;right:0;top:.4em;width:2em;height:2em;cursor:pointer;background-color:#262626',
buttonParent: function () {
const e = document.getElementById('playerContainer');
return e ? e.querySelector('.player-status') : null;
},
buttonWillAppear: function () {
resources['netflix'].buttonParent().style.paddingRight = '50px';
},
videoElement: function () {
const e = document.querySelector('.player-video-wrapper');
return e && e.querySelector('video');
}
},
'twitch': {
buttonStyle: 'transform:scale(0.8)',
buttonClassName: 'player-button',
buttonParent: function () {
const e = document.getElementById('video-playback') || document.getElementById('player');
return e && e.querySelector('.player-buttons-right');
},
videoElement: function () {
const e = document.getElementById('video-playback') || document.getElementById('player');
return e && e.querySelector('video');
}
},
'metacafe': {
buttonElementType: 'div',
buttonStyle: 'transform:scale(0.9);left:-2px',
buttonParent: function () {
const e = document.getElementById('player_place');
return e && e.querySelector('.tray');
},
videoElement: function () {
const e = document.getElementById('player_place');
return e && e.querySelector('video');
}
},
'openload': {
buttonStyle: 'transform:scale(0.6);left:5px',
buttonClassName: 'vjs-control vjs-button',
buttonInsertBefore: function (/** Element */ parent) {
return parent.lastChild;
},
buttonParent: function () {
const e = document.getElementById('olvideo');
return e && e.querySelector('.vjs-control-bar');
},
videoElement: function () {
return document.getElementById('olvideo_html5_api');
}
},
'vevo': {
buttonStyle: 'transform:scale(0.7);border:0;background:transparent',
buttonClassName: 'player-control',
buttonInsertBefore: function (/** Element */ parent) {
return parent.lastChild;
},
buttonParent: function () {
const e = document.getElementById('control-bar');
return e && e.querySelector('.right-controls');
},
videoElement: function () {
return document.getElementById('html5-player');
}
},
'collegehumor': {
buttonStyle: 'transform:scale(0.6)',
buttonClassName: 'vjs-control vjs-button',
buttonInsertBefore: function (/** Element */ parent) {
return parent.lastChild;
},
buttonParent: function () {
const e = document.getElementById('vjs_video_3');
return e && e.querySelector('.vjs-control-bar');
},
videoElement: function () {
return document.getElementById('vjs_video_3_html5_api');
}
},
'vid': {
buttonStyle: 'position:relative;left:9px;top:-2px;transform:scale(0.7);padding:0;margin:0',
buttonInsertBefore: function (/** Element */ parent) {
return parent.lastChild;
},
buttonParent: function () {
const e = document.getElementById('video_player');
return e && e.querySelector('.vjs-control-bar');
},
videoElement: function () {
return document.getElementById('video_player_html5_api');
}
}
};
const domainName = location.hostname.match(/([^.]+)\.(?:co\.)?[^.]+$/)[1];
if (domainName in resources) {
log("Matched site " + domainName + " (" + location + ")");
currentResource = resources[domainName];
const observer = new MutationObserver(buttonObserver);
observer.observe(document, {
childList:true,
subtree:true
});
buttonObserver();
}
+23
View File
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Extension Updates</key>
<array>
<dict>
<key>CFBundleIdentifier</key>
<string>com.amarcus.safari.piper</string>
<key>CFBundleShortVersionString</key>
<string>0.1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>Developer Identifier</key>
<string>BQ6Q24MF9X</string>
<key>URL</key>
<string>https://s3.amazonaws.com/piper-extension/PiPer.safariextz</string>
<key>Update From Gallery</key>
<true/>
</dict>
</array>
</dict>
</plist>