39

I have to create a text box which only allows you tube url for videos.

To handle the validation in server side, I am using below code

$rx = '~
    ^(?:https?://)?              # Optional protocol
     (?:www\.)?                  # Optional subdomain
     (?:youtube\.com|youtu\.be)  # Mandatory domain name
     /watch\?v=([^&]+)           # URI with video id as capture group 1
     ~x';

$has_match = preg_match($rx, $url, $matches);

I was looking for the same solution for client side validation. I found about <input type="url"> here but it seems it is only for html5 browsers.

Is it possible to do client side validation with text box so that it will be compatible with all browser?

Thanks

Community
  • 1
  • 1
Hitesh
  • 4,098
  • 11
  • 44
  • 82
  • Hitesh, you should mark as answer to any of three if anyone works for you, so that others can trust on that. – Jitendra Pancholi Feb 26 '15 at 08:50
  • having hard time choosing which one is right !!! both @innomanik and your answer seems to work as I expected . Give me some time I will accept right answer. – Hitesh Feb 26 '15 at 09:05

5 Answers5

80

Here is the code which validates the youtube url-

function validateYouTubeUrl()
{
    var url = $('#youTubeUrl').val();
        if (url != undefined || url != '') {
            var regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=|\?v=)([^#\&\?]*).*/;
            var match = url.match(regExp);
            if (match && match[2].length == 11) {
                // Do anything for being valid
                // if need to change the url to embed url then use below line
                $('#ytplayerSide').attr('src', 'https://www.youtube.com/embed/' + match[2] + '?autoplay=0');
            }
            else {
                // Do anything for not being valid
            }
        }
}

Fiddle Url: https://jsfiddle.net/cpjushnn/12/

Manik Arora
  • 4,702
  • 1
  • 25
  • 48
  • 3
    WOW !!! you even added how to change the url to embed url ... pls provide jsfiddle if possible. Thanks – Hitesh Feb 26 '15 at 06:46
  • @hitesh, just added the fiddle url as well, try it and mark as answer, if this was it... – Manik Arora Feb 26 '15 at 07:06
  • `if (match && match[2].length == 11) {`... whats the logic behind this one i dont understand ... can you please explain ... thanks – Hitesh Feb 26 '15 at 09:08
  • "match" specifies that the regex has a match with the string or not and the "match[2].length" specifies length of the second parentheses match should always be 11 characters. As youtube urls have 11 character code for the videos and it will remain 11 characters always like this- v=oB1CUxX1JJE or you can see this answer - http://stackoverflow.com/a/6250619/3748701 – Manik Arora Feb 26 '15 at 09:13
  • Sorry to bother you again and again but just one last question , in your regex expression I dont see any expression added for `youtube.com` Eg. `|youtube\.com\/` but it is still working as expected !!! how ? – Hitesh Feb 26 '15 at 10:34
  • Actually youtube has several url formats like youtube.com, youtu.be this regular expression will validate and extract the video ID from any YouTube URL including shortened URLs (youtu.be), watch URLS (youtube.com/watch?) and embed URLs (youtube.com/embed?). – Manik Arora Feb 26 '15 at 10:48
  • Thank you so much for explaining. I have accepted your answer as right answer – Hitesh Feb 26 '15 at 10:56
  • @ManikArora [^#\&\?]* It looks like this piece is having some special meaning which I am not able to figure out. Can you please help me? – user3276435 Aug 11 '16 at 09:27
  • 2
    @Prateek, this means that section should match a single character not present in the list i.e. #, & and ? as these characters have special meaning/use in a URL. You can play more over this here - https://regex101.com/r/pN5hU5/1 – Manik Arora Aug 11 '16 at 11:31
  • this will mark "https://www.youuuuuuuutube.com/watch?v=furTlhb-990" and other malformatted links valid as well (an replace them with the id from it for embedding correctly though). I recommend the solution below by Jitendra – ForestG Nov 23 '17 at 11:33
  • 1
    Cleaner version: `/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=|\?v=)([^#&?]*).*/` – Amir Savand Jan 04 '19 at 22:33
37

See this working example:

function matchYoutubeUrl(url) {
    var p = /^(?:https?:\/\/)?(?:m\.|www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;
    if(url.match(p)){
        return url.match(p)[1];
    }
    return false;
}

Updated Fiddle: http://jsfiddle.net/3ouq9u3v/13/

webrama.pl
  • 1,870
  • 1
  • 23
  • 36
Jitendra Pancholi
  • 7,897
  • 12
  • 51
  • 84
  • 1
    it is working as expected and I am glad you you added the working link .. thank you so much – Hitesh Feb 26 '15 at 06:45
  • Thanks for sharing the information that RegExp properties are deprecated but I'm surprised why microsoft doesn't mentioned that. I updated my solution now. – Jitendra Pancholi Feb 26 '15 at 10:17
5

Updating this again. (Again 07/September/2022) As I see it there are 7 scenarios to cater for that I have identified so far (I made up these names):

Normal Url

  1. https://www.youtube.com/watch?v=12345678901

Share Url

  1. https://youtu.be/12345678901

Share Url with start time

  1. https://youtu.be/12345678901?t=6

Mobile browser url

  1. https://m.youtube.com/watch?v=12345678901&list=RD12345678901&start_radio=1

Long url

  1. https://www.youtube.com/watch?v=12345678901&list=RD12345678901&start_radio=1&rv=smKgVuS

Long url with start time

  1. https://www.youtube.com/watch?v=12345678901&list=RD12345678901&start_radio=1&rv=12345678901&t=38

Youtube Shorts

  1. https://youtube.com/shorts/12345678901

I originally offered an expanded answer based off Jitendras answer as that seems to have some gaps. For example with the shorter sharing url such as https://youtu.be/abcdefgh_ij if you added characters onto the end, Jitendras did not catch it. Also, if the user deleted the value in a text field while an error was already present, it was not removed.

However, I'm assuming the original poster actually wanted to use the URL in an iframe embed, and none of the answers here will work for that. The embedded url is formatted differently and needs to be processed.

Below is what I am doing to take the users input, validate it, replace it with a correctly formed embeddable url, and then assign it to an iframe for display on the UI.

Note: The below will handle all of the above, but it will not display all of the above. i.e. It will format a start-time url to embed it, but you will lose the start-time. But a little customisation I'm sure you could handle that.

Might be of benefit to someone....

function validateVideoLinkTitle() {

    document.getElementById('TitleImageUrl').value = null;
    document.getElementById('TitleImageValidationField').value = null;
    document.getElementById('CardDynamicImage').src = '/images/cardimage.png';

    var ArticleTitleVideoField = document.getElementById("ArticleTitleVideoField");
    var VideoLinkText = ArticleTitleVideoField.value;
    var ArticleTitleVideoValidation = document.getElementById("ArticleTitleVideoValidation");
    var ArticleTitleImageSizeValidation = document.getElementById("ArticleTitleImageSizeValidation");
    ArticleTitleImageSizeValidation.style.display = 'none';
    var ArticleTitleImageTypeValidation = document.getElementById("ArticleTitleImageTypeValidation");
    ArticleTitleImageTypeValidation.style.display = 'none';
    var CardDynamicImageDiv = document.getElementById("CardDynamicImageDiv");
    CardDynamicImageDiv.style.display = 'none';
    var CardDynamicVideoDiv = document.getElementById("CardDynamicVideoDiv");
    CardDynamicVideoDiv.style.display = 'block';

    var urlType = 0;

    if ((VideoLinkText.includes("watch?v=") || VideoLinkText.includes("https://m.youtube") || VideoLinkText.includes("watch?app=desktop&v=")) && !VideoLinkText == "") {
        urlType = 1;
    } else if ((VideoLinkText.includes("embed") && !VideoLinkText == "")) {
        urlType = 2;
    } else if ((VideoLinkText.includes("tu.be") && !VideoLinkText == "")) {
        urlType = 3;
    }
    console.log('title urlType:' + urlType);

    switch (urlType) {
        case 1:
            var endOfString = VideoLinkText.split(/=(.*)/)[1];
            var stringLength = endOfString.length;
            console.log('title stringLength 1: ' + stringLength);

            var p = /^(?:https?:\/\/)?(?:m\.|www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;
            if (!VideoLinkText.match(p)) {
                ArticleTitleVideoValidation.style.display = 'block';
                $("#TitleImageValidationField").val("");
                document.getElementById('CardDynamicVideo').src = '';
                CardDynamicImageDiv.style.display = 'block';
                CardDynamicVideoDiv.style.display = 'none';
            } else {
                ArticleTitleVideoValidation.style.display = 'none';
                $("#TitleImageValidationField").val("Ok");
                console.log("title VideoLinkText 1: " + VideoLinkText)
                var processedUrl = titleProcessVideoUrl(VideoLinkText, true);
                document.getElementById('CardDynamicVideo').src = processedUrl;
                $("#ArticleTitleVideoField").val(processedUrl);
            }

            if (stringLength > 11) {
                ArticleTitleVideoValidation.style.display = 'block';
                $("#TitleImageValidationField").val("");
                document.getElementById('CardDynamicVideo').src = '';
                CardDynamicImageDiv.style.display = 'block';
                CardDynamicVideoDiv.style.display = 'none';
            }
            break;
        case 2:
            var endOfString = VideoLinkText.split('/')[4];
            var p = /^(?:https?:\/\/)?(?:m\.|www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;

            if (VideoLinkText.includes("https://www.") && VideoLinkText.match(p)) {
                stringLength = endOfString.length;
                console.log('title stringLength 2: ' + stringLength);
            } else {
                VideoLinkText = titleProcessVideoUrl(VideoLinkText, true);
                if (VideoLinkText.match(p)) {
                    var endOfString = VideoLinkText.split('/')[4];
                    stringLength = endOfString.length;
                    console.log("new VideoLinkText: " + VideoLinkText)
                }
            }
            console.log('title stringLength 3: ' + stringLength);

            if (!VideoLinkText.match(p)) {
                console.log('VideoLinkText !match(p): ' + VideoLinkText);
                ArticleTitleVideoValidation.style.display = 'block';
                $("#TitleImageValidationField").val("");
                document.getElementById('CardDynamicVideo').src = '';
                CardDynamicImageDiv.style.display = 'block';
                CardDynamicVideoDiv.style.display = 'none';
            } else {
                ArticleTitleVideoValidation.style.display = 'none';
                $("#TitleImageValidationField").val("Ok");
                console.log("title VideoLinkText 2: " + VideoLinkText)
                var processedUrl = titleProcessVideoUrl(VideoLinkText, true);
                document.getElementById('CardDynamicVideo').src = processedUrl;
                $("#ArticleTitleVideoField").val(processedUrl);
            }

            if (stringLength > 11) {
                console.log('stringLength > 11 if block');
                ArticleTitleVideoValidation.style.display = 'block';
                $("#TitleImageValidationField").val("");
                document.getElementById('CardDynamicVideo').src = '';
                CardDynamicImageDiv.style.display = 'block';
                CardDynamicVideoDiv.style.display = 'none';
            }
            break;
        case 3:
            var endOfString = VideoLinkText.split('/')[3];
            var p = /^(?:https?:\/\/)?(?:m\.|www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;

            if (VideoLinkText.includes("https://youtu.be/") && VideoLinkText.match(p)) {
                stringLength = endOfString.length;
                console.log('title stringLength 4: ' + stringLength);
            } else {
                VideoLinkText = titleProcessVideoUrl(VideoLinkText, true);
                if (VideoLinkText.match(p)) {
                    var endOfString = VideoLinkText.split('/')[3];
                    stringLength = endOfString.length;
                    console.log("new VideoLinkText: " + VideoLinkText)
                }
            }
            console.log('title stringLength 5: ' + stringLength);
            if (!VideoLinkText.match(p)) {
                console.log('VideoLinkText !match(p) 1: ' + VideoLinkText);
                ArticleTitleVideoValidation.style.display = 'block';
                $("#TitleImageValidationField").val("");
                document.getElementById('CardDynamicVideo').src = '';
                CardDynamicImageDiv.style.display = 'block';
                CardDynamicVideoDiv.style.display = 'none';
            } else {
                ArticleTitleVideoValidation.style.display = 'none';
                $("#TitleImageValidationField").val("Ok");
                document.getElementById('CardDynamicVideo').src = VideoLinkText;
                console.log("title VideoLinkText 3: " + VideoLinkText)
                var processedUrl = titleProcessVideoUrl(VideoLinkText, false);
                document.getElementById('CardDynamicVideo').src = processedUrl;
                $("#ArticleTitleVideoField").val(processedUrl);
            }

            if (stringLength > 11) {
                console.log('stringLength > 11 if block 1');
                ArticleTitleVideoValidation.style.display = 'block';
                $("#TitleImageValidationField").val("");
                document.getElementById('CardDynamicVideo').src = '';
                CardDynamicImageDiv.style.display = 'block';
                CardDynamicVideoDiv.style.display = 'none';
            }
            break;
        default:
            console.log('title default');
            var p = /^(?:https?:\/\/)?(?:m\.|www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;
            if (!VideoLinkText.match(p) && !VideoLinkText == "") {
                console.log('VideoLinkText !match(p) 2: ' + VideoLinkText);
                ArticleTitleVideoValidation.style.display = 'block';
                $("#TitleImageValidationField").val("");
                document.getElementById('CardDynamicVideo').src = '';
                CardDynamicImageDiv.style.display = 'block';
                CardDynamicVideoDiv.style.display = 'none';
            } else {
                ArticleTitleVideoValidation.style.display = 'none';
                $("#TitleImageValidationField").val("");
                var CardDynamicImageDiv = document.getElementById("CardDynamicImageDiv");
                CardDynamicImageDiv.style.display = 'block';
                var CardDynamicVideoDiv = document.getElementById("CardDynamicVideoDiv");
                CardDynamicVideoDiv.style.display = 'none';
            }
    }
}

...

function titleProcessVideoUrl(rawUrl, notShortSharingUrl) {

    if (notShortSharingUrl) {

        console.log('rawUrl 1: ' + rawUrl)

        if (!rawUrl.includes("https://") && rawUrl.includes("http://")) {
            rawUrl = rawUrl.replace('http://', 'https://')
            console.log('rawUrl 2: ' + rawUrl)
        } else if (!rawUrl.includes("https://") && rawUrl.includes("www.") && !rawUrl.includes("youtu.be")) {
            rawUrl = rawUrl.replace('www.', 'https://www.')
            console.log('rawUrl 3: ' + rawUrl)
        } else if (rawUrl.includes("https://") && !rawUrl.includes("www.") && !rawUrl.includes("youtu.be")) {
            rawUrl = rawUrl.replace('https://', 'https://www.')
            console.log('rawUrl 4: ' + rawUrl)
        } else if (rawUrl.includes("youtube.com/") && !rawUrl.includes("https://") && !rawUrl.includes("http://")) {
            rawUrl = rawUrl.replace('youtube.com/', 'https://www.youtube.com/')
            console.log('rawUrl 5: ' + rawUrl)
        } else if (rawUrl.includes("youtu.be/") && !rawUrl.includes("https://") && !rawUrl.includes("http://") && !rawUrl.includes("www.")) {
            rawUrl = rawUrl.replace('youtu.be/', 'https://youtu.be/')
            console.log('rawUrl 6: ' + rawUrl)
        } else if (rawUrl.includes("youtu.be/") && !rawUrl.includes("https://") && rawUrl.includes("http://")) {
            rawUrl = rawUrl.replace('http://', 'https://')
            console.log('rawUrl 7: ' + rawUrl)
        } else if (rawUrl.includes("https://") && rawUrl.includes("www.youtu.be/")) {
            rawUrl = rawUrl.replace('www.youtu.be/', 'youtu.be/')
            console.log('rawUrl 8: ' + rawUrl)
        } else if (!rawUrl.includes("https://") && rawUrl.includes("www.youtu.be/")) {
            rawUrl = rawUrl.replace('www.youtu.be/', 'https://youtu.be/')
            console.log('rawUrl 9: ' + rawUrl)
        }

        console.log('rawUrl 10: ' + rawUrl)

        var processed = rawUrl.replace('watch?v=', 'embed/')
        if (processed.includes("https://m.youtube")) {
            processed = rawUrl.replace('m.youtube', 'www.youtube');
            if (processed.includes("&list=")) {
                processed = processed.split('&list=')[0];
                return processed;
                console.log('title Processed 1: ' + processed);
            }
        } else if (processed.includes("watch?app=desktop&v=")) {
            processed = rawUrl.replace('watch?app=desktop&v=', 'embed/');
            if (processed.includes("&list=")) {
                processed = processed.split('&list=')[0];
                return processed;
                console.log('title Processed 1a: ' + processed);
            }
        } else if (processed.includes("&list=")) {
            processed = processed.split('&list=')[0];
            return processed;
            console.log('title Processed 1b: ' + processed);
        }
        console.log('title Processed 2: ' + processed);
        return processed;
    } else {
        console.log('rawUrl 1:' + rawUrl)
        if (!rawUrl.includes("https://") && rawUrl.includes("http://")) {
            rawUrl = rawUrl.replace('http://', 'https://')
            console.log('rawUrl 2:' + rawUrl)
        } else if (!rawUrl.includes("https://") && rawUrl.includes("www.")) {
            rawUrl = rawUrl.replace('www.', 'https://www.')
            console.log('rawUrl 3:' + rawUrl)
        }
        console.log('rawUrl 4:' + rawUrl)
        var processed = rawUrl.replace('https://youtu.be/', 'https://www.youtube.com/embed/')
        if (processed.includes("?t=")) {
            processed = processed.split('?t=')[0];
            return processed;
        }

        console.log('title Processed 3: ' + processed);
        return processed;
    }

Note 1: I know the above is not pretty. I haven't had time to tidy it up yet and get rid off all the 'ifs', but it hopefully gives enough context to be useful

Note 2: Be careful what videos you use to test. YouTube does not allow embedding videos with copyrighted background music, and they will show as unavailable.

aliazhar
  • 21
  • 3
DMur
  • 625
  • 13
  • 26
3

Combining the answers from here, and some modification, I came up with this regexp:

^(?:https?:\/\/)?(?:m\.|www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(\?\S*)?$

This will match any format, but will mismatch if the id is longer than 11 characters. Also able to add start video tag with the "?" part at the end.

You can test it by pasting it into this

ForestG
  • 17,538
  • 14
  • 52
  • 86
2

Escape all the forward slashes present in your regex and then put the modified regex within / delimiter without any spaces or comment lines and you don't need to add x modifier.

var re = /^(?:https?:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/watch\?v=([^&]+)/m;
Avinash Raj
  • 172,303
  • 28
  • 230
  • 274