mirax-player
Advanced tools
Comparing version 3.0.0-alpha.13 to 3.0.0-alpha.14
@@ -5,5 +5,5 @@ # Changelog | ||
- Feature: Getting minimal scripts | ||
- Feature: Mirax embed follows api docs for YouTube and Vimeo | ||
- Bug Fix: Removed duplicated files. | ||
- Documentation: Adding documentation files with src and docs folders. | ||
- Documentation: In some ways controllers for embed videos are based on their official developer sites. | ||
@@ -13,5 +13,5 @@ | ||
### [3.0.0-alpha.13] | ||
### [3.0.0-alpha.14] | ||
- Initial Alpha Release: Documentation features included in this alpha version. | ||
@@ -1,52 +0,109 @@ | ||
function miraxEmbed(playerRef) { | ||
const youtubeRegex = /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=)?([a-zA-Z0-9\-_]+)/; | ||
const vimeoRegex = /https:\/\/vimeo\.com\/(\d+)/; | ||
const videoUrl = playerRef.getAttribute("mirax-embed-video"); | ||
if (playerRef && videoUrl) { | ||
const youtubeMatch = videoUrl.match(youtubeRegex); | ||
const vimeoMatch = videoUrl.match(vimeoRegex); | ||
if (youtubeMatch) { | ||
// YouTube video | ||
const videoId = youtubeMatch[1]; | ||
const existingIframe = playerRef.querySelector("iframe"); | ||
if (!existingIframe) { | ||
const iframe = document.createElement("iframe"); | ||
iframe.src = `https://www.youtube.com/embed/${videoId}`; | ||
iframe.frameBorder = "0"; | ||
iframe.allowFullscreen = true; | ||
playerRef.appendChild(iframe); | ||
} | ||
} else if (vimeoMatch) { | ||
// Vimeo video | ||
const videoId = vimeoMatch[1]; | ||
// Load Vimeo Player | ||
const script = document.createElement("script"); | ||
script.src = "https://player.vimeo.com/api/player.js"; | ||
script.async = true; | ||
script.onload = () => { | ||
// Create the Vimeo Player | ||
const vimeoPlayer = new window.Vimeo.Player(playerRef, { | ||
id: videoId, | ||
}); | ||
// Initialize the player | ||
vimeoPlayer.ready().then(() => { | ||
// You can use player methods here as needed | ||
}); | ||
}; | ||
document.body.appendChild(script); | ||
return () => { | ||
// Cleanup when the component unmounts | ||
document.body.removeChild(script); | ||
}; | ||
} | ||
// Function to extract YouTube video ID from a URL | ||
const extractYouTubeVideoId = (url) => { | ||
const videoIdMatch = url.match(/(\?v=|\/embed\/|\/watch\?v=|\/v\/|\/e\/|youtu\.be\/)([^#\&\?]*).*/); | ||
if (videoIdMatch && videoIdMatch[2].length === 11) { | ||
return videoIdMatch[2]; | ||
} else { | ||
console.error("Invalid YouTube video URL"); | ||
return ""; | ||
} | ||
}; | ||
// Function to embed a YouTube video | ||
const embedYouTube = (playerRef, params) => { | ||
const videoUrl = playerRef.getAttribute("mirax-embed-video"); | ||
const videoId = extractYouTubeVideoId(videoUrl); | ||
// Check if the YouTube iframe API is already available | ||
if (window.YT && window.YT.Player) { | ||
initializeYouTubeAPI(playerRef, videoId, params); | ||
} else { | ||
// Define the callback function when the YouTube iframe API is ready | ||
window.onYouTubeIframeAPIReady = () => { | ||
initializeYouTubeAPI(playerRef, videoId, params); | ||
}; | ||
// Load the YouTube iframe API script | ||
const script = document.createElement("script"); | ||
script.src = "https://www.youtube.com/iframe_api"; | ||
script.async = true; | ||
document.body.appendChild(script); | ||
} | ||
// Function to clean up the player | ||
return () => { | ||
if (playerRef) { | ||
playerRef.innerHTML = ""; // Remove the YouTube player iframe | ||
} | ||
}; | ||
}; | ||
// Function to initialize the YouTube API | ||
const initializeYouTubeAPI = (playerRef, videoId, params) => { | ||
if (playerRef) { | ||
new window.YT.Player(playerRef, { | ||
videoId: videoId, | ||
...params, | ||
}); | ||
} | ||
}; | ||
export default miraxEmbed; | ||
// Function to extract Vimeo video ID from a URL | ||
const extractVimeoVideoId = (url) => { | ||
const videoIdMatch = url.match(/\/(\d+)/); | ||
if (videoIdMatch && videoIdMatch[1]) { | ||
return videoIdMatch[1]; | ||
} else { | ||
console.error("Invalid Vimeo video URL"); | ||
return ""; | ||
} | ||
}; | ||
// Function to embed a Vimeo video | ||
const embedVimeo = (playerRef, params) => { | ||
const videoUrl = playerRef.getAttribute("mirax-embed-video"); | ||
const videoId = extractVimeoVideoId(videoUrl); | ||
// Create a script element to load the Vimeo Player API | ||
const script = document.createElement("script"); | ||
script.src = "https://player.vimeo.com/api/player.js"; | ||
script.async = true; | ||
// Callback when the Vimeo Player API script is loaded | ||
script.onload = () => { | ||
const vimeoPlayer = new window.Vimeo.Player(playerRef, { | ||
id: videoId, | ||
...params, | ||
}); | ||
vimeoPlayer.ready().then(() => { | ||
// You can use player methods here as needed | ||
}); | ||
}; | ||
document.body.appendChild(script); | ||
// Function to clean up the player | ||
return () => { | ||
if (playerRef) { | ||
playerRef.innerHTML = ""; // Remove the Vimeo player iframe | ||
} | ||
document.body.removeChild(script); | ||
}; | ||
}; | ||
// Function to embed either YouTube or Vimeo video based on the URL | ||
const miraxEmbed = (playerRef, params) => { | ||
const videoUrl = playerRef.getAttribute("mirax-embed-video"); | ||
const isVimeo = videoUrl.includes("vimeo.com"); | ||
if (isVimeo) { | ||
embedVimeo(playerRef, params); | ||
} else { | ||
embedYouTube(playerRef, params); | ||
} | ||
}; | ||
// Export functions and types | ||
export { embedYouTube, embedVimeo, miraxEmbed }; |
@@ -9,3 +9,3 @@ | ||
const content_play = "\\25B6"; | ||
const content_pause = "\\2590" + "\\A0" + "\\258C"; | ||
const content_pause = "|" + " " + "|"; | ||
const content_speaker = "\\1F508"; | ||
@@ -46,6 +46,6 @@ const content_pip = "\\0393"; | ||
height: 20px; | ||
max-width:740px; | ||
margin-top:-34px; | ||
max-width:720px; | ||
margin-top:-44px; | ||
bottom: 0; | ||
left: 0; | ||
left: 10; | ||
/* background-color: rgba(0, 0, 0, 0.5); */ | ||
@@ -58,2 +58,3 @@ color: #fff; | ||
align-items: center; | ||
border-radius:3px; | ||
} | ||
@@ -86,4 +87,4 @@ | ||
color:white; | ||
font-size:10px; | ||
font-size:12px; | ||
appearance: none; | ||
} | ||
@@ -248,3 +249,4 @@ | ||
width: 100%; | ||
max-width:425px; | ||
max-width:410px; | ||
height:10px; | ||
float: left; | ||
@@ -342,2 +344,3 @@ margin-left: 196px; | ||
`; | ||
@@ -360,2 +363,31 @@ miraxStyle.appendChild(document.createTextNode(styles)); | ||
} | ||
/* Hide the control div by default */ | ||
.mirax-theme { | ||
display: none; | ||
} | ||
/* Show the control div when hovering over the video or itself */ | ||
.mirax-player:hover + .mirax-theme, | ||
.mirax-theme:hover { | ||
display: block; | ||
margin: 0 auto; | ||
float: inherit; | ||
position: relative; | ||
width: 100%; | ||
height: 20px; | ||
max-width:96%; | ||
margin-top:-44px; | ||
bottom: 0; | ||
left: 10; | ||
/* background-color: rgba(0, 0, 0, 0.5); */ | ||
color: #fff; | ||
padding-top:5px; | ||
padding-bottom:5px; | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
border-radius:3px; | ||
} | ||
} | ||
@@ -362,0 +394,0 @@ `; |
@@ -36,1 +36,2 @@ // mirax-player.d.ts | ||
} | ||
{ | ||
"name": "mirax-player", | ||
"version": "3.0.0-alpha.13", | ||
"description": "Mirax Player is a framework-agnostic video player that seamlessly integrates with TypeScript and JavaScript applications across a range of popular front-end libraries and frameworks, including React, Vue, Angular, and Svelte.", | ||
"version": "3.0.0-alpha.14", | ||
"description": "Mirax Player is an adaptable video player and embedding solution that seamlessly integrates with TypeScript and JavaScript applications across a range of popular front-end libraries and frameworks, including React, Vue, Angular, and Svelte.", | ||
"main": "index.js", | ||
@@ -9,4 +9,7 @@ "scripts": { | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/demjhonsilver/mirax-player.git" | ||
}, | ||
"types": "mirax-player.d.ts", | ||
"homepage": "https://github.com/demjhonsilver/mirax-player", | ||
"keywords": [ | ||
@@ -26,3 +29,7 @@ "video player", | ||
"author": "Demjhon Silver", | ||
"license": "MIT" | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/demjhonsilver/mirax-player/issues" | ||
}, | ||
"homepage": "https://github.com/demjhonsilver/mirax-player" | ||
} |
239
README.md
@@ -10,7 +10,4 @@ <p align="center"> | ||
![Downloads](https://img.shields.io/npm/dt/mirax-player.svg?style=flat-square&label=Downloads&color=brightgreen) | ||
![License](https://img.shields.io/npm/l/mirax-player.svg?style=flat-square&label=License&color=brightgreen) | ||
[![License](https://img.shields.io/npm/l/mirax-player.svg?style=flat-square&label=License&color=brightgreen)](http://www.opensource.org/licenses/MIT) | ||
</div> | ||
@@ -24,6 +21,6 @@ <p align="center"> | ||
- [Description](#description) | ||
- [Paradigm](#paradigm) | ||
- [Features](#features) | ||
- [Installation](#installation) | ||
- [Controllers](#Controllers) | ||
- [Usage](#usage) | ||
- [Embed](#embed) | ||
- [Player](#player) | ||
- [React](#react) | ||
@@ -33,6 +30,5 @@ - [Vue](#vue) | ||
- [Svelte](#svelte) | ||
- [CSS Embed](#css-embed) | ||
- [CSS Player](#css-player) | ||
- [CSS Embed](#css-embed) | ||
- [Colors](#colors) | ||
- [Features](#features) | ||
- [License](#license) | ||
@@ -43,57 +39,19 @@ - [Author](#author) | ||
Mirax Player is a framework-agnostic video player that seamlessly integrates with TypeScript and JavaScript applications across a range of popular front-end libraries and frameworks, including React, Vue, Angular, and Svelte. This framework-agnostic approach allows developers to incorporate Mirax Player into their projects without being bound to a specific library or framework, providing flexibility and compatibility for a diverse range of web development environments. It was written in pure JavaScript but can be implemented in both TypeScript and JavaScript. | ||
Mirax Player is an adaptable video player and embedding solution that seamlessly integrates with TypeScript and JavaScript applications across a range of popular front-end libraries and frameworks, including React, Vue, Angular, and Svelte. It was written in pure JavaScript but can be implemented in both TypeScript and JavaScript. | ||
----------- | ||
Mirax Player is designed to work across multiple JavaScript frameworks without being tied to any specific one, making it a versatile choice for developers. | ||
## Features | ||
## Paradigm | ||
- Easy to use | ||
- Can embed videos like YouTube and Vimeo | ||
- Capable of playing videos (Portrait or Landscape) | ||
- Supports 9:16 dimensions (Mobile video) | ||
- Fullscreen functionality | ||
- Customizable color themes | ||
- Supports PIP (Picture-in-Picture) | ||
Mirax employs Object-Oriented Programming principles to organize its components effectively. | ||
Embed Sites | ![React](https://img.shields.io/badge/react-%2320232a.svg?style=for-the-badge&logo=react&logoColor=%2361DAFB) | ![Vue.js](https://img.shields.io/badge/vuejs-%2335495e.svg?style=for-the-badge&logo=vuedotjs&logoColor=%234FC08D) | ![Angular](https://img.shields.io/badge/angular-%23DD0031.svg?style=for-the-badge&logo=angular&logoColor=white) | ![Svelte](https://img.shields.io/badge/svelte-%23f1413d.svg?style=for-the-badge&logo=svelte&logoColor=white) | ||
---- | ------------------ | ----------- | ------------ | ----------- | ||
Youtube | JS `&` TS | JS `&` TS | TS | JS `&` TS | | ||
Vimeo | JS `&` TS | JS `&` TS | TS | JS `&` TS | | ||
It aims to provide a consistent and adaptable video player and embedding solution. | ||
-------------- | ||
```js | ||
Mirax Player | ||
Mirax libraries/scripts (Reusable scripts) | ||
├── JavaScript | ||
├── TypeScript | ||
├── React | ||
├── Vue | ||
├── Angular | ||
├── Svelte | ||
├── Video Player/ | ||
│ ├── Themes/ | ||
│ │ ├── player theme | ||
│ │ ├── progress theme | ||
├── Embed Videos/ | ||
│ ├── Video sites/ | ||
│ │ ├── Youtube | ||
│ │ ├── Vimeo | ||
``` | ||
![React](https://img.shields.io/badge/react-%2320232a.svg?style=for-the-badge&logo=react&logoColor=%2361DAFB) | ||
![Vue.js](https://img.shields.io/badge/vuejs-%2335495e.svg?style=for-the-badge&logo=vuedotjs&logoColor=%234FC08D) | ||
![Angular](https://img.shields.io/badge/angular-%23DD0031.svg?style=for-the-badge&logo=angular&logoColor=white) | ||
![Svelte](https://img.shields.io/badge/svelte-%23f1413d.svg?style=for-the-badge&logo=svelte&logoColor=white) | ||
Frameworks / Library | Tested Versions | JavaScript | TypeScript | | ||
---- | ------------------ | ----------- | ------------ | | ||
React | React 18 & above | `Yes` | `Yes` | | ||
Vue | Vue 3 & above | `Yes` | `Yes` | | ||
Angular | Angular 16 & above| `No` | `Yes` | | ||
Svelte | Svelte 4 & above | `Yes` | `Yes` | | ||
------------ | ||
Supported scripts: | ||
--------- | ||
![JavaScript](https://img.shields.io/badge/javascript-%23323330.svg?style=for-the-badge&logo=javascript&logoColor=%23F7DF1E) | ||
![TypeScript](https://img.shields.io/badge/typescript-%23007ACC.svg?style=for-the-badge&logo=typescript&logoColor=white) | ||
Compatibility for web browsers: | ||
--------- | ||
![Google Chrome](https://img.shields.io/badge/Google%20Chrome-4285F4?style=for-the-badge&logo=GoogleChrome&logoColor=white) | ||
![Firefox](https://img.shields.io/badge/Firefox-FF7139?style=for-the-badge&logo=Firefox-Browser&logoColor=white) | ||
![Edge](https://img.shields.io/badge/Edge-0078D7?style=for-the-badge&logo=Microsoft-edge&logoColor=white) | ||
![Opera](https://img.shields.io/badge/Opera-FF1B2D?style=for-the-badge&logo=Opera&logoColor=white) | ||
------------- | ||
@@ -107,6 +65,19 @@ ## Installation | ||
``` | ||
---------- | ||
### Controllers | ||
## Embed | ||
Embed Sites | Source type | link | Controller Params | | ||
------ | --------- | --------- | -------------- | | ||
YouTube | Iframe Api | https://developers.google.com/youtube/iframe_api_reference | https://developers.google.com/youtube/player_parameters | ||
Vimeo | Player SDK | https://developer.vimeo.com/player/sdk | https://developer.vimeo.com/player/sdk/embed | ||
input: | ||
```js | ||
mirax-embed-video=" " // Link from Youtube or Vimeo | ||
``` | ||
## Player | ||
Keyboard keys / buttons | Functions | Description | Supported Browsers | | ||
@@ -116,3 +87,3 @@ ---- | ---------------------- | ----------- | ------- | ||
`click` ▶ | Play & Pause | The video will play or pause | All browsers | ||
`alt+p or cmd+p` | PiP | Picture in Picture screen | `!firefox but auto appear PiP icon` | ||
`alt+p` | PiP | Picture in Picture screen | `!firefox but auto appear PiP icon` | ||
`click` Γ | PiP | Picture in Picture screen | All browsers | ||
@@ -123,45 +94,11 @@ `double click the video` | Fullscreen | It will set as fullscreen mode | All browsers | ||
`swipe for time frame` | Progress bar | To adjust video frame timestamp | All browsers | ||
------------- | ||
## Usage | ||
Mirax Player version 3 has major changes: | ||
-------------- | ||
- You can now declare value of colors inside to your component app. | ||
- Adding `miraxCustomizer` is an area to set the value of colors. | ||
- Has ability to embed videos `miraxEmbed` like video links from Youtube and Vimeo. | ||
Clip source | Usage | Responsive | Themes | Formats | | ||
-------- | ---------------------- | ----------- | ----------- | ------------ | | ||
`local/online with file ext.` | Video Player | Yes | Yes | `.mp4 .mkv` links | | ||
`online url/links` | Embed Videos | Yes | No | Url or Links | | ||
------------------ | ||
-------- | ||
syntax for importing Mirax Player for video player or embed videos : | ||
input: | ||
```js | ||
// Importing syntax for React, Vue, Angular, and Svelte: | ||
src=" " // Link example.com/video/my.mp4 or clip.mp4 | ||
import { miraxplayer, miraxEmbed } from 'mirax-player'; | ||
``` | ||
-------------- | ||
------------- | ||
Once you finish set-up the code examples below you need to restart your server like: | ||
```bash | ||
npm run dev | ||
npx ng serve | ||
``` | ||
## React | ||
@@ -193,12 +130,23 @@ - Video player - [CSS for Video Player](#css-player) | ||
```js | ||
import React, { useEffect, useRef } from "react"; | ||
import { miraxEmbed } from 'mirax-player'; | ||
const ExampleComponent = () => { | ||
const embedRef = useRef(null); | ||
const embedVideoRef = useRef(null); | ||
const embedPlayerReady = (event) => { | ||
event.target.playVideo(); | ||
}; | ||
const youtubeParams = { | ||
width: 800, height: 460, | ||
playerVars: { controls: 1, autoplay: 0, fs: 1, iv_load_policy: 3, cc_load_policy: 1 }, | ||
events: { onReady: embedPlayerReady }, | ||
}; | ||
const vimeoParams = { width: 800, height: 460, autopause: 0, controls: true }; | ||
useEffect(() => { | ||
miraxEmbed(embedRef.current); | ||
miraxEmbed(embedVideoRef.current, youtubeParams, vimeoParams); | ||
}, []); | ||
return ( | ||
<div className="whatever-embed"> | ||
<div className="whatever-embed-videoclip" ref={embedRef} mirax-embed-video="https://vimeo.com/217499569"></div> | ||
<div className="whatever-embed-videoclip" ref={embedVideoRef} mirax-embed-video="https://vimeo.com/217499569"> | ||
</div> | ||
</div> | ||
@@ -248,22 +196,3 @@ ); | ||
```js | ||
<template> | ||
<div class="whatever-embed"> | ||
<div class="whatever-embed-videoclip" ref="embedRef" mirax-embed-video="https://vimeo.com/217499569"></div> | ||
</div> | ||
</template> | ||
<script> | ||
import { onMounted, ref } from 'vue'; | ||
import { miraxEmbed } from 'mirax-player'; | ||
export default { | ||
setup() { | ||
const embedRef = ref(null); | ||
onMounted(() => { | ||
miraxEmbed(embedRef.value); | ||
}); | ||
return { | ||
embedRef | ||
}; | ||
} | ||
}; | ||
</script> | ||
``` | ||
@@ -316,17 +245,14 @@ - For TypeScript version: ( located at repository file ) | ||
```js | ||
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; | ||
import { miraxEmbed } from 'mirax-player'; | ||
@Component({ | ||
selector: 'app-example', | ||
templateUrl: './example.component.html', | ||
styleUrls: ['./example.component.css'] | ||
}) | ||
export class ExampleComponent implements OnInit { | ||
@ViewChild('embedRef', {static: true, read: ElementRef}) embedRef!: ElementRef<HTMLVideoElement>; | ||
constructor() { } | ||
ngOnInit(): void { | ||
miraxEmbed(this.embedRef.nativeElement); | ||
} | ||
} | ||
``` | ||
-------------------------------------- | ||
example.component.html | ||
------------- | ||
```html | ||
<div class="whatever-embed"> | ||
<div class="whatever-embed-videoclip" #embedRef mirax-embed-video="https://vimeo.com/217499569"></div> | ||
</div> | ||
``` | ||
## Svelte | ||
@@ -357,13 +283,3 @@ - Video player - [CSS for Video Player](#css-player) | ||
```js | ||
<script> | ||
import { onMount } from 'svelte'; | ||
import { miraxEmbed } from 'mirax-player'; | ||
let embedRef; | ||
onMount(() => { | ||
miraxEmbed(embedRef); | ||
}); | ||
</script> | ||
<div class="whatever-embed"> | ||
<div class="whatever-embed-videoclip" bind:this={embedRef} mirax-embed-video="https://vimeo.com/217499569"></div> | ||
</div> | ||
``` | ||
@@ -406,2 +322,7 @@ - For TypeScript version: ( located at repository file ) | ||
``` | ||
If you want pure transparent: | ||
--------- | ||
```js | ||
const playerTheme = "none"; | ||
``` | ||
## CSS-embed | ||
@@ -418,3 +339,3 @@ | ||
width: 100%; | ||
max-width: 600px; | ||
max-width: 1000px; /* you can change the width */ | ||
} | ||
@@ -445,22 +366,2 @@ | ||
`COLORNAME` | colorname | red | `none` | Red | ||
------------- | ||
If you want pure transparent: | ||
--------- | ||
```js | ||
const playerTheme = "none"; | ||
``` | ||
## Features | ||
- Play and Pause | ||
- Easy to use | ||
- Can embed videos | ||
- Responsiveness | ||
- Automatically hides the player bar | ||
- Capable of playing videos (Portrait or Landscape) | ||
- Supports 9:16 dimensions (Mobile video) | ||
- Fullscreen functionality | ||
- Adjustable volume (low or high) | ||
- Customizable color themes | ||
- Allows you to point and drag the timestamp anywhere within the video's duration | ||
- Supports PIP (Picture-in-Picture) | ||
---------------------------------------------------- | ||
@@ -467,0 +368,0 @@ ## License |
@@ -5,9 +5,19 @@ ```js | ||
const ExampleComponent = () => { | ||
const embedRef = useRef(null); | ||
const embedVideoRef = useRef(null); | ||
const embedPlayerReady = (event) => { | ||
event.target.playVideo(); | ||
}; | ||
const youtubeParams = { | ||
width: 800, height: 460, | ||
playerVars: { controls: 1, autoplay: 0, fs: 1, iv_load_policy: 3, cc_load_policy: 1 }, | ||
events: { onReady: embedPlayerReady }, | ||
}; | ||
const vimeoParams = { width: 800, height: 460, autopause: 0, controls: true }; | ||
useEffect(() => { | ||
miraxEmbed(embedRef.current); | ||
miraxEmbed(embedVideoRef.current, youtubeParams, vimeoParams); | ||
}, []); | ||
return ( | ||
<div className="whatever-embed"> | ||
<div className="whatever-embed-videoclip" ref={embedRef} mirax-embed-video="https://vimeo.com/217499569"></div> | ||
<div className="whatever-embed-videoclip" ref={embedVideoRef} mirax-embed-video="https://vimeo.com/217499569"> | ||
</div> | ||
</div> | ||
@@ -14,0 +24,0 @@ ); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
62600
30
856
0
0
365