1

Archive Project

This commit is contained in:
Kim Wittenburg
2017-10-31 14:39:33 +01:00
commit a83ef869d5
29 changed files with 10211 additions and 0 deletions

3
.babelrc Normal file
View File

@@ -0,0 +1,3 @@
{
"presets": ["env"]
}

11
.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
# IDEA Project Files
.idea
# Node.js
node_modules/
# Media Assets
assets/
# Generated Files
*.ite

BIN
Play Button.pxm Normal file

Binary file not shown.

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# iTunes Extras Template
## Asset File Structure and Naming

BIN
TuneKit/sounds/Exit.aif Normal file

Binary file not shown.

BIN
TuneKit/sounds/Limit.aif Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

5941
TuneKit/src/TuneKit.js Normal file

File diff suppressed because it is too large Load Diff

67
compile.sh Normal file
View File

@@ -0,0 +1,67 @@
#!/usr/bin/env bash
BABEL=./node_modules/babel-cli/bin/babel.js
SASS=./node_modules/node-sass/bin/node-sass
MUSTACHE=./node_modules/mustache/bin/mustache
DATA_FILE=data.json
SOURCE_ROOT=src
TARGET_DIR="iTunes Extras.ite"
rm -r "${TARGET_DIR}"
mkdir -p "${TARGET_DIR}"
cp -R TuneKit/ "${TARGET_DIR}/TuneKit/"
for file in $(find ${SOURCE_ROOT} -type f)
do
echo -n "Compiling $file..."
dir=$(dirname "$file")
filename=$(basename "$file")
extension="${filename##*.}"
filename="${filename%.*}"
filename="${filename%.*}" # For double extensions
if [[ $filename == _* ]]; then
echo -en "\033[0;33m"
echo " Skipping"
echo -en "\033[0;00m"
continue
fi
targetDir="${TARGET_DIR}${dir#${SOURCE_ROOT}}"
partialTarget="${targetDir}/${filename}"
mkdir -p "$targetDir"
case "$file" in
*.xml.mustache )
${MUSTACHE} "${DATA_FILE}" "${file}" "${partialTarget}.xml"
;;
*.plist.mustache )
${MUSTACHE} "${DATA_FILE}" "${file}" "${partialTarget}.plist"
;;
*.js.mustache )
${MUSTACHE} "${DATA_FILE}" "${file}" | ${BABEL} --out-file "${partialTarget}.js" --presets env
;;
*.js )
${BABEL} "${file}" --out-file "${partialTarget}.js" --presets env
;;
*.scss )
${SASS} --no-cache --output-style compressed "$file" > "${partialTarget}.css"
;;
*)
cp "$file" "${partialTarget}.${extension}"
;;
esac
echo -en "\033[0;92m"
echo " Done"
echo -en "\033[0;00m"
done
echo -n "Copying Assets... "
cp -R assets/ "${TARGET_DIR}/"
echo -en "\033[0;92m"
echo " Done"
echo -en "\033[0;00m"

76
data.json Normal file
View File

@@ -0,0 +1,76 @@
{
"XID": "TEST:uuid:5F861BFF-C205-4F6C-94F0-2FEEF662404D",
"movieID": 982749837,
"extrasID": 530706165,
"extrasVersion": "1.0",
"extrasBuildNumber": 42,
"meta": {
"title": "König Ödipus",
"artist": "Sven Schütze",
"description": "Mit nur neun Requisiten und rasanten Rollenwechseln erzählt Bodo Wartke die Geschichte des Ödipus, Sohn des Laios, König von Theben, der unwissend seinen eigenen Vater tötet. Und später, als Belohnung dafür, dass er Theben von der Sphinx befreit, Iokaste, die Witwe des Königs und damit seine eigene Mutter, zur Ehefrau erhält. Ein Solo-Theater mit dem Klavierkabarettisten in allen 14 Rollen! Bodo Wartkes exzellente Darbietung seiner Bühnenfassung der klassischen Tragödie „König Ödipus“ bietet einen barrierefreien Einstieg in einen zu Recht berühmten Sagenstoff und vereint Komödie und Tragödie zu einem fantastischen und unvergesslichen Theaterabend so macht Bildung Spaß! (aus dem Pressetext).",
"longDescription": "Mit nur neun Requisiten und rasanten Rollenwechseln erzählt Bodo Wartke die Geschichte des Ödipus, Sohn des Laios, König von Theben, der unwissend seinen eigenen Vater tötet. Und später, als Belohnung dafür, dass er Theben von der Sphinx befreit, Iokaste, die Witwe des Königs und damit seine eigene Mutter, zur Ehefrau erhält. Ein Solo-Theater mit dem Klavierkabarettisten in allen 14 Rollen! Bodo Wartkes exzellente Darbietung seiner Bühnenfassung der klassischen Tragödie „König Ödipus“ bietet einen barrierefreien Einstieg in einen zu Recht berühmten Sagenstoff und vereint Komödie und Tragödie zu einem fantastischen und unvergesslichen Theaterabend so macht Bildung Spaß! (aus dem Pressetext).",
"genre": "Comedy",
"releaseDate": "2010-04-11T00:00:00Z",
"year": "2010",
"studio": "ReimKultur",
"sort-name": "König Ödipus"
},
"features": [
{
"title": "Gespräch I",
"description": "Katrin Jäger spricht mit Bodo Wartke, Sven Schütze und Carmen Kalisch über die Entsehung von „König Ödipus“, die Adaptionsarbeit, die Proben, über dramaturgische und schauspielerische Herausforderungen.",
"src": "Gespraech_I.m4v",
"imageName": "Gespraech_I.png",
"duration": "43:03"
},
{
"title": "Interview mit Polybos",
"description": "Katrin Jäger spricht mit Bodo Wartke über die Figur des Polybos.",
"src": "Interview_mit_Polybos.m4v",
"imageName": "Interview_mit_Polybos.png",
"duration": "0:50"
},
{
"title": "Gespräch II",
"description": "Katrin Jäger spricht mit Bodo Wartke, Sven Schütze und Carmen Kalisch tiefergehend über die Problematik des Schicksalsbegriffs und den Umgang damit in Bodo Wartkes „König Ödipus“.",
"src": "Gespraech_II.m4v",
"imageName": "Gespraech_II.png",
"duration": "28:02"
},
{
"title": "Gespräche III",
"description": "Carl der Löwe plaudert mit Katrin Jäger über seine langjährige Karriere im Showgeschäft und seine Rolle als Sphinx im „König Ödipus“.",
"src": "Gespraech_III.m4v",
"imageName": "Gespraech_III.png",
"duration": "07:02"
},
{
"title": "Das Making-of",
"description": "Wie „König Ödipus“ auf die DVD kam.",
"src": "Making-of.m4v",
"imageName": "Making-of.png",
"duration": "43:10"
},
{
"title": "Episode IV: Ödipus und Teiresias",
"description": "Die erste von Bodo verfasste Szene des Dramas, hier in der Fassung der DVD zu „Ich denke, also sing ich“, aufgezeichnet 2004 im Werk9 in Berlin.",
"src": "Episode_IV.m4v",
"imageName": "Episode_IV.png",
"duration": "9:41"
},
{
"title": "Finale Mortale",
"description": "Anlässlich der gemeinsamen „United Slapstick Show“ erweiterte Bodo im Jahr 2000 das bis dahin als alleinstehende Zugabe existierende Ende des Stückes um etliche Rollen und Verse.",
"src": "Finale_Mortale.m4v",
"imageName": "Finale_Mortale.png",
"duration": "10:12"
},
{
"title": "Antigone",
"description": "Das Sequel zu „König Ödipus“.",
"src": "Antigone.m4v",
"imageName": "Antigone.png",
"duration": "01:32"
}
]
}

3246
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

10
package.json Normal file
View File

@@ -0,0 +1,10 @@
{
"name": "iTunes Extras Template",
"version": "1.0.0",
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-env": "^1.6.0",
"mustache": "^2.3.0",
"node-sass": "^4.5.3"
}
}

View File

@@ -0,0 +1,74 @@
const chaptersController = new TKPageSliderController({
id: 'chapters',
previousPageButton: '.carousel-previous',
nextPageButton: '.carousel-next',
outlets: [{name: 'label', selector: '.carousel-label'}],
title: _('Chapters')
});
chaptersController.viewDidLoad = function () {
this.view.appendChild(TKUtils.buildElement({
type: 'emptyDiv',
className: 'carousel-previous button'
}));
this.view.appendChild(TKUtils.buildElement({
type: 'emptyDiv',
className: 'carousel-next button'
}));
this.view.appendChild(TKUtils.buildElement({
type: 'emptyDiv',
className: 'carousel-label'
}));
this.slidingViewData = {
sideElementsVisible: 4,
distanceBetweenElements: 800,
sideOffsetBefore: 0,
sideOffsetAfter: 0,
elements: this.createThumbnails(),
incrementalLoading: true
};
bookletController.registerForDisplayUpdates(this);
};
chaptersController.viewDidAppear = function () {
this.updateDisplay();
};
chaptersController.updateDisplay = function () {
if (this.highlightedPageIndex !== (bookletController.getChapter() - 1)) {
this.highlightedPageIndex = bookletController.getChapter() - 1;
}
};
/* ==================== Creating Pages ==================== */
chaptersController.createThumbnails = function () {
let elements = [];
for (let i = 1; i <= appData.chapters.length; i++) {
let padded_index = (i < 10) ? '0' + i : i;
let url = 'images/chapters/chapter' + padded_index + '.jpg';
elements.push({
type: 'container',
children: [{
type: 'container', children: [
{type: 'image', src: url},
{type: 'image', src: 'images/interface/buttonPlayCircle.png'}
]
}]
});
}
return elements;
};
/* ==================== Pages Navigation ==================== */
// called when the user focuses another page
chaptersController.pageWasHighlighted = function (index) {
this.label.textContent = appData.chapters[index];
};
// called when the user activates the focused pages
chaptersController.pageWasSelected = function (index) {
bookletController.playChapter(index + 1);
};

View File

@@ -0,0 +1,25 @@
let appData = {
feature: {
XID: "{{{ XID }}}",
title: "{{{ meta.title }}}",
artist: "{{{ meta.artist }}}"
},
audioLoop: {
src: "audio/background.m4a",
loop: true
},
chapters: [
{{#chapters}}"{{{.}}}",{{/chapters}}
],
features: [
{{#features}}
{
title: "{{{title}}}",
description: "{{{description}}}",
src: "{{{src}}}",
imageName: "{{{imageName}}}",
duration: "{{{duration}}}"
},
{{/features}}
]
};

View File

@@ -0,0 +1,87 @@
let featuresHelper = {};
featuresHelper.createFeatureController = function (feature, index) {
let controller = new TKController({
id: 'selection-' + index
});
controller.viewDidLoad = function () {
this.view.classList.add('selection');
this.view.appendChild(TKUtils.buildElement({
type: 'container',
children: [
{
type: 'image',
src: 'images/' + feature.imageName
}, {
type: 'image',
src: 'images/interface/buttonPlayCircle.png',
className: 'play-overlay'
}]
}));
this.view.appendChild(TKUtils.buildElement({
type: 'div',
className: 'title',
content: feature.title
}));
this.view.appendChild(TKUtils.buildElement({
type: 'div',
className: 'description',
content: feature.description
}));
this.view.querySelector('.play-overlay').addEventListener('click', () => {
console.log(feature);
bookletController.playNonLibraryContent({src: feature.src, string: feature.title});
})
};
return controller;
}
;
let featuresController = new TKTabController({
id: 'features',
tabsSelector: '.list .list-item',
controllers: appData.features.map(featuresHelper.createFeatureController),
title: _('Features')
});
featuresController.viewDidLoad = function () {
const vignetteElement = TKUtils.buildElement({
type: 'emptyDiv',
className: 'vignette'
});
this.view.appendChild(vignetteElement);
this.view.addClassName('list-on-right');
this.buildFeaturesList(appData.features);
};
featuresController.buildFeaturesList = function (features) {
let featureElements = features.map(feature => {
return {
type: 'container',
className: 'list-item',
children: [
{type: 'image', src: 'images/' + feature.imageName},
{
type: 'container',
className: 'list-item-body',
children: [
{type: 'div', className: 'title', content: feature.title},
{type: 'div', className: 'duration', content: formatTime(feature.duration)}
]
}
]
}
});
this.view.appendChild(TKUtils.buildElement({
type: 'container',
className: 'list',
children: [
{type: 'div', className: 'list-title', content: _('Features')},
{
type: 'container',
className: 'list-content',
children: featureElements
}
]
}));
};

32
src/controllers/home.js Normal file
View File

@@ -0,0 +1,32 @@
const homeController = new TKController({
id: 'home',
actions: [
{selector: '.play', action: bookletController.playFeature}
],
navigatesTo: [
{selector: '.features', controller: 'features'}
],
highlightedElement: '.play'
});
homeController.viewDidLoad = function () {
let mainMenuItems = [];
mainMenuItems.push({
type: 'div',
className: 'play button',
content: _('Play')
});
if (typeof appData.features !== 'undefined' && appData.features.length > 0) {
mainMenuItems.push({
type: 'div',
className: 'features button',
content: _('Features')
})
}
this.view.appendChild(TKUtils.buildElement({
type: 'container',
className: 'main-menu',
children: mainMenuItems
}));
};

View File

@@ -0,0 +1,31 @@
window.addEventListener('load', () => {
let backButtonElement = document.querySelector('#header .back');
backButtonElement.innerHTML = _('Back');
TKNavigationController.sharedNavigation.backButton = '.back';
});
bookletController.navigationControllerWillShowController = function (navigationController, controller) {
function showBackgroundForController() {
let imagePath = 'images/';
if (navigationController.rootController !== controller) {
imagePath += controller.id + '/'
}
imagePath += 'background.png';
controller.view.style.backgroundImage = "url('" + imagePath + "')";
}
function toggleHeader() {
let headerElement = document.getElementById('header');
let titleElement = headerElement.querySelector('.title');
if (controller === navigationController.rootController) {
headerElement.addClassName('hidden');
} else {
titleElement.innerHTML = controller.title;
headerElement.removeClassName('hidden');
}
}
toggleHeader();
showBackgroundForController();
};

View File

@@ -0,0 +1,37 @@
let STRINGS_DE = {
"Back": "Zurück",
"Play": "Wiedergabe",
"Chapters": "Kapitel",
"Features": "Bonusmaterial",
"Hr.": "Std.",
"Min.": "Min.",
"Sec.": "Sek."
};
function _(key) {
if (key in STRINGS_DE) {
return STRINGS_DE[key]
} else {
return key
}
}
function formatTime(timeString) {
const components = timeString.split(':').map(Number);
let formattedComponents = [];
if (components[0] > 0) {
formattedComponents.push(components[0]);
const name = components.length === 3 ? _('Hr.') : _('Min.');
formattedComponents.push(name);
}
if (components[1] > 0) {
formattedComponents.push(components[1]);
const name = components.length === 3 ? _('Min.') : _('Sec.');
formattedComponents.push(name);
}
if (formattedComponents.length < 4 && components.length >= 3 && components[2] > 0) {
formattedComponents.push(components[2]);
formattedComponents.push(_('Sec.'));
}
return formattedComponents.join(' ');
}

23
src/css/_functions.scss Normal file
View File

@@ -0,0 +1,23 @@
$directions: (
left: 135deg,
right: -45deg,
);
@mixin arrow($direction, $size, $thickness) {
$angle: 0;
@if map-has_key($directions, $direction) {
$angle: #{map_get($directions, $direction)}
} @else {
$angle: $direction
}
content: ' ';
border: 0 solid;
border-right-width: $thickness;
border-bottom-width: $thickness;
display: inline-block;
padding: $size;
transform: rotate($angle);
-webkit-transform: rotate($angle);
margin-bottom: $size / 2;
}

124
src/css/_variables.scss Normal file
View File

@@ -0,0 +1,124 @@
// Global (default) Values
$page-min-width: 1280px;
$page-min-height: 720px;
$content-margin-left: 75px;
$content-margin-right: 75px;
$content-margin-top: 140px; // Distance from top of page to top of content (ignoring the header)
$content-margin-bottom: 60px;
$default-text-color: rgba(235, 235, 235, 1);
$default-border-color: rgba(255, 255, 255, 0.3);
$default-transition-duration: 500ms;
$controller-change-transition-duration: $default-transition-duration;
$default-font-family: "Helvetica Neue", "Helvetica", sans-serif;
$default-font-size: 1rem;
$default-font-weight: 300;
$play-overlay-size: 100px;
// Z-Indexes
$header-z-index: 10;
$carousel-controls-z-index: 2;
// Header
$header-left: $content-margin-left;
$header-right: $content-margin-right;
$header-top: 0;
$header-vertical-padding: 0.8rem;
$header-horizontal-padding: 0;
$header-border-width: 1px;
$header-border-color: $default-border-color;
$header-font-size: 1.7rem;
$header-transition-duration: $controller-change-transition-duration;
// Back Button
$back-button-arrow-thickness: 1px;
$back-button-arrow-size: 4px; // TODO: Use rem instead
$back-button-arrow-text-spacer: 5px;
$back-button-font-size: 1.1rem;
$back-button-corner-radius: 5px;
$back-button-background-color: $header-border-color;
$back-button-horizontal-padding: 10px;
$back-button-vertical-padding: 7px;
// Main Menu
$main-menu-bottom-distance: 62px;
$main-menu-padding: 25px;
$main-menu-item-distance: 100px;
$main-menu-font-size: 1.7rem;
$main-menu-blur-radius: 10px;
$main-menu-background-color: rgba(0, 0, 0, 0.3);
$main-menu-border-width: 1px;
$main-menu-border-color: $default-border-color;
// Carousel
$carousel-top-space: $content-margin-top;
$carousel-left-margin: 0;
$carousel-right-margin: 0;
$carousel-item-width: 800px;
$carousel-item-height: 450px;
$carousel-secondary-scale-factor: 0.75;
$carousel-transition-duration: $default-transition-duration;
$carousel-label-top-margin: 20px;
$carousel-label-font-size: 2.25rem;
$carousel-control-buttons-top-margin: $carousel-top-space + 0.5 * $carousel-item-height;
$carousel-previous-left: $content-margin-left;
$carousel-next-right: $content-margin-right;
$carousel-control-buttons-size: 15px;
$carousel-control-buttons-thickness: 5px;
// List on Right
$lor-margin-left: $content-margin-left;
$lor-margin-right: $content-margin-right;
$lor-margin-top: $content-margin-top;
$lor-margin-bottom: $content-margin-bottom;
$lor-list-margin-top: $lor-margin-top;
$lor-list-margin-right: $lor-margin-right;
$lor-list-margin-bottom: $lor-margin-bottom;
$lor-list-width: 320px;
$lor-list-background-color: rgba(0, 0, 0, 0.75);
$lor-list-title-color: white;
$lor-list-title-padding: 10px;
$lor-item-border: 1px solid $default-border-color;
$lor-item-background-color: transparent;
$lor-item-selected-background: linear-gradient(to bottom, rgba(41, 137, 216, 0.75) 0%, rgba(30, 87, 153, 0.75) 100%);
$lor-item-padding: 12px;
$lor-item-image-width: 64px;
$lor-item-image-height: 36px;
$lor-item-title-color: white;
$lor-item-duration-sep: 5px;
$lor-item-duration-color: rgb(140, 140, 140);
$lor-selection-width: 800px;
$lor-selection-image-max-height: 450px;
$lor-selection-title-sep: 10px;
$lor-selection-title-font-size: 1.6rem;
$lor-selection-title-color: white;
$lor-selection-description-sep: 10px;
$lor-selection-description-font-size: 1.6rem;
$lor-selection-description-font-weight: 200;
$lor-selection-description-color: rgba(255, 255, 255, 0.75);
$lor-vignette-color: black;
$lor-vignette-size: 750px;

92
src/css/carousel.scss Normal file
View File

@@ -0,0 +1,92 @@
@import "variables";
@import "functions";
/* Sliding View */
.tk-page-slider-controller-view {
.sliding-view {
position: absolute;
top: $carousel-top-space;
left: $carousel-left-margin;
right: $carousel-right-margin;
overflow: hidden;
height: $carousel-item-height;
}
.sliding-view-element {
position: absolute;
left: calc(50% - #{$carousel-item-width / 2});
-webkit-transition: -webkit-transform $carousel-transition-duration;
cursor: pointer;
div {
-webkit-transition: -webkit-transform $carousel-transition-duration;
-webkit-transform: scale($carousel-secondary-scale-factor);
}
&.sliding-view-element-focused {
div {
-webkit-transform: scale(1);
}
}
// The actual image of the item
div img:nth-child(1) {
width: $carousel-item-width;
height: $carousel-item-height;
}
// The play button
div img:nth-child(2) {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
opacity: 0;
-webkit-transition: opacity $carousel-transition-duration;
}
.sliding-view-element-focused div img:nth-child(2) {
opacity: 1;
}
}
.sliding-view-element-hidden {
display: none;
}
}
/* Label */
.carousel-label {
position: absolute;
left: calc(50% - #{0.5 * $carousel-item-width});
right: calc(50% - #{0.5 * $carousel-item-width});
top: $carousel-top-space + $carousel-item-height + $carousel-label-top-margin;
font-size: $carousel-label-font-size;
text-align: center;
overflow: visible;
overflow-wrap: break-word;
word-wrap: break-word;
hyphens: auto;
}
/* Arrows */
.carousel-previous {
@include arrow(left, $carousel-control-buttons-size, $carousel-control-buttons-thickness);
position: absolute;
top: $carousel-control-buttons-top-margin;
left: $carousel-previous-left;
z-index: $carousel-controls-z-index;
}
.carousel-next {
@include arrow(right, $carousel-control-buttons-size, $carousel-control-buttons-thickness);
position: absolute;
margin-top: $carousel-control-buttons-top-margin;
right: $carousel-next-right;
z-index: $carousel-controls-z-index;
}

26
src/css/home.scss Normal file
View File

@@ -0,0 +1,26 @@
@import "variables";
.main-menu {
position: absolute;
left: 0;
right: 0;
bottom: $main-menu-bottom-distance;
padding: $main-menu-padding;
-webkit-backdrop-filter: blur($main-menu-blur-radius);
background-color: $main-menu-background-color;
border-top: $main-menu-border-width solid $main-menu-border-color;
border-bottom: $main-menu-border-width solid $main-menu-border-color;
display: flex;
justify-content: center;
div {
text-align: center;
font-size: $main-menu-font-size;
}
div + div {
margin-left: $main-menu-item-distance;
}
}

View File

@@ -0,0 +1,95 @@
@import "variables";
.list-on-right .list {
position: absolute;
top: $lor-list-margin-top;
right: $lor-list-margin-right;
bottom: $lor-list-margin-bottom;
width: $lor-list-width;
background-color: $lor-list-background-color;
display: flex;
flex-direction: column;
.list-title {
color: $lor-list-title-color;
padding: $lor-list-title-padding;
border-bottom: $lor-item-border;
}
.list-content {
overflow: scroll;
}
.list-item {
background-color: $lor-item-background-color;
display: flex;
& + .list-item {
border-top: $lor-item-border;
}
&.tk-tab-selected {
background: $lor-item-selected-background;
}
img {
width: $lor-item-image-width;
height: $lor-item-image-height;
margin: $lor-item-padding;
}
.list-item-body {
flex: 1;
margin: $lor-item-padding $lor-item-padding $lor-item-padding 0;
}
.title {
color: $lor-item-title-color;
}
.duration {
margin-top: $lor-item-duration-sep;
color: $lor-item-duration-color;
}
}
}
.list-on-right .selection {
position: absolute;
left: $lor-margin-left;
top: $lor-margin-top;
width: $lor-selection-width;
div {
position: relative;
text-align: center;
}
img:not(.play-overlay) {
max-width: 100%;
max-height: $lor-selection-image-max-height;
}
.title {
margin-top: $lor-selection-title-sep;
font-size: $lor-selection-title-font-size;
color: $lor-selection-title-color;
}
.description {
margin-top: $lor-selection-description-sep;
font-size: $lor-selection-description-font-size;
font-weight: $lor-selection-description-font-weight;
color: $lor-selection-description-color;
}
}
.vignette {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-shadow: 0 0 $lor-vignette-size $lor-vignette-color inset;
}

103
src/css/shared.scss Normal file
View File

@@ -0,0 +1,103 @@
@import "variables";
@import "functions";
* {
-webkit-user-select: none;
-webkit-user-drag: none;
}
html {
min-width: $page-min-width;
min-height: $page-min-height;
height: 100%;
}
body {
margin: 0;
width: 100%;
height: 100%;
font-family: $default-font-family;
font-weight: $default-font-weight;
font-size: $default-font-size;
color: $default-text-color;
}
#header {
position: absolute;
left: $header-left;
right: $header-right;
top: $header-top;
padding: $header-vertical-padding $header-horizontal-padding;
z-index: $header-z-index;
border-bottom: $header-border-width solid $header-border-color;
-webkit-transition: opacity $header-transition-duration;
text-align: center;
font-size: $header-font-size;
.back {
position: absolute;
font-size: $back-button-font-size;
border-radius: $back-button-corner-radius;
background-color: $back-button-background-color;
padding: $back-button-vertical-padding $back-button-horizontal-padding;
&::before {
@include arrow(left, $back-button-arrow-size, $back-button-arrow-thickness);
margin-right: $back-button-arrow-text-spacer;
}
}
}
#navigation {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-image: url("/images/background.png");
background-size: cover;
> div {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-size: cover;
}
}
// Elements
.button {
cursor: pointer;
&:active {
color: white;
}
}
.hidden {
opacity: 0;
}
.inactive {
opacity: 0;
pointer-events: none;
}
.play-overlay {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: $play-overlay-size;
height: $play-overlay-size;
cursor: pointer;
}

View File

@@ -0,0 +1,58 @@
<?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>Metadata</key>
<dict>
<key>artistName</key>
<string>{{{ meta.artist }}}</string>
<key>description</key>
<string>{{{ meta.description }}}</string>
<key>longDescription</key>
<string>{{{ meta.longDescription }}}</string>
<key>genre</key>
<string>{{{ meta.genre }}}</string>
<key>itemId</key>
<integer>{{{ extrasID }}}</integer>
<key>itemName</key>
<string>{{{ meta.title }}} - iTunes Extras</string>
<key>kind</key>
<string>feature-movie</string>
<key>releaseDate</key>
<string>{{{ meta.releaseDate }}}</string>
<key>sort-artist-status</key>
<integer>128</integer>
<key>sort-name</key>
<string>{{{ meta.sort-name}}}</string>
<key>sort-name-status</key>
<integer>1</integer>
<key>year</key>
<integer>{{{ meta.year }}}</integer>
<key>studio</key>
<string>{{{ meta.studio }}}</string>
<key>playlistId</key>
<integer>{{{ movieID }}}</integer>
<key>playlistName</key>
<string>{{{ meta.title }}}</string>
<key>xid</key>
<string>{{{ XID }}}_ITUNES_EXTRAS</string>
</dict>
<key>Name File</key>
<string>{{{ meta.title }}} - iTunes Extras.ite</string>
<key>associated-adam-ids</key>
<array>
<integer>{{{ movieID }}}</integer>
</array>
<key>xid-asset-mapping</key>
<dict>
<key>{{{ XID }}}</key>
<array>
<integer>{{{ movieID }}}</integer>
</array>
<key>{{{ XID }}}_ITUNES_EXTRAS</key>
<array>
<integer>{{{ extrasID }}}</integer>
</array>
</dict>
</dict>
</plist>

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

33
src/index.html Normal file
View File

@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<meta name="hdtv-fullscreen" content="true"/>
<meta name="hdtv-cursor-off" content="true"/>
<title>iTunes Extra for Movie</title>
<!-- Styles -->
<link rel="stylesheet" href="css/shared.css" type="text/css" media="screen" charset="utf-8">
<link rel="stylesheet" href="css/home.css" type="text/css" media="screen" charset="utf-8">
<link rel="stylesheet" href="css/carousel.css" type="text/css" media="screen" charset="utf-8">
<link rel="stylesheet" href="css/list-on-right.css" type="text/css" media="screen" charset="utf-8">
<!-- TuneKit -->
<script type="text/javascript" src="../TuneKit/src/TuneKit.js" charset="utf-8"></script>
<!-- Controllers -->
<script type="text/javascript" src="controllers/data.js" charset="utf-8"></script>
<script type="text/javascript" src="controllers/strings.js" charset="utf-8"></script>
<script type="text/javascript" src="controllers/navigation.js" charset="utf-8"></script>
<script type="text/javascript" src="controllers/home.js" charset="utf-8"></script>
<script type="text/javascript" src="controllers/chapters.js" charset="utf-8"></script>
<script type="text/javascript" src="controllers/features.js" charset="utf-8"></script>
</head>
<body>
<div id="navigation">
<header id="header" class="hidden">
<div class="back button"></div>
<div class="title"></div>
</header>
</div>
</body>
</html>

14
src/manifest.xml.mustache Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns="http://apple.com/itunes/media_archive/manifest" version="1.0" media_archive_version="{{{ extrasVersion }}}"
media_archive_build_number="{{{ extrasBuildNumber }}}">
<requirements>
<supported_platforms>
<platform name="iTunes" minimum_version="9.0"/>
<platform name="AppleTV" minimum_version="3.0"/>
</supported_platforms>
</requirements>
<library_items>
<library_item type="video" xid="{{{ XID }}}" name="{{{ meta.title }}}"/>
</library_items>
</manifest>