VLC Station

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

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==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;
})();