Index: src/wp-includes/css/media-views.css
===================================================================
--- src/wp-includes/css/media-views.css	(revision 27238)
+++ src/wp-includes/css/media-views.css	(working copy)
@@ -595,10 +595,6 @@
 	box-shadow: 0 4px 4px -4px rgba( 0, 0, 0, 0.1 );
 }
 
-.media-frame .media-toolbar .add-to-gallery {
-	display: none;
-}
-
 .media-frame-title h1 {
 	padding: 0 16px;
 	font-size: 22px;
@@ -1427,7 +1423,7 @@
 	margin: 1.4em 0 0.4em;
 }
 
-.gallery-settings {
+.collection-settings {
 	overflow: hidden;
 }
 
@@ -1886,7 +1882,7 @@
 		border-top: none;
 	}
 
-	.gallery-settings h3 {
+	.collection-settings h3 {
 		margin-top: 45px;
 	}
 
Index: src/wp-includes/js/media-editor.js
===================================================================
--- src/wp-includes/js/media-editor.js	(revision 27238)
+++ src/wp-includes/js/media-editor.js	(working copy)
@@ -319,7 +319,7 @@
 				}
 
 				if ( -1 !== jQuery.inArray( prop, ['playlist', 'video-playlist'] ) ) {
-					_.each(['tracknumbers', 'tracklist', 'images'], function (setting) {
+					_.each(['tracknumbers', 'tracklist', 'images', 'artists'], function (setting) {
 						if ( 'undefined' === typeof attrs[setting] ) {
 							attrs['_' + setting] = wp.media[ prop ].defaults[ setting ];
 						} else if ( 'true' === attrs[setting] || true === attrs[setting] ) {
@@ -395,7 +395,7 @@
 			}
 
 			if ( -1 !== jQuery.inArray( prop, ['playlist', 'video-playlist'] ) ) {
-				_.each(['tracknumbers', 'tracklist', 'images'], function (setting) {
+				_.each(['tracknumbers', 'tracklist', 'images', 'artists'], function (setting) {
 					if ( attrs['_' + setting] ) {
 						attrs[setting] = true;
 					} else {
@@ -558,6 +558,41 @@
  		}));
   	}());
 
+	wp.media.playlist = (function() {
+		var playlist = {
+			defaults : {
+				id: wp.media.view.settings.post.id,
+				style: 'light',
+				tracklist: true,
+				tracknumbers: true,
+				images: true,
+				artists: true
+			}
+		};
+
+		return _.extend(playlist, wp.media.collection.instance( 'playlist', {
+			type : 'audio',
+			title : wp.media.view.l10n.editPlaylistTitle
+		}));
+	}());
+
+	wp.media['video-playlist'] = (function() {
+		var playlist = {
+			defaults : {
+				id: wp.media.view.settings.post.id,
+				style: 'light',
+				tracklist: false,
+				tracknumbers: false,
+				images: false
+			}
+		};
+
+		return _.extend(playlist, wp.media.collection.instance( 'video-playlist', {
+			type : 'video',
+			title : wp.media.view.l10n.editVideoPlaylistTitle
+		}));
+	}());
+
 	/**
 	 * wp.media.featuredImage
 	 * @namespace
@@ -776,6 +811,14 @@
 				this.insert( wp.media.gallery.shortcode( selection ).string() );
 			}, this );
 
+			workflow.state('playlist-edit').on( 'update', function( selection ) {
+				this.insert( wp.media.playlist.shortcode( selection ).string() );
+			}, this );
+
+			workflow.state('video-playlist-edit').on( 'update', function( selection ) {
+				this.insert( wp.media['video-playlist'].shortcode( selection ).string() );
+			}, this );
+
 			workflow.state('embed').on( 'select', function() {
 				/**
 				 * @this wp.media.editor
@@ -1015,6 +1058,12 @@
 				if ( elem.hasClass( 'gallery' ) ) {
 					options.state = 'gallery';
 					options.title = wp.media.view.l10n.createGalleryTitle;
+				} else if ( elem.hasClass( 'playlist' ) ) {
+					options.state = 'playlist';
+					options.title = wp.media.view.l10n.createPlaylistTitle;
+				} else if ( elem.hasClass( 'video-playlist' ) ) {
+					options.state = 'video-playlist';
+					options.title = wp.media.view.l10n.createVideoPlaylistTitle;
 				}
 
 				wp.media.editor.open( editor, options );
Index: src/wp-includes/js/media-views.js
===================================================================
--- src/wp-includes/js/media-views.js	(revision 27238)
+++ src/wp-includes/js/media-views.js	(working copy)
@@ -941,6 +941,49 @@
 			title: l10n.addToGalleryTitle
 		}
 	});
+
+	// wp.media.controller.PlaylistEdit
+	// -------------------------------
+	media.controller.PlaylistEdit = media.controller.CollectionEdit( 'playlist', {
+		type: 'audio',
+		settings: 'Playlist',
+		dragInfoText: l10n.playlistDragInfo,
+		defaults: {
+			title: l10n.editPlaylistTitle,
+			dragInfo : false
+		}
+	});
+
+	// wp.media.controller.PlaylistAdd
+	// ---------------------------------
+	media.controller.PlaylistAdd = media.controller.CollectionAdd( 'playlist', {
+		type: 'audio',
+		defaults: {
+			title: l10n.addToPlaylistTitle
+		}
+	});
+
+	// wp.media.controller.VideoPlaylistEdit
+	// -------------------------------
+	media.controller.VideoPlaylistEdit = media.controller.CollectionEdit( 'video-playlist', {
+		type: 'video',
+		settings: 'Playlist',
+		dragInfoText: l10n.videoPlaylistDragInfo,
+		defaults: {
+			title: l10n.editVideoPlaylistTitle,
+			dragInfo : false
+		}
+	});
+
+	// wp.media.controller.VideoPlaylistAdd
+	// ---------------------------------
+	media.controller.VideoPlaylistAdd = media.controller.CollectionAdd( 'video-playlist', {
+		type: 'video',
+		defaults: {
+			title: l10n.addToVideoPlaylistTitle
+		}
+	});
+
 	/**
 	 * wp.media.controller.FeaturedImage
 	 *
@@ -1767,7 +1810,53 @@
 					menu:    'gallery'
 				}),
 
-				new media.controller.GalleryAdd()
+				new media.controller.GalleryAdd(),
+
+				new media.controller.Library({
+					id:         'playlist',
+					title:      l10n.createPlaylistTitle,
+					priority:   60,
+					toolbar:    'main-playlist',
+					filterable: 'uploaded',
+					multiple:   'add',
+					editable:   false,
+
+					library:  media.query( _.defaults({
+						type: 'audio'
+					}, options.library ) )
+				}),
+
+				// Playlist states.
+				new media.controller.PlaylistEdit({
+					library: options.selection,
+					editing: options.editing,
+					menu:    'playlist'
+				}),
+
+				new media.controller.PlaylistAdd(),
+
+				new media.controller.Library({
+					id:         'video-playlist',
+					title:      l10n.createVideoPlaylistTitle,
+					priority:   60,
+					toolbar:    'main-video-playlist',
+					filterable: 'uploaded',
+					multiple:   'add',
+					editable:   false,
+
+					library:  media.query( _.defaults({
+						type: 'video'
+					}, options.library ) )
+				}),
+
+				// Video Playlist states.
+				new media.controller.VideoPlaylistEdit({
+					library: options.selection,
+					editing: options.editing,
+					menu:    'video-playlist'
+				}),
+
+				new media.controller.VideoPlaylistAdd()
 			]);
 
 
@@ -1782,8 +1871,12 @@
 			 */
 			media.view.MediaFrame.Select.prototype.bindHandlers.apply( this, arguments );
 			this.on( 'menu:create:gallery', this.createMenu, this );
+			this.on( 'menu:create:playlist', this.createMenu, this );
+			this.on( 'menu:create:video-playlist', this.createMenu, this );
 			this.on( 'toolbar:create:main-insert', this.createToolbar, this );
-			this.on( 'toolbar:create:main-gallery', this.createToolbar, this );
+  			this.on( 'toolbar:create:main-gallery', this.createToolbar, this );
+ 			this.on( 'toolbar:create:main-playlist', this.createToolbar, this );
+ 			this.on( 'toolbar:create:main-video-playlist', this.createToolbar, this );
 			this.on( 'toolbar:create:featured-image', this.featuredImageToolbar, this );
 			this.on( 'toolbar:create:main-embed', this.mainEmbedToolbar, this );
 
@@ -1790,7 +1883,9 @@
 			var handlers = {
 				menu: {
 					'default': 'mainMenu',
-					'gallery': 'galleryMenu'
+					'gallery': 'galleryMenu',
+					'playlist': 'playlistMenu',
+					'video-playlist': 'videoPlaylistMenu'
 				},
 
 				content: {
@@ -1802,7 +1897,13 @@
 					'main-insert':      'mainInsertToolbar',
 					'main-gallery':     'mainGalleryToolbar',
 					'gallery-edit':     'galleryEditToolbar',
-					'gallery-add':      'galleryAddToolbar'
+					'gallery-add':      'galleryAddToolbar',
+					'main-playlist':	'mainPlaylistToolbar',
+					'playlist-edit':	'playlistEditToolbar',
+					'playlist-add':		'playlistAddToolbar',
+					'main-video-playlist': 'mainVideoPlaylistToolbar',
+					'video-playlist-edit': 'videoPlaylistEditToolbar',
+					'video-playlist-add': 'videoPlaylistAddToolbar'
 				}
 			};
 
@@ -1852,6 +1953,52 @@
 			});
 		},
 
+		playlistMenu: function( view ) {
+			var lastState = this.lastState(),
+				previous = lastState && lastState.id,
+				frame = this;
+
+			view.set({
+				cancel: {
+					text:     l10n.cancelPlaylistTitle,
+					priority: 20,
+					click:    function() {
+						if ( previous )
+							frame.setState( previous );
+						else
+							frame.close();
+					}
+				},
+				separateCancel: new media.View({
+					className: 'separator',
+					priority: 60
+				})
+			});
+		},
+
+		videoPlaylistMenu: function( view ) {
+			var lastState = this.lastState(),
+				previous = lastState && lastState.id,
+				frame = this;
+
+			view.set({
+				cancel: {
+					text:     l10n.cancelVideoPlaylistTitle,
+					priority: 20,
+					click:    function() {
+						if ( previous )
+							frame.setState( previous );
+						else
+							frame.close();
+					}
+				},
+				separateCancel: new media.View({
+					className: 'separator',
+					priority: 80
+				})
+			});
+		},
+
 		// Content
 		embedContent: function() {
 			var view = new media.view.Embed({
@@ -1970,6 +2117,58 @@
 			});
 		},
 
+		mainPlaylistToolbar: function( view ) {
+			var controller = this;
+
+			this.selectionStatusToolbar( view );
+
+			view.set( 'playlist', {
+				style:    'primary',
+				text:     l10n.createNewPlaylist,
+				priority: 100,
+				requires: { selection: true },
+
+				click: function() {
+					var selection = controller.state().get('selection'),
+						edit = controller.state('playlist-edit'),
+						models = selection.where({ type: 'audio' });
+
+					edit.set( 'library', new media.model.Selection( models, {
+						props:    selection.props.toJSON(),
+						multiple: true
+					}) );
+
+					this.controller.setState('playlist-edit');
+				}
+			});
+		},
+
+		mainVideoPlaylistToolbar: function( view ) {
+			var controller = this;
+
+			this.selectionStatusToolbar( view );
+
+			view.set( 'video-playlist', {
+				style:    'primary',
+				text:     l10n.createNewVideoPlaylist,
+				priority: 100,
+				requires: { selection: true },
+
+				click: function() {
+					var selection = controller.state().get('selection'),
+						edit = controller.state('video-playlist-edit'),
+						models = selection.where({ type: 'video' });
+
+					edit.set( 'library', new media.model.Selection( models, {
+						props:    selection.props.toJSON(),
+						multiple: true
+					}) );
+
+					this.controller.setState('video-playlist-edit');
+				}
+			});
+		},
+
 		featuredImageToolbar: function( toolbar ) {
 			this.createSelectToolbar( toolbar, {
 				text:  l10n.setFeaturedImage,
@@ -2038,8 +2237,115 @@
 					}
 				}
 			}) );
+		},
+
+		playlistEditToolbar: function() {
+			var editing = this.state().get('editing');
+			this.toolbar.set( new media.view.Toolbar({
+				controller: this,
+				items: {
+					insert: {
+						style:    'primary',
+						text:     editing ? l10n.updatePlaylist : l10n.insertPlaylist,
+						priority: 80,
+						requires: { library: true },
+
+						/**
+						 * @fires wp.media.controller.State#update
+						 */
+						click: function() {
+							var controller = this.controller,
+								state = controller.state();
+
+							controller.close();
+							state.trigger( 'update', state.get('library') );
+
+							// Restore and reset the default state.
+							controller.setState( controller.options.state );
+							controller.reset();
+						}
+					}
+				}
+			}) );
+		},
+
+		playlistAddToolbar: function() {
+			this.toolbar.set( new media.view.Toolbar({
+				controller: this,
+				items: {
+					insert: {
+						style:    'primary',
+						text:     l10n.addToPlaylist,
+						priority: 80,
+						requires: { selection: true },
+
+						/**
+						 * @fires wp.media.controller.State#reset
+						 */
+						click: function() {
+							var controller = this.controller,
+								state = controller.state(),
+								edit = controller.state('playlist-edit');
+
+							edit.get('library').add( state.get('selection').models );
+							state.trigger('reset');
+							controller.setState('playlist-edit');
+						}
+					}
+				}
+			}) );
+		},
+
+		videoPlaylistEditToolbar: function() {
+			var editing = this.state().get('editing');
+			this.toolbar.set( new media.view.Toolbar({
+				controller: this,
+				items: {
+					insert: {
+						style:    'primary',
+						text:     editing ? l10n.updateVideoPlaylist : l10n.insertVideoPlaylist,
+						priority: 140,
+						requires: { library: true },
+
+						click: function() {
+							var controller = this.controller,
+								state = controller.state();
+
+							controller.close();
+							state.trigger( 'update', state.get('library') );
+
+							// Restore and reset the default state.
+							controller.setState( controller.options.state );
+							controller.reset();
+						}
+					}
+				}
+			}) );
+		},
+
+		videoPlaylistAddToolbar: function() {
+			this.toolbar.set( new media.view.Toolbar({
+				controller: this,
+				items: {
+					insert: {
+						style:    'primary',
+						text:     l10n.addToVideoPlaylist,
+						priority: 140,
+						requires: { selection: true },
+
+						click: function() {
+							var controller = this.controller,
+								state = controller.state(),
+								edit = controller.state('video-playlist-edit');
+
+							edit.get('library').add( state.get('selection').models );
+							state.trigger('reset');
+							controller.setState('video-playlist-edit');
+						}
+					}
+				}
+			}) );
 		}
-
 	});
 
 	media.view.MediaFrame.ImageDetails = media.view.MediaFrame.Select.extend({
@@ -4864,11 +5170,25 @@
 	 * @augments Backbone.View
 	 */
 	media.view.Settings.Gallery = media.view.Settings.extend({
-		className: 'gallery-settings',
+		className: 'collection-settings gallery-settings',
 		template:  media.template('gallery-settings')
 	});
 
 	/**
+	 * wp.media.view.Settings.Playlist
+	 *
+	 * @constructor
+	 * @augments wp.media.view.Settings
+	 * @augments wp.media.View
+	 * @augments wp.Backbone.View
+	 * @augments Backbone.View
+	 */
+	media.view.Settings.Playlist = media.view.Settings.extend({
+		className: 'collection-settings playlist-settings',
+		template:  media.template('playlist-settings')
+	});
+
+	/**
 	 * wp.media.view.Attachment.Details
 	 *
 	 * @constructor
Index: src/wp-includes/js/mediaelement/wp-mediaelement.css
===================================================================
--- src/wp-includes/js/mediaelement/wp-mediaelement.css	(revision 27238)
+++ src/wp-includes/js/mediaelement/wp-mediaelement.css	(working copy)
@@ -13,3 +13,104 @@
 .me-cannotplay {
 	width: auto !important;
 }
+
+.wp-playlist {
+	border: 1px solid #ccc;
+	padding: 10px;
+	margin: 12px 0 18px;
+	font-size: 85%;
+	line-height: 160%;
+}
+
+.wp-playlist audio,
+.wp-playlist video {
+	display: inline-block;
+	max-width: 100%;
+}
+
+.wp-playlist .mejs-container {
+	margin: 0;
+	width: 100%;
+}
+
+.wp-playlist .mejs-controls .mejs-button button {
+	outline: 0;
+}
+
+.wp-playlist-light {
+	background: #fff;
+}
+
+.wp-playlist-dark {
+	color: #fff;
+	background: #000;
+}
+
+.wp-playlist-caption {
+	max-width: 85%;
+	overflow: hidden;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+}
+
+.wp-caption-meta {
+	display: block;
+}
+
+.wp-caption-title {
+	font-size: 100%;
+}
+
+.wp-caption-album {
+	font-style: italic;
+	overflow: hidden;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+}
+
+.wp-caption-artist {
+	font-size: 85%;
+	text-transform: uppercase;
+}
+
+.wp-caption-by {
+	font-size: 65%;
+	font-weight: bold;
+}
+
+.wp-playlist-item-length {
+	position: absolute;
+	right: 0;
+	top: 0;
+}
+
+.wp-playlist-tracks {
+	margin-top: 10px;
+	border-top: 1px solid #ccc;
+}
+
+.wp-playlist-item {
+	position: relative;
+	cursor: pointer;
+	border-bottom: 1px solid #ccc;
+}
+
+.wp-playlist-current-item {
+	overflow: hidden;
+	margin-bottom: 10px;
+	height: 60px;
+}
+
+.wp-playlist-current-item img {
+	float: left;
+	max-width: 60px;
+	height: auto;
+	margin-right: 10px;
+}
+
+.wp-playlist-current-item .wp-caption-title,
+.wp-playlist-current-item .wp-caption-artist {
+	overflow: hidden;
+	text-overflow: ellipsis;
+	white-space: nowrap;
+}
\ No newline at end of file
Index: src/wp-includes/js/mediaelement/wp-playlist.js
===================================================================
--- src/wp-includes/js/mediaelement/wp-playlist.js	(revision 0)
+++ src/wp-includes/js/mediaelement/wp-playlist.js	(working copy)
@@ -0,0 +1,130 @@
+/*globals window, document, $, jQuery */
+
+(function ($, _, Backbone) {
+	"use strict";
+
+	var WPPlaylistView = Backbone.View.extend({
+		index : 0,
+
+		itemTemplate : wp.template('wp-playlist-item'),
+
+		initialize : function () {
+			var settings = {};
+
+			this.data = $.parseJSON( this.$('script').html() );
+			this.playerNode = this.$( this.data.type );
+
+			this.tracks = new Backbone.Collection( this.data.tracks );
+			this.current = this.tracks.first();
+
+			if ( 'audio' === this.data.type ) {
+				this.currentTemplate = wp.template('wp-playlist-current-item');
+				this.currentNode = this.$( '.wp-playlist-current-item' );
+			}
+
+			this.renderCurrent();
+
+			if ( this.data.tracklist ) {
+				this.renderTracks();
+			}
+
+			this.playerNode.attr( 'src', this.current.get('src') );
+
+			_.bindAll( this, 'bindPlayer', 'ended', 'clickTrack' );
+
+			if ( typeof _wpmejsSettings !== 'undefined' ) {
+				settings.pluginPath = _wpmejsSettings.pluginPath;
+			}
+			settings.success = this.bindPlayer;
+
+			new MediaElementPlayer( this.playerNode.get(0), settings );
+		},
+
+		renderCurrent : function () {
+			var dimensions;
+			if ( 'video' === this.data.type ) {
+				if ( this.data.images && this.current.get( 'image' ) ) {
+					this.playerNode.attr( 'poster', this.current.get( 'image' ).src );
+				}
+				dimensions = this.current.get( 'dimensions' ).resized;
+				this.playerNode.attr( 'width', dimensions.width );
+				this.playerNode.attr( 'height', dimensions.height );
+			} else {
+				if ( ! this.data.images ) {
+					this.current.set( 'image', false );
+				}
+				this.currentNode.html( this.currentTemplate( this.current.toJSON() ) );
+			}
+		},
+
+		renderTracks : function () {
+			var that = this, i = 1, tracklist = $( '<div class="wp-playlist-tracks"></div>' );
+			this.tracks.each(function (model) {
+				if ( ! that.data.images ) {
+					model.set( 'image', false );
+				}
+				model.set( 'artists', that.data.artists );
+				model.set( 'index', that.data.tracknumbers ? i : false );
+				tracklist.append( that.itemTemplate( model.toJSON() ) );
+				i += 1;
+			});
+			this.$el.append( tracklist );
+		},
+
+		events : {
+			'click .wp-playlist-item' : 'clickTrack',
+			'click .wp-playlist-next' : 'next',
+			'click .wp-playlist-prev' : 'prev'
+		},
+
+		bindPlayer : function (mejs) {
+			this.player = mejs;
+			this.player.addEventListener( 'ended', this.ended );
+		},
+
+		clickTrack : function (e) {
+			this.index = this.$( '.wp-playlist-item' ).index( e.currentTarget );
+			this.setCurrent();
+		},
+
+		ended : function () {
+			if ( this.index + 1 < this.tracks.length ) {
+				this.next();
+			} else {
+				this.index = 0;
+				this.current = this.tracks.at( this.index );
+				this.loadCurrent();
+			}
+		},
+
+		next : function () {
+			this.index = this.index + 1 >= this.tracks.length ? 0 : this.index + 1;
+			this.setCurrent();
+		},
+
+		prev : function () {
+			this.index = this.index - 1 < 0 ? this.tracks.length - 1 : this.index - 1;
+			this.setCurrent();
+		},
+
+		loadCurrent : function () {
+			this.player.pause();
+			this.playerNode.attr( 'src', this.current.get( 'src' ) );
+			this.renderCurrent();
+			this.player.load();
+		},
+
+		setCurrent : function () {
+			this.current = this.tracks.at( this.index );
+			this.loadCurrent();
+			this.player.play();
+		}
+	});
+
+    $(document).ready(function () {
+		$('.wp-playlist').each(function () {
+			return new WPPlaylistView({ el: this });
+		});
+    });
+
+}(jQuery, _, Backbone));
\ No newline at end of file
Index: src/wp-includes/js/plupload/handlers.js
===================================================================
--- src/wp-includes/js/plupload/handlers.js	(revision 27238)
+++ src/wp-includes/js/plupload/handlers.js	(working copy)
@@ -22,7 +22,7 @@
 		.appendTo( jQuery('#media-items' ) );
 
 	// Disable submit
-	jQuery('#insert-gallery').prop('disabled', true);
+	jQuery('#insert-gallery, #insert-playlist').prop('disabled', true);
 }
 
 function uploadStart() {
@@ -64,11 +64,11 @@
 	// Just one file, no need for collapsible part
 	if ( items.length == 1 ) {
 		items.addClass('open').find('.slidetoggle').show();
-		jQuery('.insert-gallery').hide();
+		jQuery('.insert-gallery, .insert-playlist').hide();
 	} else if ( items.length > 1 ) {
 		items.removeClass('open');
-		// Only show Gallery button when there are at least two files.
-		jQuery('.insert-gallery').show();
+		// Only show Gallery/Playlist buttons when there are at least two files.
+		jQuery('.insert-gallery, .insert-playlist').show();
 	}
 
 	// Only show Save buttons when there is at least one file.
@@ -257,7 +257,7 @@
 }
 
 function uploadComplete() {
-	jQuery('#insert-gallery').prop('disabled', false);
+	jQuery('#insert-gallery, #insert-playlist').prop('disabled', false);
 }
 
 function switchUploader(s) {
Index: src/wp-includes/js/swfupload/handlers.js
===================================================================
--- src/wp-includes/js/swfupload/handlers.js	(revision 27238)
+++ src/wp-includes/js/swfupload/handlers.js	(working copy)
@@ -212,11 +212,12 @@
 	else
 		jQuery('.savebutton').hide();
 
-	// Only show Gallery button when there are at least two files.
-	if ( items.length > 1 )
-		jQuery('.insert-gallery').show();
-	else
-		jQuery('.insert-gallery').hide();
+	// Only show Gallery/Playlist buttons when there are at least two files.
+	if ( items.length > 1 ) {
+		jQuery('.insert-gallery, .insert-playlist').show();
+	} else {
+		jQuery('.insert-gallery, .insert-playlist').hide();
+	}
 }
 
 function uploadSuccess(fileObj, serverData) {
@@ -238,7 +239,7 @@
 	// If no more uploads queued, enable the submit button
 	if ( swfu.getStats().files_queued == 0 ) {
 		jQuery('#cancel-upload').prop('disabled', true);
-		jQuery('#insert-gallery').prop('disabled', false);
+		jQuery('#insert-gallery, #insert-playlist').prop('disabled', false);
 	}
 }
 
Index: src/wp-includes/js/tinymce/langs/wp-langs-en.js
===================================================================
--- src/wp-includes/js/tinymce/langs/wp-langs-en.js	(revision 27238)
+++ src/wp-includes/js/tinymce/langs/wp-langs-en.js	(working copy)
@@ -478,6 +478,8 @@
 		add_audio: "Add Audio",
 		editgallery: "Edit Gallery",
 		delgallery: "Delete Gallery",
+		editplaylist: "Edit Playlist",
+		delplaylist: "Delete Playlist",
 		wp_fullscreen_desc: "Distraction Free Writing mode (Alt + Shift + W)"
 	});
 
Index: src/wp-includes/js/tinymce/plugins/wpgallery/plugin.js
===================================================================
--- src/wp-includes/js/tinymce/plugins/wpgallery/plugin.js	(revision 27238)
+++ src/wp-includes/js/tinymce/plugins/wpgallery/plugin.js	(working copy)
@@ -25,8 +25,8 @@
 	}
 
 	function replaceAVShortcodes( content ) {
-		var testRegex = /\[(audio|video)[^\]]*\]/,
-			replaceRegex = /\[(audio|video)[^\]]*\]([\s\S]*?\[\/\1\])?/;
+		var testRegex = /\[(video-playlist|audio|video|playlist)[^\]]*\]/,
+			replaceRegex = /\[(video-playlist|audio|video|playlist)[^\]]*\]([\s\S]*?\[\/\1\])?/;
 
 		while ( testRegex.test( content ) ) {
 			content = content.replace( replaceRegex, replaceCallback );
@@ -60,12 +60,12 @@
 		}
 
 		// Check if the `wp.media.gallery` API exists.
-		if ( typeof wp === 'undefined' || ! wp.media || ! wp.media.gallery ) {
+		if ( typeof wp === 'undefined' || ! wp.media ) {
 			return;
 		}
 
 		// Make sure we've selected a gallery node.
-		if ( editor.dom.hasClass( node, 'wp-gallery' ) ) {
+		if ( editor.dom.hasClass( node, 'wp-gallery' ) && wp.media.gallery ) {
 			gallery = wp.media.gallery;
 			data = window.decodeURIComponent( editor.dom.getAttrib( node, 'data-wp-media' ) );
 			frame = gallery.edit( data );
@@ -74,6 +74,22 @@
 				var shortcode = gallery.shortcode( selection ).string();
 				editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) );
 			});
+		} else if ( editor.dom.hasClass( node, 'wp-playlist' ) && wp.media.playlist ) {
+			data = window.decodeURIComponent( editor.dom.getAttrib( node, 'data-wp-media' ) );
+			frame = wp.media.playlist.edit( data );
+
+			frame.state('playlist-edit').on( 'update', function( selection ) {
+				var shortcode = wp.media.playlist.shortcode( selection ).string();
+				editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) );
+			});
+		} else if ( editor.dom.hasClass( node, 'wp-video-playlist' ) && wp.media['video-playlist'] ) {
+			data = window.decodeURIComponent( editor.dom.getAttrib( node, 'data-wp-media' ) );
+			frame = wp.media['video-playlist'].edit( data );
+
+			frame.state('video-playlist-edit').on( 'update', function( selection ) {
+				var shortcode = wp.media['video-playlist'].shortcode( selection ).string();
+				editor.dom.setAttrib( node, 'data-wp-media', window.encodeURIComponent( shortcode ) );
+			});
 		} else {
 			// temp
 			window.console && window.console.log( 'Edit AV shortcode ' + window.decodeURIComponent( editor.dom.getAttrib( node, 'data-wp-media' ) ) );
@@ -138,6 +154,10 @@
 				event.name = 'video';
 			} else if ( dom.hasClass( node, 'wp-audio' ) ) {
 				event.name = 'audio';
+			} else if ( dom.hasClass( node, 'wp-playlist' ) ) {
+				event.name = 'playlist';
+			} else if ( dom.hasClass( node, 'wp-video-playlist' ) ) {
+				event.name = 'video-playlist';
 			}
 		}
 	});
Index: src/wp-includes/js/tinymce/skins/wordpress/images/playlist-audio.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: src/wp-includes/js/tinymce/skins/wordpress/images/playlist-audio.png
===================================================================
--- src/wp-includes/js/tinymce/skins/wordpress/images/playlist-audio.png	(revision 0)
+++ src/wp-includes/js/tinymce/skins/wordpress/images/playlist-audio.png	(working copy)

Property changes on: src/wp-includes/js/tinymce/skins/wordpress/images/playlist-audio.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: src/wp-includes/js/tinymce/skins/wordpress/images/playlist-video.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: src/wp-includes/js/tinymce/skins/wordpress/images/playlist-video.png
===================================================================
--- src/wp-includes/js/tinymce/skins/wordpress/images/playlist-video.png	(revision 0)
+++ src/wp-includes/js/tinymce/skins/wordpress/images/playlist-video.png	(working copy)

Property changes on: src/wp-includes/js/tinymce/skins/wordpress/images/playlist-video.png
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: src/wp-includes/js/tinymce/skins/wordpress/wp-content.css
===================================================================
--- src/wp-includes/js/tinymce/skins/wordpress/wp-content.css	(revision 27238)
+++ src/wp-includes/js/tinymce/skins/wordpress/wp-content.css	(working copy)
@@ -143,6 +143,14 @@
 	background-image: url("images/audio.png");
 }
 
+.mce-content-body img.wp-media.wp-playlist {
+	background-image: url("images/playlist-audio.png");
+}
+
+.mce-content-body img.wp-media.wp-video-playlist {
+	background-image: url("images/playlist-video.png");
+}
+
 #wp-image-toolbar {
 	position: absolute;
 }
Index: src/wp-includes/media-template.php
===================================================================
--- src/wp-includes/media-template.php	(revision 27238)
+++ src/wp-includes/media-template.php	(working copy)
@@ -411,6 +411,47 @@
 		</label>
 	</script>
 
+	<script type="text/html" id="tmpl-playlist-settings">
+		<h3><?php _e('Playlist Settings'); ?></h3>
+
+		<label class="setting">
+			<span><?php _e( 'Random Order' ); ?></span>
+			<input type="checkbox" data-setting="_orderbyRandom" />
+		</label>
+
+		<label class="setting">
+			<span><?php _e('Style'); ?></span>
+			<select class="style" data-setting="style">
+				<option value="light">
+					<?php esc_attr_e('Light'); ?>
+				</option>
+				<option value="dark">
+					<?php esc_attr_e('Dark'); ?>
+				</option>
+			</select>
+		</label>
+
+		<label class="setting">
+			<span><?php _e( 'Show Tracklist' ); ?></span>
+			<input type="checkbox" data-setting="_tracklist" />
+		</label>
+
+		<label class="setting">
+			<span><?php _e( 'Show Track Numbers' ); ?></span>
+			<input type="checkbox" data-setting="_tracknumbers" />
+		</label>
+
+		<label class="setting">
+			<span><?php _e( 'Show Artist Name in Tracklist' ); ?></span>
+			<input type="checkbox" data-setting="_artists" />
+		</label>
+
+		<label class="setting">
+			<span><?php _e( 'Show Images' ); ?></span>
+			<input type="checkbox" data-setting="_images" />
+		</label>
+	</script>
+
 	<script type="text/html" id="tmpl-embed-link-settings">
 		<label class="setting">
 			<span><?php _e('Title'); ?></span>
Index: src/wp-includes/media.php
===================================================================
--- src/wp-includes/media.php	(revision 27238)
+++ src/wp-includes/media.php	(working copy)
@@ -934,6 +934,242 @@
 }
 
 /**
+ * The Playlist shortcode.
+ *
+ * This implements the functionality of the Playlist Shortcode for displaying
+ * a collection of WordPress audio or video files in a post.
+ *
+ * @since 3.9.0
+ *
+ * @param array $attr Attributes of the shortcode.
+ * @return string $type Type of playlist. Defaults to audio, video is also supported
+ */
+function wp_get_playlist( $attr, $type ) {
+	global $content_width;
+	$post = get_post();
+
+	if ( ! in_array( $type, array( 'audio', 'video' ) ) ) {
+		return '';
+	}
+
+	static $instance = 0;
+	$instance++;
+
+	if ( ! empty( $attr['ids'] ) ) {
+		// 'ids' is explicitly ordered, unless you specify otherwise.
+		if ( empty( $attr['orderby'] ) ) {
+			$attr['orderby'] = 'post__in';
+		}
+		$attr['include'] = $attr['ids'];
+	}
+
+	// Allow plugins/themes to override the default gallery template.
+	$output = apply_filters( 'post_playlist', '', $attr, $type );
+	if ( $output != '' ) {
+		return $output;
+	}
+
+	// We're trusting author input, so let's at least make sure it looks like a valid orderby statement
+	if ( isset( $attr['orderby'] ) ) {
+		$attr['orderby'] = sanitize_sql_orderby( $attr['orderby'] );
+		if ( ! $attr['orderby'] )
+			unset( $attr['orderby'] );
+	}
+
+	extract( shortcode_atts( array(
+		'order'		=> 'ASC',
+		'orderby'	=> 'menu_order ID',
+		'id'		=> $post ? $post->ID : 0,
+		'include'	=> '',
+		'exclude'   => '',
+		'style'		=> 'light',
+		'tracklist' => 'audio' === $type,
+		'tracknumbers' => 'audio' === $type,
+		'images'	=> true,
+		'artists'	=> true
+	), $attr, 'playlist' ) );
+
+	$id = intval( $id );
+	if ( 'RAND' == $order ) {
+		$orderby = 'none';
+	}
+
+	$args = array(
+		'post_status' => 'inherit',
+		'post_type' => 'attachment',
+		'post_mime_type' => $type,
+		'order' => $order,
+		'orderby' => $orderby
+	);
+
+	if ( ! empty( $include ) ) {
+		$args['include'] = $include;
+		$_attachments = get_posts( $args );
+
+		$attachments = array();
+		foreach ( $_attachments as $key => $val ) {
+			$attachments[$val->ID] = $_attachments[$key];
+		}
+	} elseif ( ! empty( $exclude ) ) {
+		$args['post_parent'] = $id;
+		$args['exclude'] = $exclude;
+		$attachments = get_children( $args );
+	} else {
+		$args['post_parent'] = $id;
+		$attachments = get_children( $args );
+	}
+
+	if ( empty( $attachments ) ) {
+		return '';
+	}
+
+	if ( is_feed() ) {
+		$output = "\n";
+		foreach ( $attachments as $att_id => $attachment ) {
+			$output .= wp_get_attachment_link( $att_id ) . "\n";
+		}
+		return $output;
+	}
+
+	$supports_thumbs = ( current_theme_supports( 'post-thumbnails', "attachment:$type" ) && post_type_supports( "attachment:$type", 'thumbnail' ) )
+		|| $images;
+
+	$outer = 22; // default padding and border of wrapper
+	$theme_width = $content_width - $outer;
+	$data = compact( 'type', 'style' );
+
+	// don't pass strings to JSON, will be truthy in JS
+	foreach ( array( 'tracklist', 'tracknumbers', 'images', 'artists' ) as $key ) {
+		$data[$key] = filter_var( $$key, FILTER_VALIDATE_BOOLEAN );
+	}
+
+	$tracks = array();
+	foreach ( $attachments as $attachment ) {
+		$url = wp_get_attachment_url( $attachment->ID );
+		$ftype = wp_check_filetype( $url, wp_get_mime_types() );
+		$track = array(
+			'type' => $type,
+			'src' => $url,
+			'type' => $ftype['ext'],
+			'title' => get_the_title( $attachment->ID ),
+			'caption' => wptexturize( $attachment->post_excerpt ),
+			'description' => wptexturize( $attachment->post_content )
+		);
+
+		$meta = wp_get_attachment_metadata( $attachment->ID );
+		if ( ! empty( $meta ) ) {
+			$track['meta'] = array();
+
+			$keys = array( 'title', 'artist', 'band', 'album', 'genre', 'year', 'length', 'length_formatted' );
+			foreach ( $keys as $key ) {
+				if ( ! empty( $meta[ $key ] ) ) {
+					$track['meta'][ $key ] = $meta[ $key ];
+				}
+			}
+
+			if ( 'video' === $type ) {
+				$width = empty( $meta['width'] ) ? 640 : $meta['width'];
+				$height = empty( $meta['height'] ) ? 360 : $meta['height'];
+				$theme_height = round( ( $height * $theme_width ) / $width );
+				$track['dimensions'] = array(
+					'original' => compact( 'width', 'height' ),
+					'resized' => array(
+						'width' => $theme_width,
+						'height' => $theme_height
+					)
+				);
+			}
+		}
+
+		if ( $supports_thumbs ) {
+			$id = get_post_thumbnail_id( $attachment->ID );
+			if ( ! empty( $id ) ) {
+				list( $src, $width, $height ) = wp_get_attachment_image_src( $id, 'full' );
+				$track['image'] = compact( 'src', 'width', 'height' );
+				list( $src, $width, $height ) = wp_get_attachment_image_src( $id, 'thumb' );
+				$track['thumb'] = compact( 'src', 'width', 'height' );
+			}
+		}
+
+		$tracks[] = $track;
+	}
+	$data['tracks'] = $tracks;
+
+	ob_start();
+
+	if ( 1 === $instance ):
+		wp_enqueue_style( 'wp-mediaelement' );
+		wp_enqueue_script( 'wp-playlist' );
+?>
+<!--[if lt IE 9]><script>document.createElement('<?php echo $type ?>');</script><![endif]-->
+<script type="text/html" id="tmpl-wp-playlist-current-item">
+	<# if ( data.image ) { #>
+	<img src="{{{ data.thumb.src }}}"/>
+	<# } #>
+	<# if ( data.meta.title ) { #>
+	<div class="wp-playlist-caption">
+		<span class="wp-caption-meta wp-caption-title">&#8220;{{{ data.meta.title }}}&#8221;</span>
+		<span class="wp-caption-meta wp-caption-album">{{{ data.meta.album }}}</span>
+		<span class="wp-caption-meta wp-caption-artist">{{{ data.meta.artist }}}</span>
+	</div>
+	<# } else { #>
+	<div class="wp-playlist-caption">{{{ data.caption }}}</div>
+	<# } #>
+</script>
+<script type="text/html" id="tmpl-wp-playlist-item">
+	<div class="wp-playlist-item">
+		<# if ( ( data.title || data.meta.title ) && ( ! data.artists || data.meta.artist ) ) { #>
+		<div class="wp-playlist-caption">
+			{{{ data.index ? ( data.index + '.&nbsp;' ) : '' }}}
+			<span class="wp-caption-title">&#8220;{{{ data.title ? data.title : data.meta.title }}}&#8221;</span>
+			<# if ( data.artists ) { #>
+			<span class="wp-caption-by"><?php _e( 'by' ) ?></span>
+			<span class="wp-caption-artist">{{{ data.meta.artist }}}</span>
+			<# } #>
+		</div>
+		<# } else { #>
+		<div class="wp-playlist-caption">{{{ data.index ? ( data.index + '.' ) : '' }}} {{{ data.caption ? data.caption : data.title }}}</div>
+		<# } #>
+		<# if ( data.meta.length_formatted ) { #>
+		<div class="wp-playlist-item-length">{{{ data.meta.length_formatted }}}</div>
+		<# } #>
+	</div>
+</script>
+	<?php endif ?>
+<div class="wp-playlist wp-<?php echo $type ?>-playlist wp-playlist-<?php echo $style ?>">
+	<?php if ( 'audio' === $type ): ?>
+	<div class="wp-playlist-current-item"></div>
+	<?php endif ?>
+	<<?php echo $type ?> controls="controls" preload="metadata" width="<?php echo $content_width - $outer ?>"></<?php echo $type ?>>
+	<div class="wp-playlist-next"></div>
+	<div class="wp-playlist-prev"></div>
+	<noscript>
+	<?php
+	$output = "\n";
+	foreach ( $attachments as $att_id => $attachment ) {
+		$output .= wp_get_attachment_link( $att_id ) . "\n";
+	}
+
+	echo $output;
+	?>
+	</noscript>
+	<script type="application/json"><?php echo json_encode( $data ) ?></script>
+</div>
+	<?php
+	return ob_get_clean();
+}
+
+function wp_playlist_shortcode( $attr ) {
+	return wp_get_playlist( $attr, 'audio' );
+}
+add_shortcode( 'playlist', 'wp_playlist_shortcode' );
+
+function wp_video_playlist_shortcode( $attr ) {
+	return wp_get_playlist( $attr, 'video' );
+}
+add_shortcode( 'video-playlist', 'wp_video_playlist_shortcode' );
+
+/**
  * Provide a No-JS Flash fallback as a last resort for audio / video
  *
  * @since 3.6.0
@@ -2044,6 +2280,8 @@
 		'mediaLibraryTitle'  => __( 'Media Library' ),
 		'insertMediaTitle'   => __( 'Insert Media' ),
 		'createNewGallery'   => __( 'Create a new gallery' ),
+		'createNewPlaylist'   => __( 'Create a new playlist' ),
+		'createNewVideoPlaylist'   => __( 'Create a new video playlist' ),
 		'returnToLibrary'    => __( '&#8592; Return to library' ),
 		'allMediaItems'      => __( 'All media items' ),
 		'noItemsFound'       => __( 'No items found.' ),
@@ -2071,7 +2309,27 @@
 		// Edit Image
 		'imageDetailsTitle'     => __( 'Image Details' ),
 		'imageReplaceTitle'     => __( 'Replace Image' ),
-		'imageDetailsCancel'     => __( 'Cancel Edit' )
+		'imageDetailsCancel'    => __( 'Cancel Edit' ),
+
+ 		// Playlist
+ 		'playlistDragInfo'    => __( 'Drag and drop to reorder tracks.' ),
+ 		'createPlaylistTitle' => __( 'Create Playlist' ),
+ 		'editPlaylistTitle'   => __( 'Edit Playlist' ),
+ 		'cancelPlaylistTitle' => __( '&#8592; Cancel Playlist' ),
+ 		'insertPlaylist'      => __( 'Insert playlist' ),
+ 		'updatePlaylist'      => __( 'Update playlist' ),
+ 		'addToPlaylist'       => __( 'Add to playlist' ),
+ 		'addToPlaylistTitle'  => __( 'Add to Playlist' ),
+
+ 		// Video Playlist
+ 		'videoPlaylistDragInfo'    => __( 'Drag and drop to reorder videos.' ),
+ 		'createVideoPlaylistTitle' => __( 'Create Video Playlist' ),
+ 		'editVideoPlaylistTitle'   => __( 'Edit Video Playlist' ),
+ 		'cancelVideoPlaylistTitle' => __( '&#8592; Cancel Video Playlist' ),
+ 		'insertVideoPlaylist'      => __( 'Insert video playlist' ),
+ 		'updateVideoPlaylist'      => __( 'Update video playlist' ),
+ 		'addToVideoPlaylist'       => __( 'Add to video playlist' ),
+ 		'addToVideoPlaylistTitle'  => __( 'Add to Video Playlist' ),
 	);
 
 	$settings = apply_filters( 'media_view_settings', $settings, $post );
Index: src/wp-includes/script-loader.php
===================================================================
--- src/wp-includes/script-loader.php	(revision 27238)
+++ src/wp-includes/script-loader.php	(working copy)
@@ -315,6 +315,8 @@
 		'pluginPath' => includes_url( 'js/mediaelement/', 'relative' ),
 	) );
 
+	$scripts->add( 'wp-playlist', "/wp-includes/js/mediaelement/wp-playlist.js", array( 'wp-util', 'backbone', 'mediaelement' ), false, 1 );
+
 	$scripts->add( 'zxcvbn-async', "/wp-includes/js/zxcvbn-async$suffix.js", array(), '1.0' );
 	did_action( 'init' ) && $scripts->localize( 'zxcvbn-async', '_zxcvbnSettings', array(
 		'src' => empty( $guessed_url ) ? includes_url( '/js/zxcvbn.min.js' ) : $scripts->base_url . '/wp-includes/js/zxcvbn.min.js',
