VLC Station

Makes play buttons in Video Station generate VLC playlists rather than playing videos in the browser.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==UserScript==
// @name        VLC Station
// @namespace   VLCStation
// @description Makes play buttons in Video Station generate VLC playlists rather than playing videos in the browser.
// @include     /^https?://[^/]*/.*[?&]launchApp=SYNO.SDS.VideoStation.AppInstance(?=[&#]|$).*$/
// @version     7
// @grant       GM_registerMenuCommand
// ==/UserScript==

(function()
{
	'use strict';

	var DSRequest;
	var FileServer;
	var Playlist;
	var Problem;
	var Thumbnail;
	var VLCStation;
	var Platform;

	Platform = 
	{
		LINUX: navigator.platform.startsWith("Linux ")
	};

	Problem =
	{
		MESSAGE__HEADING:                    "VLC Station Error",
		MESSAGE_INVALID_REQUEST:             "An invalid request was made.",
		MESSAGE_NO_CONTACT:                  "Unable to contact the DiskStation.",
		MESSAGE_UNEXPECTED_RESPONSE_FORMAT:  "Unexpected response structure.",
		MESSAGE_UNKNOWN_THUMBNAIL_TYPE:      "Unknown thumbnail type.",
		MESSAGE_UNKNOWN_ITEM_FORMAT:         "Unknown item format.",

		EXTRA_STATUS_CODE:                   "Status code",
		EXTRA_ERROR_CODE:                    "Error code",
		EXTRA_FIELD:                         "Field",
		EXTRA_PATH_ELEMENT:                  "Path element",
		EXTRA_INDEX:                         "Index",
		EXTRA_TYPE:                          "Type",

		PREFIX_EXTRA_SYNOLOGY:               "Synology ",

		SEPARATOR_EXTRA:                     "\n",
		SEPARATOR_EXTRA_ITEM:                ": ",
		SEPARATOR_SECTION:                   "\n\n",

		report: function(message, extra)
		{
			var index;
			var buffer;

			buffer = [];
			if(extra)
				for(index in extra)
					buffer.push(index + Problem.SEPARATOR_EXTRA_ITEM + extra[index]);
			alert([Problem.MESSAGE__HEADING, message, buffer.join(Problem.SEPARATOR_EXTRA)].join(Problem.SEPARATOR_SECTION) + Problem.SEPARATOR_SECTION);

			return;
		}
	};


	(Thumbnail = function(element)
	{
		this._element = element;

		return;
	}).prototype =
	{
		_element: null,
		_thread: null,
		onload: null,
		id: null,
		token: null,
		type: null,

		_getImageURL: function()
		{
			return(this._element.style.backgroundImage || this._element.getAttribute(Thumbnail.ATTRIBUTE_URL));
		},

		_poll: function()
		{
			var url;

			url = this._getImageURL();
			if(!this._element.classList.contains(Thumbnail.CLASS_LOADING) && url)
			{
				clearInterval(this._thread);
				this._parse(url);
			}

			return;
		},

		_parse: function(url)
		{
			var index;
			var item;

			if(url.endsWith(Thumbnail.SUFFIX_CSS))
				url = url.substr(0, url.length - Thumbnail.SUFFIX_CSS.length);
			if(url.indexOf(Thumbnail.SEPARATOR_PARAMETER_LIST) !== -1)
			{
				url = url.substr(url.indexOf(Thumbnail.SEPARATOR_PARAMETER_LIST) + Thumbnail.SEPARATOR_PARAMETER_LIST.length).split(Thumbnail.SEPARATOR_PARAMETER);
				index = url.length;
				while(index--)
				{
					item = url[index].split(Thumbnail.SEPARATOR_VALUE);
					this[Thumbnail.PARAMETER[item[0]] || Thumbnail.PARAMETER_DEFAULT] = item[1];
				}
			}
			else
				Problem.report(Problem.MESSAGE_UNKNOWN_ITEM_FORMAT);
			if(this.onload)
				this.onload(this);

			return;
		},

		load: function()
		{
			var url;

			url = this._getImageURL();
			if(!url)
				this._thread = setInterval(this._poll.bind(this), Thumbnail.RATE_POLL);
			else
				this._parse(url);

			return;
		}
	};

	Thumbnail.PARAMETER =
	{
		id: "id",
		SynoToken: "token",
		type: "type"
	};

	Thumbnail.TYPE =
	{
		tvshow_episode:
		{
			api: "SYNO.VideoStation2.TVShowEpisode",
			infoContainer: "episode",
			preferredTitle: "tagline",
			episodic: true
		},
		movie:
		{
			api: "SYNO.VideoStation2.Movie",
			infoContainer: "movie"
		},
		home_video:
		{
			api: "SYNO.VideoStation2.HomeVideo",
			infoContainer: "video"
		}
	};

	Thumbnail.SEPARATOR_PARAMETER_LIST  = "?";
	Thumbnail.SEPARATOR_PARAMETER       = "&";
	Thumbnail.SEPARATOR_VALUE           = "=";

	Thumbnail.SUFFIX_CSS                = "\")";

	Thumbnail.PARAMETER_DEFAULT         = "last";

	Thumbnail.ATTRIBUTE_URL             = "url";

	Thumbnail.CLASS_LOADING             = "loading";

	Thumbnail.RATE_POLL                 = 50;


	(DSRequest = function(token)
	{
		this._request = new XMLHttpRequest();
		this._request.onreadystatechange = this._handleStateChange.bind(this);
		this._request.open(DSRequest.TYPE_POST, location.origin + DSRequest.URL_ENTRY_POINT);
		this._request.setRequestHeader(DSRequest.HEADER_TOKEN, token);
		this._parameter = {};

		return;
	}).prototype =
	{
		_request: null,
		_parameter: null,
		onsuccess: null,
		response: null,

		_handleStateChange: function()
		{
			var extra;
			var index;

			if(this._request.readyState === XMLHttpRequest.DONE)
			{
				if(this._request.status === DSRequest.STATUS_OK)
				{
					this.response = JSON.parse(this._request.responseText);
					if(this.response.success)
					{
						if(this.onsuccess)
							this.onsuccess(this);
					}
					else
					{
						extra = this.response.error.errors;
						for(index in extra)
						{
							extra[Problem.PREFIX_EXTRA_SYNOLOGY + index] = extra[index];
							delete extra[index];
						}
						extra[Problem.EXTRA_ERROR_CODE] = this.response.error.code;
						Problem.report(Problem.MESSAGE_INVALID_REQUEST, extra);
					}
				}
				else
				{
					extra = {};
					extra[Problem.EXTRA_STATUS_CODE] = this._request.status;
					Problem.report(Problem.MESSAGE_NO_CONTACT, extra);
				}
			}

			return;
		},

		setParameter: function(name, value)
		{
			this._parameter[name] = value;

			return;
		},

		send: function()
		{
			var index;
			var buffer;

			buffer = [];
			for(index in this._parameter)
				buffer.push(encodeURIComponent(index) + DSRequest.SEPARATOR_VALUE + encodeURIComponent(this._parameter[index]));
			this._request.send(buffer.join(DSRequest.SEPARATOR_PARAMETER));

			return;
		}
	};

	DSRequest.HEADER_TOKEN          = "X-SYNO-TOKEN";

	DSRequest.URL_ENTRY_POINT       = "/webapi/entry.cgi";

	DSRequest.TYPE_POST             = "POST";

	DSRequest.SEPARATOR_VALUE       = "=";
	DSRequest.SEPARATOR_PARAMETER   = "&";

	DSRequest.PARAMETER_ID          = "id";
	DSRequest.PARAMETER_ADDITIONAL  = "additional";
	DSRequest.PARAMETER_API         = "api";
	DSRequest.PARAMETER_METHOD      = "method";
	DSRequest.PARAMETER_VERSION     = "version";

	DSRequest.METHOD_GET_INFO       = "getinfo";

	DSRequest.ADDITION_FILE         = "file";

	DSRequest.STATUS_OK             = 200;

	DSRequest.VERSION_API           = "1";


	(Playlist = function()
	{
		this.list = [];

		return;
	}).prototype =
	{
		list: null,

		add: function(filename, title)
		{
			this.list.push({filename: filename, title: title});

			return;
		},

		addVideoEntry: function(entry, type)
		{
			var index;
			var list;
			var listLength;

			list = entry.additional.file.concat();
			list.sort(Playlist._compareSharepath);
			listLength = list.length;
			if(listLength > 1)
				for(index = 0; index < listLength; index++)
					this.add(Playlist._getVideoEntryFilename(list[index]), Playlist._getVideoEntryTitle(entry, type) + Playlist.PREFIX_TITLE_PART + (index + 1) + Playlist.SEPARATOR_TITLE_PART + listLength + Playlist.SUFFIX_TITLE_PART);
			else
				this.add(Playlist._getVideoEntryFilename(list[0]), Playlist._getVideoEntryTitle(entry, type));

			return;
		},

		addVideoEntryList: function(list, type)
		{
			var index;
			var listLength;

			listLength = list.length;
			for(index = 0; index < listLength; index++)
				this.addVideoEntry(list[index], type);

			return;
		},

		getPLSText: function()
		{
			var result;
			var listLength;
			var index;

			listLength = this.list.length;
			result = [Playlist.HEADER_PLS, Playlist._getPLSRecord(Playlist.FIELD_PLS_ENTRY_COUNT, listLength)];
			for(index = 0; index < listLength; index++)
			{
				result.push(Playlist._getPLSRecord(Playlist.PREIFX_PLS_FIELD_TITLE + (index + 1), this.list[index].title));
				result.push(Playlist._getPLSRecord(Playlist.PREIFX_PLS_FIELD_FILE + (index + 1), this.list[index].filename));
			}

			return(result.join(Playlist.SEPARATOR_PLS_RECORD) + Playlist.SEPARATOR_PLS_RECORD);
		}
	};

	Playlist._compareSharepath = function(alpha, beta)
	{
		alpha = alpha.sharepath.toUpperCase();
		beta = beta.sharepath.toUpperCase();

		return(alpha < beta ? -1 : (alpha > beta ? 1 : 0));
	};

	Playlist._getPLSRecord = function(name, value)
	{
		return(name + Playlist.SEPARATOR_PLS_VALUE + value);
	};

	Playlist._getVideoEntryTitle = function(entry, type)
	{
		var title;

		if(type.preferredTitle && entry[type.preferredTitle])
			title = entry[type.preferredTitle];
		else
			if(type.episodic)
				title = Playlist.PREFIX_TITLE_EPISODE + entry.episode;
			else
				title = entry.title;

		return(title);
	};

	Playlist._getVideoEntryFilename = Platform.LINUX
		?
			function(file)
			{
				return(Playlist.PREFIX_PATH_LINUX + location.hostname + file.sharepath);
			}
		:
			function(file)
			{
				return(Playlist.PREFIX_PATH_WINDOWS + (location.hostname + file.sharepath).replace(Playlist.PATTERN_PATH_SEPARATOR, Playlist.SEPARATOR_PATH));
			};

	Playlist.HEADER_PLS              = "[playlist]";

	Playlist.FIELD_PLS_ENTRY_COUNT   = "NumberOfEntries";

	Playlist.PREFIX_TITLE_PART       = " (";
	Playlist.PREFIX_TITLE_EPISODE    = "Episode ";
	Playlist.PREFIX_PATH_WINDOWS     = "\\\\";
	Playlist.PREFIX_PATH_LINUX       = "smb://";
	Playlist.PREIFX_PLS_FIELD_TITLE  = "Title";
	Playlist.PREIFX_PLS_FIELD_FILE   = "File";

	Playlist.SEPARATOR_TITLE_PART    = " of ";
	Playlist.SEPARATOR_PATH          = "\\";
	Playlist.SEPARATOR_PLS_RECORD    = "\n";
	Playlist.SEPARATOR_PLS_VALUE     = "=";

	Playlist.SUFFIX_TITLE_PART       = ")";

	Playlist.PATTERN_PATH_SEPARATOR  = new RegExp("/", "g");


	FileServer =
	{
		FILENAME_DEFAULT:    {},

		ELEMENT_ANCHOR:      "a",
		ELEMENT_IFRAME:      "iframe",

		ATTRIBUTE_DOWNLOAD:  "download",
		ATTRIBUTE_HREF:      "href",
		ATTRIBUTE_SRC:       "src",

		STYLE_VALUE_NONE:    "none",

		DELAY_SAFE_REMOVAL:  1000,

		PROTOCOL_DATA:       "data:",

		SEPARATOR_MIME:      ";",
		SEPARATOR_PRAGMA:    ",",

		PRAGMA_UTF8:         "charset=utf-8",
		PRAGMA_BASE64:       "base64",

		CONTENT_COMPANION:   "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",

		MIME_PLAYLIST:       "application/videolan",
		MIME_EXECUTABLE:     "application/octet-stream",

		FILENAME_COMPANION:  "vlc-full.exe",

		serve: function(filename, content, pragma, mime)
		{
			var element;
			var url;

			if(window.chrome || filename)
			{
				element = document.createElement(FileServer.ELEMENT_ANCHOR);
				element.style.display = FileServer.STYLE_VALUE_NONE;
				element.setAttribute(FileServer.ATTRIBUTE_HREF, FileServer._getDataURL(content, pragma, mime));
				element.setAttribute(FileServer.ATTRIBUTE_DOWNLOAD, filename || FileServer.FILENAME_DEFAULT[mime]);
				document.body.appendChild(element);
				element.click();
				document.body.removeChild(element);
			}
			else
			{
				filename = FileServer.FILENAME_DEFAULT[mime];
				element = document.createElement(FileServer.ELEMENT_IFRAME);
				element.style.display = FileServer.STYLE_VALUE_NONE;
				document.body.appendChild(element);
				if(filename && window.File && window.URL)
				{
					url = URL.createObjectURL(new File([content], filename, {type: mime}));
					element.setAttribute(FileServer.ATTRIBUTE_SRC, url);
				}
				else
					element.setAttribute(FileServer.ATTRIBUTE_SRC, FileServer._getDataURL(content, pragma, mime));
				setTimeout(this._cleanUpDownload.bind(this, element, url), FileServer.DELAY_SAFE_REMOVAL);
			}

			return;
		},

		serveText: function(filename, text, mime)
		{
			this.serve(filename, text, FileServer.PRAGMA_UTF8, mime);

			return;
		},

		serveCompanion: function()
		{
			this.serve(FileServer.FILENAME_COMPANION, FileServer.CONTENT_COMPANION, FileServer.PRAGMA_BASE64, FileServer.MIME_EXECUTABLE);

			return;
		}
	};

	FileServer._cleanUpDownload = function(element, url)
	{
		document.body.removeChild(element);
		if(url)
			URL.revokeObjectURL(url);

		return;
	};

	FileServer._getDataURL = function(content, pragma, mime)
	{
		return(FileServer.PROTOCOL_DATA + mime + FileServer.SEPARATOR_MIME + pragma + FileServer.SEPARATOR_PRAGMA + (pragma === FileServer.PRAGMA_BASE64 ? content : encodeURIComponent(content)));
	};

	FileServer.FILENAME_DEFAULT[FileServer.MIME_PLAYLIST] = "playlist.vlc";


	VLCStation =
	{
		start: function()
		{
			document.body.addEventListener(VLCStation.EVENT_CLICK, this, true);
			if(typeof(GM_registerMenuCommand) !== VLCStation.TYPE_UNDEFINED)
				GM_registerMenuCommand(VLCStation.MENU_ITEM_DOWNLOAD_COMPANION, FileServer.serveCompanion.bind(FileServer), VLCStation.MENU_ACCESS_DOWNLOAD_COMPANION);

			return;
		},

		handleEvent: function(event)
		{
			var button;

			switch(event.type)
			{
				case VLCStation.EVENT_CLICK:
					button = event.target;
					if(!this.play(event, button))
					{
						button = button.parentNode;
						if(button)
						{
							button = button.parentNode;
							if(button)
								this.play(event, button);
						}
					}
					break;
			}

			return;
		},

		play: function(event, button)
		{
			var source;
			var valid;

			valid = button.classList.contains(VLCStation.CLASS_PLAY_BUTTON);
			if(valid)
			{
				source = new Thumbnail(button.parentNode);
				source.onload = this.requestPlaylist.bind(this);
				source.load();
				event.stopPropagation();
			}

			return(valid);
		},

		requestPlaylist: function(thumbnail)
		{
			var playlist;
			var request;
			var type;

			type = Thumbnail.TYPE[thumbnail.type];
			if(type)
			{
				request = new DSRequest(thumbnail.token);
				request.onsuccess = function(request)
				{
					var element;
					var extra;
					var field;
					var index;
					var pathLength;
					var response;
					var test;

					response = request.response;
					field = type.infoContainer;
					test = response;
					pathLength = VLCStation.PATH_TEST_PLAYLIST.length;
					for(index = 0; index < pathLength; index++)
					{
						element = VLCStation.PATH_TEST_PLAYLIST[index];
						if(element === null)
							element = field;
						test = test[element];
						if(!test)
						{
							extra = {};
							extra[Problem.EXTRA_FIELD] = field;
							extra[Problem.EXTRA_PATH_ELEMENT] = VLCStation.PATH_TEST_PLAYLIST[index];
							extra[Problem.EXTRA_INDEX] = index;
							Problem.report(Problem.MESSAGE_UNEXPECTED_RESPONSE_FORMAT, extra);
							break;
						}
					}
					if(index === pathLength)
					{
						playlist = new Playlist();
						playlist.addVideoEntryList(response.data[field], type);
						FileServer.serveText(null, playlist.getPLSText(), FileServer.MIME_PLAYLIST);
					}

					return;
				};
				request.setParameter(DSRequest.PARAMETER_ID, JSON.stringify([+thumbnail.id]));
				request.setParameter(DSRequest.PARAMETER_ADDITIONAL, JSON.stringify([DSRequest.ADDITION_FILE]));
				request.setParameter(DSRequest.PARAMETER_API, type.api);
				request.setParameter(DSRequest.PARAMETER_METHOD, DSRequest.METHOD_GET_INFO);
				request.setParameter(DSRequest.PARAMETER_VERSION, DSRequest.VERSION_API);
				request.send();
			}
			else
			{
				extra = {};
				extra[Problem.EXTRA_TYPE] = thumbnail.type;
				Problem.report(Problem.MESSAGE_UNKNOWN_THUMBNAIL_TYPE, extra);
			}

			return;
		}
	};

	VLCStation.PATH_TEST_PLAYLIST              = ["data", null, 0, "additional", "file", 0];

	VLCStation.MENU_ACCESS_DOWNLOAD_COMPANION  = "D";
	VLCStation.MENU_ITEM_DOWNLOAD_COMPANION    = "Download VLC Full Screen Launcher (Windows)";

	VLCStation.CLASS_PLAY_BUTTON               = "play";

	VLCStation.EVENT_CLICK                     = "click";

	VLCStation.TYPE_UNDEFINED                  = "undefined";


	VLCStation.start();

	return;
})();