Browse Source

first commit

Nolovenodie丶 2 years ago
commit
55baf2792f
10 changed files with 578 additions and 0 deletions
  1. 5 0
      README.md
  2. 138 0
      content/main.js
  3. 29 0
      manifest.json
  4. BIN
      res/icon.png
  5. BIN
      res/icon.psd
  6. 251 0
      static/css/style.css
  7. BIN
      static/img/icon.png
  8. 63 0
      static/js/common-utils.js
  9. 1 0
      static/js/jquery-3.6.0.min.js
  10. 91 0
      static/js/md5.js

+ 5 - 0
README.md

@@ -0,0 +1,5 @@
+# Emby Crx
+
+_Emby 增强插件 (适用于 Chrome 内核浏览器)_
+
+---

+ 138 - 0
content/main.js

@@ -0,0 +1,138 @@
+class Home {
+	static start() {
+		this.itemQuery = { ImageTypes: "Backdrop", EnableImageTypes: "Logo,Backdrop", IncludeItemTypes: "Movie,Series", SortBy: "ProductionYear, PremiereDate, SortName", Recursive: true, ImageTypeLimit: 1, Limit: 10, Fields: "ProductionYear", SortOrder: "Descending", EnableUserData: false, EnableTotalRecordCount: false };
+		this.coverOptions = { type: "Backdrop", maxWidth: 3000 };
+		this.logoOptions = { type: "Logo", maxWidth: 3000 };
+
+		if (window.location.href.indexOf("home") != -1) {
+			const load = `
+            <div class="misty-loading">
+                <h1>MISTY MEDIA</h1>
+                <div class="mdl-spinner"><div class="mdl-spinner__layer mdl-spinner__layer-1"><div class="mdl-spinner__circle-clipper mdl-spinner__left"><div class="mdl-spinner__circle mdl-spinner__circleLeft"></div></div><div class="mdl-spinner__circle-clipper mdl-spinner__right"><div class="mdl-spinner__circle mdl-spinner__circleRight"></div></div></div></div>
+            </div>
+            `;
+			$("body").append(load);
+		}
+		CommonUtils.selectWait(".section0 .backdropCard", async () => {
+			await this.initBanner();
+		});
+	}
+
+	static injectCall(func, arg) {
+		let hash = md5(arg);
+		return new Promise((resolve, reject) => {
+			const channel = new BroadcastChannel(hash);
+			channel.addEventListener("message", (event) => resolve(event.data));
+			const script = `
+            <script class="I${hash}">
+                setTimeout(async ()=> {
+                    var client = await new Promise((resolve, reject) => {
+                        setInterval(() => {
+                            if (window.ApiClient != undefined) resolve(window.ApiClient);
+                        }, 16);
+                    });
+                    const channel = new BroadcastChannel("${hash}");
+                    channel.postMessage(await client.${func}(${arg}));
+                    document.querySelector("script.I${hash}").remove()
+                }, 16)
+            </script>
+            `;
+			$(document.head || document.documentElement).append(script);
+		});
+	}
+
+	static getItems(query) {
+		return this.injectCall("getItems", "client.getCurrentUserId(), " + JSON.stringify(query));
+	}
+
+	static getItem(itemId) {
+		return this.injectCall("getItem", `client.getCurrentUserId(), "${itemId}"`);
+	}
+
+	static getImageUrl(itemId, options) {
+		return this.injectCall("getImageUrl", itemId + ", " + JSON.stringify(options));
+	}
+
+	/* 插入Banner */
+	static async initBanner() {
+		const banner = `
+		<div class="misty-banner">
+            <div class="misty-banner-body">
+            </div>
+            <div class="misty-banner-library">
+                <div class="misty-banner-logos"></div>
+            </div>
+        </div>
+        `;
+		$(".homeSectionsContainer").prepend(banner);
+		$(".section0").detach().appendTo(".misty-banner-library");
+
+		// 插入数据
+		const data = await this.getItems(this.itemQuery);
+		console.log(data);
+		data.Items.forEach(async (item) => {
+			const detail = await this.getItem(item.Id),
+				itemHtml = `
+            <div class="misty-banner-item" id="${detail.Id}">
+                <img draggable="false" loading="eager" decoding="async" class="misty-banner-cover" src="${await this.getImageUrl(detail.Id, this.coverOptions)}" alt="Backdrop" style="">
+                <div class="misty-banner-info padded-left padded-right">
+                    <h1>${detail.Name}</h1>
+                    <div><p>${detail.Overview}</p></div>
+                    <div><button>MORE</button></div>
+                </div>
+            </div>
+            `,
+				logoHtml = `
+                <img id="${detail.Id}" draggable="false" loading="auto" decoding="lazy" class="misty-banner-logo" data-banner="img-title" alt="Logo" src="${await this.getImageUrl(detail.Id, this.logoOptions)}">
+            `;
+			if (detail.ImageTags && detail.ImageTags.Logo) {
+				$(".misty-banner-body").append(itemHtml);
+				$(".misty-banner-logos").append(logoHtml);
+			}
+			console.log(item.Id, detail);
+		});
+
+		let complete = 0;
+		let loading = setInterval(async () => {
+			// 判断图片加载完毕
+			$(".misty-banner-cover:not(.complete)").each((i, dom) => {
+				if (dom.complete) {
+					dom.classList.add("complete");
+					complete++;
+				}
+			});
+			if (complete == $(".misty-banner-item").length && $(".misty-banner-item").length != 0) {
+				clearInterval(loading);
+				$(".misty-loading").fadeOut(500);
+				await CommonUtils.sleep(150);
+				// 置入场动画
+				let delay = 80; // 动媒体库画间隔
+				let id = $(".misty-banner-item").eq(0).addClass("active").attr("id"); // 初次信息动画
+				$(`.misty-banner-logo[id=${id}]`).addClass("active");
+
+				await CommonUtils.sleep(200); // 间隔动画
+				$(".section0 > div").addClass("misty-banner-library-overflow"); // 关闭overflow 防止媒体库动画溢出
+				$(".misty-banner .backdropCard").each((i, dom) => setTimeout(() => $(dom).addClass("misty-banner-library-show"), i * delay)); // 媒体库动画
+				await CommonUtils.sleep(delay * 8 + 1000); // 等待媒体库动画完毕
+				$(".section0 > div").removeClass("misty-banner-library-overflow"); // 开启overflow 防止无法滚动
+
+				// 滚屏逻辑
+				var index = 0;
+				setInterval(async () => {
+					// 背景切换
+					index += index + 1 == $(".misty-banner-item").length ? -index : 1;
+					$(".misty-banner-body").css("left", -(index * 100).toString() + "%");
+					// 信息切换
+					$(".misty-banner-item.active").removeClass("active");
+					let id = $(".misty-banner-item").eq(index).addClass("active").attr("id");
+					// LOGO切换
+					$(".misty-banner-logo.active").removeClass("active");
+					$(`.misty-banner-logo[id=${id}]`).addClass("active");
+				}, 8000);
+			}
+		}, 16);
+	}
+}
+
+// 运行
+Home.start();

+ 29 - 0
manifest.json

@@ -0,0 +1,29 @@
+{
+	"manifest_version": 2,
+	"name": "Emby Tools.",
+	"version": "1.0.0",
+	"description": "Misty Emby Tools.",
+	"author": "Nolovenodie",
+	"homepage_url": "https://ssky.me",
+	"icons": {
+		"16": "static/img/icon.png",
+		"19": "static/img/icon.png",
+		"38": "static/img/icon.png",
+		"48": "static/img/icon.png",
+		"128": "static/img/icon.png"
+	},
+	"browser_action": {
+		"default_icon": "static/img/icon.png",
+		"default_title": "Misty Tools."
+	},
+	"content_scripts": [
+		{
+			"matches": ["<all_urls>"],
+			"include_globs": ["*://*:8096/*", "*://*:12308/*"],
+			"css": ["static/css/style.css"],
+			"js": ["static/js/jquery-3.6.0.min.js", "static/js/common-utils.js", "static/js/md5.js", "content/main.js"],
+			"run_at": "document_end"
+		}
+	],
+	"permissions": ["http://*/*", "https://*/*", "<all_urls>"]
+}

BIN
res/icon.png


BIN
res/icon.psd


+ 251 - 0
static/css/style.css

@@ -0,0 +1,251 @@
+::-webkit-scrollbar {
+	width: .3em;
+}
+
+.view:not(.hide) .skinHeader {
+	width: 100%;
+	background-image: linear-gradient(black, transparent) !important;
+	background-color: unset !important;
+}
+
+.view[data-type=home]:not(.hide) .scrollSlider.padded-top-page {
+	padding-top: 0 !important;
+}
+
+.view:not(.hide) .itemsContainer-finepointerwrap{
+	flex-wrap: initial !important;
+	-webkit-flex-wrap: initial !important;
+}
+
+.view:not(.hide) .section0{
+	z-index: 2;
+}
+
+.view:not(.hide) .section0 .cardText,
+.view:not(.hide) .section0 .cardOverlayContainer,
+.view:not(.hide) .section0 .sectionTitleContainer {
+	display: none;
+}
+
+.view:not(.hide) .section0 .cardBox-touchzoom {
+	box-shadow: 0 8px 10px rgb(0 0 0 / 15%);
+}
+
+.view:not(.hide) .section0 .backdropCard {
+	transition: all 1.5s cubic-bezier(0, 1.75, .25, 1) 0s;
+}
+
+.view:not(.hide) .section0 .backdropCard:hover {
+	transform: scale(1.1) !important;
+}
+
+.view:not(.hide) .section0 .scrollbuttoncontainer {
+	top: 0;
+	bottom: calc(.8em - min(.72em, max(.48em, 1.78vw)) / 2);
+	background-color: rgba(0, 0, 0, 0);
+	overflow: visible;
+}
+
+.view:not(.hide) .section0 .scrollbuttoncontainer:hover > .emby-scrollbuttons-scrollbutton{
+	background-color: rgba(0, 0, 0, .5);
+	transform: scale(.85) !important;
+}
+
+.view:not(.hide) .tabs-viewmenubar-backgroundcontainer:not(.tabs-viewmenubar-backgroundcontainer-tv) {
+	background: 0 0 !important;
+	-webkit-backdrop-filter: blur(10px) !important;
+	backdrop-filter: blur(10px) !important;
+}
+
+.misty-banner {
+	position: relative;
+	overflow: hidden;
+}
+
+.misty-banner-cover {
+	width: 100%;
+	max-height: 100vh;
+	user-select: none;
+	object-fit: cover;
+}
+
+.misty-banner-logo {
+	position: absolute;
+	user-select: none;
+	object-fit: contain;
+	height: clamp(0rem, -2.182rem + 10.91vw, 6rem);
+	width: fit-content;
+	transform: translateY(calc(-50% - clamp(-2rem, -3.455rem + 7.27vw, 2rem)));
+	right: calc(3.4% + min(0.72em, max(0.48em, 1.78vw)));
+	opacity: 0;
+	transition: 1s;
+}
+
+.misty-banner-logo.active {
+	transform: translateY(calc(-100% - clamp(-2rem, -3.455rem + 7.27vw, 2rem)));
+	opacity: 1;
+}
+
+.misty-loading {
+	position: fixed;
+	top: 0;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	background-color: rgba(0, 0, 0, 1);
+	z-index: 999;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	flex-direction: column;
+}
+
+.misty-loading h1 {
+	margin: 0;
+	margin-bottom: 3rem;
+}
+
+.misty-loading .mdl-spinner {
+	margin: 0;
+	position: initial
+}
+
+.misty-loading .mdl-spinner__layer-1 {
+	border-color: #fff;
+}
+
+.misty-banner-library {
+	position: absolute;
+	width: 100%;
+	height: 100%;
+	top: 0;
+	padding: clamp(0rem, -1.313rem + 3.75vw, 1.5rem) 0;
+	box-sizing: border-box;
+	display: flex;
+	justify-content: end;
+	flex-direction: column;
+	background-image: linear-gradient(90deg, rgba(0, 0, 0, .6), transparent);
+}
+
+.misty-banner-body {
+	display: flex;
+	position: relative;
+	left: 0;
+	transition: all 1.5s cubic-bezier(0.15, 0.07, 0, 1) 0s;
+}
+
+.misty-banner-item {
+	min-width: 100%;
+}
+
+.misty-banner-info{
+	width: 100%;
+	margin: min(.72em, max(.48em, 1.78vw));
+	margin-top: 0;
+	position: absolute;
+	top: 0;
+	z-index: 1;
+	height: 100%;
+	height: -webkit-fill-available;
+	display: flex;
+	flex-direction: column;
+	justify-content: center
+}
+
+.misty-banner-info > * {
+	transition: all 2.5s cubic-bezier(0, 1.41, 0.36, 0.93) .4s;
+	transform: translateY(150%);
+	opacity: 0 !important;
+}
+
+.misty-banner-item.active .misty-banner-info > *{
+	transform: translateY(0);
+	opacity: 1 !important;
+}
+
+.misty-banner-info > div:nth-child(2) {
+	transition-delay: .6s;
+}
+.misty-banner-info > div:nth-child(3) {
+	transition-delay: .8s;
+}
+
+.misty-banner-info h1 {
+	font-size: clamp(2rem, -.362rem + 6.75vw, 4.7rem);
+	font-weight: bolder;
+	margin: 0;
+	text-shadow: 0 4px 10px rgb(0 0 0 / 20%);
+	/* margin-bottom: clamp(0rem, -.545rem + 2.73vw, 1.5rem); */
+}
+
+.misty-banner-info p {
+	font-size: clamp(.6rem, .4rem + 1vw, 1.6rem);
+	font-weight: bold;
+	max-width: 47%;
+	opacity: .7;
+	/* overflow: hidden;
+	white-space: nowrap;
+	text-overflow: ellipsis; */
+	display: -webkit-box !important;
+	-webkit-box-orient: vertical;
+	-webkit-line-clamp: 2;
+	overflow: hidden;
+}
+
+.misty-banner-info button {
+	cursor: pointer;
+	margin-top: clamp(0rem, -2.625rem + 7.5vw, 3rem);
+	width: 6em;
+	height: 1.8em;
+	background-color: #fff;
+	border: none;
+	font-size: clamp(.6rem, -.275rem + 2.5vw, 1.6rem);
+	border-radius: 10px;
+	font-weight: bold;
+	letter-spacing: 2px;
+	box-shadow: 0 2px 7px rgba(1, 1, 1, -.8);
+	font-family: system-ui;
+	transition: .2s;
+}
+
+.misty-banner-info button:hover{
+	transform: scale(.95);
+}
+
+@media screen and (max-width: 62.5em) {
+	.misty-banner-info button {
+		margin-top: 0;
+	}
+}
+@media screen and (max-width: 52em) {
+	.misty-banner-info button,
+	.misty-banner-info p {
+		display: none;
+	}
+}
+
+@media screen and (max-width: 35em) {
+	.misty-banner-info,
+	.misty-banner-logo 	{
+		display: none !important;
+	}
+	.misty-banner-body{
+		opacity: 0;
+	}
+}
+
+.misty-banner .backdropCard{
+	transition-duration: 0;
+	transform: translateY(80%);
+	opacity: 0;
+}
+
+.misty-banner-library-show{
+	transition-duration: 1.7s !important;
+	transform: translateY(0) !important;
+	opacity: 1 !important;
+}
+
+.misty-banner-library-overflow {
+	overflow: visible !important;
+}

BIN
static/img/icon.png


+ 63 - 0
static/js/common-utils.js

@@ -0,0 +1,63 @@
+class CommonUtils {
+	static selectWait(selector, func, times, interval) {
+		var _times = times || 100, //100次
+			_interval = interval || 500, //20毫秒每次
+			_jquery = null,
+			_iIntervalID;
+
+		_iIntervalID = setInterval(() => {
+			if (!_times) {
+				clearInterval(_iIntervalID);
+			}
+			_times <= 0 || _times--;
+			_jquery = $(selector);
+			if (_jquery.length) {
+				func && func.call(func);
+				clearInterval(_iIntervalID);
+			}
+		}, _interval);
+		return this;
+	}
+
+	static selectNotWait(selector, func, interval) {
+		let _jquery,
+			_interval = interval || 20,
+			_iIntervalID;
+
+		_iIntervalID = setInterval(() => {
+			_jquery = $(selector);
+			if (_jquery.length < 1) {
+				func && func.call(func);
+				clearInterval(_iIntervalID);
+			}
+		}, _interval);
+	}
+
+	static copyText(value, cb) {
+		const textarea = document.createElement("textarea");
+		textarea.readOnly = "readonly";
+		textarea.style.position = "absolute";
+		textarea.style.left = "-9999px";
+		textarea.value = value;
+		document.body.appendChild(textarea);
+		textarea.select();
+		textarea.setSelectionRange(0, textarea.value.length);
+		document.execCommand("Copy");
+		document.body.removeChild(textarea);
+		if (cb && Object.prototype.toString.call(cb) === "[object Function]") {
+			cb();
+		}
+	}
+
+	/**
+	 * 休眠
+	 * @param {number} ms 休眠多少毫秒
+	 */
+	static sleep(ms) {
+		return new Promise((resolve, reject) => {
+			setTimeout(() => {
+				resolve("完成");
+			}, ms);
+		});
+	}
+}

File diff suppressed because it is too large
+ 1 - 0
static/js/jquery-3.6.0.min.js


File diff suppressed because it is too large
+ 91 - 0
static/js/md5.js


Some files were not shown because too many files changed in this diff