mirror of
https://github.com/snobu/destreamer.git
synced 2026-01-28 10:52:18 +00:00
Compare commits
29 Commits
c81b16c8f3
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
516ca54ff1 | ||
|
|
b6a3e8f1c3 | ||
|
|
b2497865a1 | ||
|
|
a86389774d | ||
|
|
7c16da6505 | ||
|
|
52d5b227e2 | ||
|
|
a62f8ef777 | ||
|
|
d9137cc690 | ||
|
|
6ac226bda6 | ||
|
|
cb9e844d06 | ||
|
|
c7efd90f7f | ||
|
|
9fcc631236 | ||
|
|
1da56990bc | ||
|
|
7d91f32af2 | ||
|
|
918aadce5d | ||
|
|
dfab30cf46 | ||
|
|
757aab1747 | ||
|
|
ef91acaf10 | ||
|
|
cb689336d8 | ||
|
|
deadd6758c | ||
|
|
e9a0954528 | ||
|
|
eb588f74a3 | ||
|
|
22658a3706 | ||
|
|
55234af08f | ||
|
|
a129ac0240 | ||
|
|
1181290d8f | ||
|
|
21215bc9da | ||
|
|
8b2a02d0ae | ||
|
|
7efa54932f |
3
.github/workflows/build.yaml
vendored
3
.github/workflows/build.yaml
vendored
@@ -6,6 +6,9 @@ on:
|
||||
- 'README.md'
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@ node_modules
|
||||
videos
|
||||
release
|
||||
build
|
||||
yarn.lock
|
||||
63
README.md
63
README.md
@@ -1,8 +1,10 @@
|
||||
<a href="https://github.com/snobu/destreamer/actions">
|
||||
<img src="https://github.com/snobu/destreamer/workflows/Node%20CI/badge.svg" alt="CI build status" />
|
||||
</a>
|
||||
# This project is abandoned. It will probably not work anymore against your MS Stream tenant.
|
||||
|
||||
**destreamer v3.0** is just around the corner. You can try out a pre-release today by cloning [this branch](https://github.com/snobu/destreamer/tree/aria2c_forRealNow).
|
||||
# A heartfelt thank you to all the contributors over the years. You are the real MVPs. 💖
|
||||
|
||||
## Check out kylon's Sharedown for a SharePoint-backend implementation - https://github.com/kylon/Sharedown
|
||||
|
||||
<hr>
|
||||
|
||||

|
||||
|
||||
@@ -14,7 +16,7 @@ _(Alternative artwork proposals are welcome! Submit one through an Issue.)_
|
||||
|
||||
This release would not have been possible without the code and time contributed by two distinguished developers: [@lukaarma](https://github.com/lukaarma) and [@kylon](https://github.com/kylon). Thank you!
|
||||
|
||||
### Specialized vesions
|
||||
### Specialized versions
|
||||
|
||||
- [Politecnico di Milano][polimi]: fork over at https://github.com/SamanFekri/destreamer
|
||||
- [Università di Pisa][unipi]: fork over at https://github.com/Guray00/destreamer-unipi
|
||||
@@ -57,34 +59,25 @@ Note that destreamer won't run in an elevated (Administrator/root) shell. Runnin
|
||||
## Can i plug in my own browser?
|
||||
|
||||
Yes, yes you can. This may be useful if your main browser has some authentication plugins that are required for you to logon to your Microsoft Stream tenant.
|
||||
To use your own browser for the authentication part, locate the following snippet in `src/destreamer.ts`:
|
||||
To use your own browser for the authentication part, locate the following snippet in `src/destreamer.ts` and `src/TokenCache.ts`:
|
||||
|
||||
```typescript
|
||||
const browser: puppeteer.Browser = await puppeteer.launch({
|
||||
executablePath: getPuppeteerChromiumPath(),
|
||||
headless: false,
|
||||
userDataDir: (argv.keepLoginCookies) ? chromeCacheFolder : undefined,
|
||||
args: [
|
||||
'--disable-dev-shm-usage',
|
||||
'--fast-start',
|
||||
'--no-sandbox'
|
||||
]
|
||||
});
|
||||
executablePath: getPuppeteerChromiumPath(),
|
||||
// …
|
||||
});
|
||||
```
|
||||
|
||||
Navigate to `chrome://version` in the browser you want to plug in and copy executable path from there. Use double backslash for Windows.
|
||||
|
||||
Now, change `executablePath` to reflect the path to your browser and profile (i.e. to use Microsoft Edge on Windows):
|
||||
```typescript
|
||||
executablePath: 'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe',
|
||||
executablePath: 'C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe',
|
||||
```
|
||||
In Linux for Chromium,
|
||||
```typescript
|
||||
executablePath: '/usr/bin/chromium-browser',
|
||||
```
|
||||
Depending on your distro, it may also be `/usr/bin/chromium`. You will have to change it appropriately for Google Chrome.
|
||||
|
||||
Note that for Mac the path may look a little different but no other changes are necessary.
|
||||
You can add `userDataDir` right after `executablePath` with the path to your browser profile (also shown in `chrome://version`) if you want that loaded as well.
|
||||
|
||||
You need to rebuild (`npm run build`) every time you change this configuration.
|
||||
Remember to rebuild (`npm run build`) every time you change this configuration.
|
||||
|
||||
## How to build
|
||||
|
||||
@@ -185,7 +178,7 @@ https://web.microsoftstream.com/video/xxxxxxxx-aaaa-xxxx-xxxx-xxxxxxxxxxxx
|
||||
```
|
||||
|
||||
### Title template
|
||||
The `-t` option allows users to input a template string for the output file names.
|
||||
The `-t` option allows user to specify a custom filename for the videos.
|
||||
|
||||
You can use one or more of the following magic sequence which will get substituted at runtime. The magic sequence must be surrounded by curly brackets like this: `{title} {publishDate}`
|
||||
|
||||
@@ -197,8 +190,20 @@ You can use one or more of the following magic sequence which will get substitut
|
||||
- `authorEmail`: E-mail of video publisher
|
||||
- `uniqueId`: An _unique-enough_ ID generated from the video metadata
|
||||
|
||||
Example -
|
||||
Examples -
|
||||
```
|
||||
Input:
|
||||
-t 'This is an example'
|
||||
|
||||
Expected filename:
|
||||
This is an example.mkv
|
||||
|
||||
Input:
|
||||
-t 'This is an example by {author}'
|
||||
|
||||
Expected filename:
|
||||
This is an example by lukaarma.mkv
|
||||
|
||||
Input:
|
||||
-t '{title} - {duration} - {publishDate} - {publishTime} - {author} - {authorEmail} - {uniqueId}'
|
||||
|
||||
@@ -218,6 +223,14 @@ iTerm2 on a Mac -
|
||||
|
||||
By default, downloads are saved under project root `Destreamer/videos/` ( Not the system media Videos folder ), unless specified by `-o` (output directory).
|
||||
|
||||
## KNOWN BUGS
|
||||
|
||||
If you get a
|
||||
```
|
||||
[FATAL ERROR] Unknown error: exit code 4
|
||||
````
|
||||
when running destreamer, then make sure you're running a recent (post year 2019), stable version of **ffmpeg**.
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome. Open an issue first before sending in a pull request. All pull requests require at least one code review before they are merged to master.
|
||||
|
||||
38
package-lock.json
generated
38
package-lock.json
generated
@@ -768,11 +768,11 @@
|
||||
"integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.21.1",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
|
||||
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
|
||||
"version": "0.21.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.2.tgz",
|
||||
"integrity": "sha512-87otirqUw3e8CzHTMO+/9kh/FSgXt/eVDvipijwDtEuwbkySWZ9SBm6VEubmJ/kLKEoLQV/POhxXFb66bfekfg==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.10.0"
|
||||
"follow-redirects": "^1.14.0"
|
||||
}
|
||||
},
|
||||
"axios-retry": {
|
||||
@@ -994,9 +994,9 @@
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"color-string": {
|
||||
"version": "1.5.4",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz",
|
||||
"integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==",
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.5.tgz",
|
||||
"integrity": "sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg==",
|
||||
"requires": {
|
||||
"color-name": "^1.0.0",
|
||||
"simple-swizzle": "^0.2.2"
|
||||
@@ -1515,9 +1515,9 @@
|
||||
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz",
|
||||
"integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg=="
|
||||
"version": "1.14.4",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz",
|
||||
"integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g=="
|
||||
},
|
||||
"fs-constants": {
|
||||
"version": "1.0.0",
|
||||
@@ -1578,9 +1578,9 @@
|
||||
}
|
||||
},
|
||||
"glob-parent": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
|
||||
"integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-glob": "^4.0.1"
|
||||
@@ -1887,9 +1887,9 @@
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
},
|
||||
"log-symbols": {
|
||||
@@ -3201,9 +3201,9 @@
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.4.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.2.tgz",
|
||||
"integrity": "sha512-T4tewALS3+qsrpGI/8dqNMLIVdq/g/85U98HPMa6F0m6xTbvhXU6RCQLqPH3+SlomNV/LdY6RXEbBpMH6EOJnA=="
|
||||
"version": "7.4.6",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
|
||||
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A=="
|
||||
},
|
||||
"xhr": {
|
||||
"version": "2.6.0",
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
"@tedconf/fessonia": "^2.1.2",
|
||||
"@types/cli-progress": "^3.8.0",
|
||||
"@types/jwt-decode": "^2.2.1",
|
||||
"axios": "^0.21.1",
|
||||
"axios": "^0.21.2",
|
||||
"axios-retry": "^3.1.9",
|
||||
"cli-progress": "^3.8.2",
|
||||
"colors": "^1.4.0",
|
||||
|
||||
@@ -174,9 +174,8 @@ function checkInputConflicts(videoUrls: Array<string | number> | undefined,
|
||||
|
||||
|
||||
function isOutputTemplateValid(argv: any): boolean {
|
||||
let finalTemplate: string = argv.outputTemplate;
|
||||
const elementRegEx = RegExp(/{(.*?)}/g);
|
||||
let match = elementRegEx.exec(finalTemplate);
|
||||
let match = elementRegEx.exec(argv.outputTemplate);
|
||||
|
||||
// if no template elements this fails
|
||||
if (match) {
|
||||
@@ -191,16 +190,11 @@ function isOutputTemplateValid(argv: any): boolean {
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
match = elementRegEx.exec(finalTemplate);
|
||||
match = elementRegEx.exec(argv.outputTemplate);
|
||||
}
|
||||
}
|
||||
// bad template from user, switching to default
|
||||
else {
|
||||
logger.warn('Empty output template provided, using default one \n');
|
||||
finalTemplate = '{title} - {publishDate} {uniqueId}';
|
||||
}
|
||||
|
||||
argv.outputTemplate = sanitize(finalTemplate.trim());
|
||||
argv.outputTemplate = sanitize(argv.outputTemplate.trim());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -43,11 +43,12 @@ export class TokenCache {
|
||||
|
||||
public Write(session: Session): void {
|
||||
const s: string = JSON.stringify(session, null, 4);
|
||||
fs.writeFile('.token_cache', s, (err: any) => {
|
||||
fs.writeFile(this.tokenCacheFile, s, (err: any) => {
|
||||
if (err) {
|
||||
return logger.error(err);
|
||||
}
|
||||
logger.info('Fresh access token dropped into .token_cachen \n'.green);
|
||||
|
||||
logger.info(`Fresh access token dropped into ${this.tokenCacheFile} \n`.green);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export type Video = {
|
||||
outPath: string;
|
||||
totalChunks: number; // Abstraction of FFmpeg timemark
|
||||
playbackUrl: string;
|
||||
posterImageUrl: string;
|
||||
posterImageUrl: string | null;
|
||||
captionsUrl?: string
|
||||
}
|
||||
|
||||
|
||||
@@ -185,7 +185,9 @@ async function downloadVideo(videoGUIDs: Array<string>, outputDirectories: Array
|
||||
const headers: string = 'Authorization: Bearer ' + session.AccessToken;
|
||||
|
||||
if (!argv.noExperiments) {
|
||||
await drawThumbnail(video.posterImageUrl, session);
|
||||
if (video.posterImageUrl) {
|
||||
await drawThumbnail(video.posterImageUrl, session);
|
||||
}
|
||||
}
|
||||
|
||||
const ffmpegInpt: any = new FFmpegInput(video.playbackUrl, new Map([
|
||||
|
||||
1
trigger_codeQL
Normal file
1
trigger_codeQL
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
Reference in New Issue
Block a user