Fix getting the youtube video id (#1890)

* Fix getting the youtube video id regardless where the id is defined in the link

* feedback review

* Simplify and add unit tests for getYouTubeVideoId
This commit is contained in:
Elias Nahum 2018-07-05 15:02:34 -04:00 committed by GitHub
parent 9f0b7304b4
commit 5bf248d5c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 75 additions and 75 deletions

View file

@ -1428,40 +1428,6 @@ SOFTWARE.
---
## youtube-video-id
This product contains 'youtube-video-id', Extracts the YouTube video ID from a url or string.
* HOMEPAGE:
* https://github.com/remarkablemark/youtube-video-id
* LICENSE:
MIT License
Copyright (c) 2016 Menglin "Mark" Xu <mark@remarkablemark.org>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
---
## react-native-video
This product contains 'react-native-video', A <Video> component for react-native.

View file

@ -13,14 +13,13 @@ import {
View,
} from 'react-native';
import {YouTubeStandaloneAndroid, YouTubeStandaloneIOS} from 'react-native-youtube';
import youTubeVideoId from 'youtube-video-id';
import ProgressiveImage from 'app/components/progressive_image';
import CustomPropTypes from 'app/constants/custom_prop_types';
import {emptyFunction} from 'app/utils/general';
import ImageCacheManager from 'app/utils/image_cache_manager';
import {isImageLink, isYoutubeLink} from 'app/utils/url';
import {getYouTubeVideoId, isImageLink, isYoutubeLink} from 'app/utils/url';
const MAX_IMAGE_HEIGHT = 150;
@ -87,7 +86,7 @@ export default class PostBodyAdditionalContent extends PureComponent {
if (isImageLink(link)) {
imageUrl = link;
} else if (isYoutubeLink(link)) {
const videoId = youTubeVideoId(link);
const videoId = getYouTubeVideoId(link);
imageUrl = `https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`;
ImageCacheManager.cache(null, `https://i.ytimg.com/vi/${videoId}/default.jpg`, () => true);
}
@ -156,7 +155,7 @@ export default class PostBodyAdditionalContent extends PureComponent {
if (link) {
if (isYouTube) {
const videoId = youTubeVideoId(link);
const videoId = getYouTubeVideoId(link);
const imgUrl = `https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`;
const thumbUrl = `https://i.ytimg.com/vi/${videoId}/default.jpg`;
@ -291,6 +290,35 @@ export default class PostBodyAdditionalContent extends PureComponent {
return previewComponent ? {...previewComponent.props} : {};
};
getYouTubeTime = (link) => {
const timeRegex = /[\\?&](t|start|time_continue)=([0-9]+h)?([0-9]+m)?([0-9]+s?)/;
const time = link.match(timeRegex);
if (!time || !time[0]) {
return 0;
}
const hours = time[2] ? time[2].match(/([0-9]+)h/) : null;
const minutes = time[3] ? time[3].match(/([0-9]+)m/) : null;
const seconds = time[4] ? time[4].match(/([0-9]+)s?/) : null;
let ticks = 0;
if (hours && hours[1]) {
ticks += parseInt(hours[1], 10) * 3600;
}
if (minutes && minutes[1]) {
ticks += parseInt(minutes[1], 10) * 60;
}
if (seconds && seconds[1]) {
ticks += parseInt(seconds[1], 10);
}
return ticks;
};
goToImagePreview = (passProps) => {
this.props.navigator.showModal({
screen: 'ImagePreview',
@ -343,7 +371,7 @@ export default class PostBodyAdditionalContent extends PureComponent {
playYouTubeVideo = () => {
const {link} = this.props;
const videoId = youTubeVideoId(link);
const videoId = getYouTubeVideoId(link);
const startTime = this.getYouTubeTime(link);
if (Platform.OS === 'ios') {
@ -364,35 +392,6 @@ export default class PostBodyAdditionalContent extends PureComponent {
}
};
getYouTubeTime = (link) => {
const timeRegex = /[\\?&](t|start|time_continue)=([0-9]+h)?([0-9]+m)?([0-9]+s?)/;
const time = link.match(timeRegex);
if (!time || !time[0]) {
return 0;
}
const hours = time[2] ? time[2].match(/([0-9]+)h/) : null;
const minutes = time[3] ? time[3].match(/([0-9]+)m/) : null;
const seconds = time[4] ? time[4].match(/([0-9]+)s?/) : null;
let ticks = 0;
if (hours && hours[1]) {
ticks += parseInt(hours[1], 10) * 3600;
}
if (minutes && minutes[1]) {
ticks += parseInt(minutes[1], 10) * 60;
}
if (seconds && seconds[1]) {
ticks += parseInt(seconds[1], 10);
}
return ticks;
};
render() {
const {link, openGraphData, postProps} = this.props;
const {linkLoadError} = this.state;

View file

@ -94,3 +94,25 @@ export function getScheme(url) {
return match && match[1];
}
export function getYouTubeVideoId(link) {
// https://youtube.com/watch?v=<id>
let match = (/youtube\.com\/watch\?\S*\bv=([a-zA-Z0-9_-]{6,11})/g).exec(link);
if (match) {
return match[1];
}
// https://youtube.com/embed/<id>
match = (/youtube\.com\/embed\/([a-zA-Z0-9_-]{6,11})/g).exec(link);
if (match) {
return match[1];
}
// https://youtu.be/<id>
match = (/youtu.be\/([a-zA-Z0-9_-]{6,11})/g).exec(link);
if (match) {
return match[1];
}
return '';
}

View file

@ -42,4 +42,23 @@ describe('UrlUtils', () => {
expect(UrlUtils.isImageLink(link)).toEqual(true);
});
});
describe('getYouTubeVideoId', () => {
const tests = [
['https://youtu.be/zrFWrmPgfzc', 'zrFWrmPgfzc'],
['https://youtu.be/zrFWrmPgfzc?t=10s', 'zrFWrmPgfzc'],
['https://www.youtube.com/watch?v=zrFWrmPgfzc&feature=youtu.be', 'zrFWrmPgfzc'],
['https://www.youtube.com/watch?v=zrFWrmPgfzc&t=10s', 'zrFWrmPgfzc'],
['https://www.youtube.com/watch?t=10s&v=zrFWrmPgfzc', 'zrFWrmPgfzc'],
];
for (const test of tests) {
const input = test[0];
const expected = test[1];
it(input, () => {
expect(UrlUtils.getYouTubeVideoId(input)).toEqual(expected);
});
}
});
});

5
package-lock.json generated
View file

@ -18344,11 +18344,6 @@
"requires": {
"camelcase": "^4.1.0"
}
},
"youtube-video-id": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/youtube-video-id/-/youtube-video-id-0.0.2.tgz",
"integrity": "sha512-aipX3kMN/JBxa1oj6ip9GOBHxSjyAogmchbwDMyxo9JEN5tQWxvP6OBuoB1iYo9RD50uXZbcpRqaj2028/qFcQ=="
}
}
}

View file

@ -66,8 +66,7 @@
"semver": "5.5.0",
"shallow-equals": "1.0.0",
"tinycolor2": "1.4.1",
"url-parse": "1.4.0",
"youtube-video-id": "0.0.2"
"url-parse": "1.4.0"
},
"devDependencies": {
"babel-cli": "6.26.0",