mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-30 16:02:00 +02:00
@@ -103,16 +103,6 @@
|
|||||||
<summary>Set PlayFlags for playbin</summary>
|
<summary>Set PlayFlags for playbin</summary>
|
||||||
</key>
|
</key>
|
||||||
|
|
||||||
<!-- YouTube -->
|
|
||||||
<key name="yt-adaptive-enabled" type="b">
|
|
||||||
<default>false</default>
|
|
||||||
<summary>Enable to use adaptive streaming for YouTube</summary>
|
|
||||||
</key>
|
|
||||||
<key name="yt-quality-type" type="i">
|
|
||||||
<default>1</default>
|
|
||||||
<summary>Max YouTube video quality type</summary>
|
|
||||||
</key>
|
|
||||||
|
|
||||||
<!-- Other -->
|
<!-- Other -->
|
||||||
<key name="window-size" type="s">
|
<key name="window-size" type="s">
|
||||||
<default>'[800, 490]'</default>
|
<default>'[800, 490]'</default>
|
||||||
|
@@ -1,151 +0,0 @@
|
|||||||
/* Copyright (C) 2012-present by fent
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
const jsVarStr = '[a-zA-Z_\\$][a-zA-Z_0-9]*';
|
|
||||||
const jsSingleQuoteStr = `'[^'\\\\]*(:?\\\\[\\s\\S][^'\\\\]*)*'`;
|
|
||||||
const jsDoubleQuoteStr = `"[^"\\\\]*(:?\\\\[\\s\\S][^"\\\\]*)*"`;
|
|
||||||
const jsQuoteStr = `(?:${jsSingleQuoteStr}|${jsDoubleQuoteStr})`;
|
|
||||||
const jsKeyStr = `(?:${jsVarStr}|${jsQuoteStr})`;
|
|
||||||
const jsPropStr = `(?:\\.${jsVarStr}|\\[${jsQuoteStr}\\])`;
|
|
||||||
const jsEmptyStr = `(?:''|"")`;
|
|
||||||
const reverseStr = ':function\\(a\\)\\{' +
|
|
||||||
'(?:return )?a\\.reverse\\(\\)' +
|
|
||||||
'\\}';
|
|
||||||
const sliceStr = ':function\\(a,b\\)\\{' +
|
|
||||||
'return a\\.slice\\(b\\)' +
|
|
||||||
'\\}';
|
|
||||||
const spliceStr = ':function\\(a,b\\)\\{' +
|
|
||||||
'a\\.splice\\(0,b\\)' +
|
|
||||||
'\\}';
|
|
||||||
const swapStr = ':function\\(a,b\\)\\{' +
|
|
||||||
'var c=a\\[0\\];a\\[0\\]=a\\[b(?:%a\\.length)?\\];a\\[b(?:%a\\.length)?\\]=c(?:;return a)?' +
|
|
||||||
'\\}';
|
|
||||||
const actionsObjRegexp = new RegExp(
|
|
||||||
`var (${jsVarStr})=\\{((?:(?:${
|
|
||||||
jsKeyStr}${reverseStr}|${
|
|
||||||
jsKeyStr}${sliceStr}|${
|
|
||||||
jsKeyStr}${spliceStr}|${
|
|
||||||
jsKeyStr}${swapStr
|
|
||||||
}),?\\r?\\n?)+)\\};`);
|
|
||||||
const actionsFuncRegexp = new RegExp(`${`function(?: ${jsVarStr})?\\(a\\)\\{` +
|
|
||||||
`a=a\\.split\\(${jsEmptyStr}\\);\\s*` +
|
|
||||||
`((?:(?:a=)?${jsVarStr}`}${
|
|
||||||
jsPropStr
|
|
||||||
}\\(a,\\d+\\);)+)` +
|
|
||||||
`return a\\.join\\(${jsEmptyStr}\\)` +
|
|
||||||
`\\}`);
|
|
||||||
const reverseRegexp = new RegExp(`(?:^|,)(${jsKeyStr})${reverseStr}`, 'm');
|
|
||||||
const sliceRegexp = new RegExp(`(?:^|,)(${jsKeyStr})${sliceStr}`, 'm');
|
|
||||||
const spliceRegexp = new RegExp(`(?:^|,)(${jsKeyStr})${spliceStr}`, 'm');
|
|
||||||
const swapRegexp = new RegExp(`(?:^|,)(${jsKeyStr})${swapStr}`, 'm');
|
|
||||||
|
|
||||||
const swapHeadAndPosition = (arr, position) => {
|
|
||||||
const first = arr[0];
|
|
||||||
arr[0] = arr[position % arr.length];
|
|
||||||
arr[position] = first;
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
function decipher(sig, tokens) {
|
|
||||||
sig = sig.split('');
|
|
||||||
tokens = tokens.split(',');
|
|
||||||
|
|
||||||
for(let i = 0, len = tokens.length; i < len; i++) {
|
|
||||||
let token = tokens[i], pos;
|
|
||||||
switch (token[0]) {
|
|
||||||
case 'r':
|
|
||||||
sig = sig.reverse();
|
|
||||||
break;
|
|
||||||
case 'w':
|
|
||||||
pos = ~~token.slice(1);
|
|
||||||
sig = swapHeadAndPosition(sig, pos);
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
pos = ~~token.slice(1);
|
|
||||||
sig = sig.slice(pos);
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
pos = ~~token.slice(1);
|
|
||||||
sig.splice(0, pos);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sig.join('');
|
|
||||||
};
|
|
||||||
|
|
||||||
function extractActions(body) {
|
|
||||||
const objResult = actionsObjRegexp.exec(body);
|
|
||||||
const funcResult = actionsFuncRegexp.exec(body);
|
|
||||||
|
|
||||||
if(!objResult || !funcResult)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
const obj = objResult[1].replace(/\$/g, '\\$');
|
|
||||||
const objBody = objResult[2].replace(/\$/g, '\\$');
|
|
||||||
const funcBody = funcResult[1].replace(/\$/g, '\\$');
|
|
||||||
|
|
||||||
let result = reverseRegexp.exec(objBody);
|
|
||||||
const reverseKey = result && result[1]
|
|
||||||
.replace(/\$/g, '\\$')
|
|
||||||
.replace(/\$|^'|^"|'$|"$/g, '');
|
|
||||||
result = sliceRegexp.exec(objBody);
|
|
||||||
const sliceKey = result && result[1]
|
|
||||||
.replace(/\$/g, '\\$')
|
|
||||||
.replace(/\$|^'|^"|'$|"$/g, '');
|
|
||||||
result = spliceRegexp.exec(objBody);
|
|
||||||
const spliceKey = result && result[1]
|
|
||||||
.replace(/\$/g, '\\$')
|
|
||||||
.replace(/\$|^'|^"|'$|"$/g, '');
|
|
||||||
result = swapRegexp.exec(objBody);
|
|
||||||
const swapKey = result && result[1]
|
|
||||||
.replace(/\$/g, '\\$')
|
|
||||||
.replace(/\$|^'|^"|'$|"$/g, '');
|
|
||||||
|
|
||||||
const keys = `(${[reverseKey, sliceKey, spliceKey, swapKey].join('|')})`;
|
|
||||||
const myreg = `(?:a=)?${obj
|
|
||||||
}(?:\\.${keys}|\\['${keys}'\\]|\\["${keys}"\\])` +
|
|
||||||
`\\(a,(\\d+)\\)`;
|
|
||||||
const tokenizeRegexp = new RegExp(myreg, 'g');
|
|
||||||
const tokens = [];
|
|
||||||
|
|
||||||
while((result = tokenizeRegexp.exec(funcBody)) !== null) {
|
|
||||||
const key = result[1] || result[2] || result[3];
|
|
||||||
const pos = result[4];
|
|
||||||
switch (key) {
|
|
||||||
case swapKey:
|
|
||||||
tokens.push(`w${result[4]}`);
|
|
||||||
break;
|
|
||||||
case reverseKey:
|
|
||||||
tokens.push('r');
|
|
||||||
break;
|
|
||||||
case sliceKey:
|
|
||||||
tokens.push(`s${result[4]}`);
|
|
||||||
break;
|
|
||||||
case spliceKey:
|
|
||||||
tokens.push(`p${result[4]}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokens.join(',');
|
|
||||||
}
|
|
165
src/dash.js
165
src/dash.js
@@ -1,165 +0,0 @@
|
|||||||
const Debug = imports.src.debug;
|
|
||||||
const FileOps = imports.src.fileOps;
|
|
||||||
const Misc = imports.src.misc;
|
|
||||||
|
|
||||||
const { debug } = Debug;
|
|
||||||
|
|
||||||
function generateDash(dashInfo)
|
|
||||||
{
|
|
||||||
debug('generating dash');
|
|
||||||
|
|
||||||
const bufferSec = Math.min(4, dashInfo.duration);
|
|
||||||
|
|
||||||
const dash = [
|
|
||||||
`<?xml version="1.0" encoding="UTF-8"?>`,
|
|
||||||
`<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"`,
|
|
||||||
` xmlns="urn:mpeg:dash:schema:mpd:2011"`,
|
|
||||||
` xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd"`,
|
|
||||||
` type="static"`,
|
|
||||||
` mediaPresentationDuration="PT${dashInfo.duration}S"`,
|
|
||||||
` minBufferTime="PT${bufferSec}S"`,
|
|
||||||
` profiles="urn:mpeg:dash:profile:isoff-on-demand:2011">`,
|
|
||||||
` <Period>`
|
|
||||||
];
|
|
||||||
|
|
||||||
for(let adaptation of dashInfo.adaptations)
|
|
||||||
dash.push(_addAdaptationSet(adaptation));
|
|
||||||
|
|
||||||
dash.push(
|
|
||||||
` </Period>`,
|
|
||||||
`</MPD>`
|
|
||||||
);
|
|
||||||
|
|
||||||
debug('dash generated');
|
|
||||||
|
|
||||||
return dash.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
function _addAdaptationSet(streamsArr)
|
|
||||||
{
|
|
||||||
/* We just need it for adaptation type,
|
|
||||||
* so any stream will do */
|
|
||||||
const { mimeInfo } = streamsArr[0];
|
|
||||||
|
|
||||||
const adaptArr = [
|
|
||||||
`contentType="${mimeInfo.content}"`,
|
|
||||||
`mimeType="${mimeInfo.type}"`,
|
|
||||||
`subsegmentAlignment="true"`,
|
|
||||||
`subsegmentStartsWithSAP="1"`,
|
|
||||||
];
|
|
||||||
|
|
||||||
const widthArr = [];
|
|
||||||
const heightArr = [];
|
|
||||||
const fpsArr = [];
|
|
||||||
|
|
||||||
const representations = [];
|
|
||||||
|
|
||||||
for(let stream of streamsArr) {
|
|
||||||
/* No point parsing if no URL */
|
|
||||||
if(!stream.url)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(stream.width && stream.height) {
|
|
||||||
widthArr.push(stream.width);
|
|
||||||
heightArr.push(stream.height);
|
|
||||||
}
|
|
||||||
if(stream.fps)
|
|
||||||
fpsArr.push(stream.fps);
|
|
||||||
|
|
||||||
representations.push(_getStreamRepresentation(stream));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(widthArr.length && heightArr.length) {
|
|
||||||
const maxWidth = Math.max.apply(null, widthArr);
|
|
||||||
const maxHeight = Math.max.apply(null, heightArr);
|
|
||||||
const par = _getPar(maxWidth, maxHeight);
|
|
||||||
|
|
||||||
adaptArr.push(`maxWidth="${maxWidth}"`);
|
|
||||||
adaptArr.push(`maxHeight="${maxHeight}"`);
|
|
||||||
adaptArr.push(`par="${par}"`);
|
|
||||||
}
|
|
||||||
if(fpsArr.length) {
|
|
||||||
const maxFps = Math.max.apply(null, fpsArr);
|
|
||||||
|
|
||||||
adaptArr.push(`maxFrameRate="${maxFps}"`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const adaptationSet = [
|
|
||||||
` <AdaptationSet ${adaptArr.join(' ')}>`,
|
|
||||||
representations.join('\n'),
|
|
||||||
` </AdaptationSet>`
|
|
||||||
];
|
|
||||||
|
|
||||||
return adaptationSet.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getStreamRepresentation(stream)
|
|
||||||
{
|
|
||||||
const repOptsArr = [
|
|
||||||
`id="${stream.itag}"`,
|
|
||||||
`codecs="${stream.mimeInfo.codecs}"`,
|
|
||||||
`bandwidth="${stream.bitrate}"`,
|
|
||||||
];
|
|
||||||
|
|
||||||
if(stream.width && stream.height) {
|
|
||||||
repOptsArr.push(`width="${stream.width}"`);
|
|
||||||
repOptsArr.push(`height="${stream.height}"`);
|
|
||||||
repOptsArr.push(`sar="1:1"`);
|
|
||||||
}
|
|
||||||
if(stream.fps)
|
|
||||||
repOptsArr.push(`frameRate="${stream.fps}"`);
|
|
||||||
|
|
||||||
const repArr = [
|
|
||||||
` <Representation ${repOptsArr.join(' ')}>`,
|
|
||||||
];
|
|
||||||
if(stream.audioChannels) {
|
|
||||||
const audioConfArr = [
|
|
||||||
`schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011"`,
|
|
||||||
`value="${stream.audioChannels}"`,
|
|
||||||
];
|
|
||||||
repArr.push(` <AudioChannelConfiguration ${audioConfArr.join(' ')}/>`);
|
|
||||||
}
|
|
||||||
|
|
||||||
repArr.push(
|
|
||||||
` <BaseURL>${stream.url}</BaseURL>`
|
|
||||||
);
|
|
||||||
|
|
||||||
if(stream.indexRange) {
|
|
||||||
const segRange = `${stream.indexRange.start}-${stream.indexRange.end}`;
|
|
||||||
repArr.push(
|
|
||||||
` <SegmentBase indexRange="${segRange}">`
|
|
||||||
);
|
|
||||||
if(stream.initRange) {
|
|
||||||
const initRange = `${stream.initRange.start}-${stream.initRange.end}`;
|
|
||||||
repArr.push(
|
|
||||||
` <Initialization range="${initRange}"/>`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
repArr.push(
|
|
||||||
` </SegmentBase>`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
repArr.push(
|
|
||||||
` </Representation>`
|
|
||||||
);
|
|
||||||
|
|
||||||
return repArr.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getPar(width, height)
|
|
||||||
{
|
|
||||||
const gcd = _getGCD(width, height);
|
|
||||||
|
|
||||||
width /= gcd;
|
|
||||||
height /= gcd;
|
|
||||||
|
|
||||||
return `${width}:${height}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getGCD(width, height)
|
|
||||||
{
|
|
||||||
return (height)
|
|
||||||
? _getGCD(height, width % height)
|
|
||||||
: width;
|
|
||||||
}
|
|
23
src/debug.js
23
src/debug.js
@@ -20,17 +20,6 @@ clapperDebugger.enabled = (
|
|||||||
&& G_DEBUG_ENV.includes('Clapper')
|
&& G_DEBUG_ENV.includes('Clapper')
|
||||||
);
|
);
|
||||||
|
|
||||||
const ytDebugger = new Debug.Debugger('YouTube', {
|
|
||||||
name_printer: new Ink.Printer({
|
|
||||||
font: Ink.Font.BOLD,
|
|
||||||
color: Ink.Color.RED
|
|
||||||
}),
|
|
||||||
time_printer: new Ink.Printer({
|
|
||||||
color: Ink.Color.LIGHT_BLUE
|
|
||||||
}),
|
|
||||||
high_precision: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
function _logStructured(debuggerName, msg, level)
|
function _logStructured(debuggerName, msg, level)
|
||||||
{
|
{
|
||||||
GLib.log_structured(
|
GLib.log_structured(
|
||||||
@@ -52,14 +41,7 @@ function _debug(debuggerName, msg)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(debuggerName) {
|
|
||||||
case 'Clapper':
|
|
||||||
clapperDebugger.debug(msg);
|
clapperDebugger.debug(msg);
|
||||||
break;
|
|
||||||
case 'YouTube':
|
|
||||||
ytDebugger.debug(msg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function debug(msg)
|
function debug(msg)
|
||||||
@@ -67,11 +49,6 @@ function debug(msg)
|
|||||||
_debug('Clapper', msg);
|
_debug('Clapper', msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ytDebug(msg)
|
|
||||||
{
|
|
||||||
_debug('YouTube', msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
function warn(msg)
|
function warn(msg)
|
||||||
{
|
{
|
||||||
_logStructured('Clapper', msg, GLib.LogLevelFlags.LEVEL_WARNING);
|
_logStructured('Clapper', msg, GLib.LogLevelFlags.LEVEL_WARNING);
|
||||||
|
19
src/misc.js
19
src/misc.js
@@ -224,22 +224,3 @@ function getIsTouch(gesture)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function encodeHTML(text)
|
|
||||||
{
|
|
||||||
return text.replace(/&/g, '&')
|
|
||||||
.replace(/</g, '<')
|
|
||||||
.replace(/>/g, '>')
|
|
||||||
.replace(/"/g, '"')
|
|
||||||
.replace(/'/g, ''');
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeURIPlus(uri)
|
|
||||||
{
|
|
||||||
return decodeURI(uri.replace(/\+/g, ' '));
|
|
||||||
}
|
|
||||||
|
|
||||||
function isHex(num)
|
|
||||||
{
|
|
||||||
return Boolean(num.match(/[0-9a-f]+$/i));
|
|
||||||
}
|
|
||||||
|
@@ -2,7 +2,6 @@ const { Adw, Gdk, Gio, GObject, Gst, GstClapper, Gtk } = imports.gi;
|
|||||||
const ByteArray = imports.byteArray;
|
const ByteArray = imports.byteArray;
|
||||||
const Debug = imports.src.debug;
|
const Debug = imports.src.debug;
|
||||||
const Misc = imports.src.misc;
|
const Misc = imports.src.misc;
|
||||||
const YouTube = imports.src.youtube;
|
|
||||||
const { PlaylistWidget } = imports.src.playlist;
|
const { PlaylistWidget } = imports.src.playlist;
|
||||||
const { WebApp } = imports.src.webApp;
|
const { WebApp } = imports.src.webApp;
|
||||||
|
|
||||||
@@ -45,12 +44,10 @@ class ClapperPlayer extends GstClapper.Clapper
|
|||||||
|
|
||||||
this.webserver = null;
|
this.webserver = null;
|
||||||
this.webapp = null;
|
this.webapp = null;
|
||||||
this.ytClient = null;
|
|
||||||
this.playlistWidget = new PlaylistWidget();
|
this.playlistWidget = new PlaylistWidget();
|
||||||
|
|
||||||
this.seekDone = true;
|
this.seekDone = true;
|
||||||
this.needsFastSeekRestore = false;
|
this.needsFastSeekRestore = false;
|
||||||
this.customVideoTitle = null;
|
|
||||||
|
|
||||||
this.windowMapped = false;
|
this.windowMapped = false;
|
||||||
this.quitOnStop = false;
|
this.quitOnStop = false;
|
||||||
@@ -142,31 +139,7 @@ class ClapperPlayer extends GstClapper.Clapper
|
|||||||
|
|
||||||
set_uri(uri)
|
set_uri(uri)
|
||||||
{
|
{
|
||||||
this.customVideoTitle = null;
|
if(Misc.getUriProtocol(uri) === 'file') {
|
||||||
|
|
||||||
if(Misc.getUriProtocol(uri) !== 'file') {
|
|
||||||
const [isYouTubeUri, videoId] = YouTube.checkYouTubeUri(uri);
|
|
||||||
|
|
||||||
if(!isYouTubeUri)
|
|
||||||
return super.set_uri(uri);
|
|
||||||
|
|
||||||
if(!this.ytClient)
|
|
||||||
this.ytClient = new YouTube.YouTubeClient();
|
|
||||||
|
|
||||||
const { root } = this.widget;
|
|
||||||
const surface = root.get_surface();
|
|
||||||
const monitor = root.display.get_monitor_at_surface(surface);
|
|
||||||
|
|
||||||
this.ytClient.getPlaybackDataAsync(videoId, monitor)
|
|
||||||
.then(data => {
|
|
||||||
this.customVideoTitle = data.title;
|
|
||||||
super.set_uri(data.uri);
|
|
||||||
})
|
|
||||||
.catch(debug);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const file = Misc.getFileFromLocalUri(uri);
|
const file = Misc.getFileFromLocalUri(uri);
|
||||||
if(!file) {
|
if(!file) {
|
||||||
if(!this.playlistWidget.nextTrack())
|
if(!this.playlistWidget.nextTrack())
|
||||||
@@ -179,6 +152,7 @@ class ClapperPlayer extends GstClapper.Clapper
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
super.set_uri(uri);
|
super.set_uri(uri);
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,6 @@ const Debug = imports.src.debug;
|
|||||||
const Dialogs = imports.src.dialogs;
|
const Dialogs = imports.src.dialogs;
|
||||||
const Misc = imports.src.misc;
|
const Misc = imports.src.misc;
|
||||||
const { Player } = imports.src.player;
|
const { Player } = imports.src.player;
|
||||||
const YouTube = imports.src.youtube;
|
|
||||||
const Revealers = imports.src.revealers;
|
const Revealers = imports.src.revealers;
|
||||||
|
|
||||||
const { debug } = Debug;
|
const { debug } = Debug;
|
||||||
@@ -309,10 +308,7 @@ class ClapperWidget extends Gtk.Grid
|
|||||||
|
|
||||||
updateTitle(mediaInfo)
|
updateTitle(mediaInfo)
|
||||||
{
|
{
|
||||||
let title = this.player.customVideoTitle;
|
let title = mediaInfo.get_title();
|
||||||
|
|
||||||
if(!title)
|
|
||||||
title = mediaInfo.get_title();
|
|
||||||
|
|
||||||
if(!title) {
|
if(!title) {
|
||||||
const item = this.player.playlistWidget.getActiveRow();
|
const item = this.player.playlistWidget.getActiveRow();
|
||||||
@@ -817,12 +813,10 @@ class ClapperWidget extends Gtk.Grid
|
|||||||
{
|
{
|
||||||
const dropTarget = new Gtk.DropTarget({
|
const dropTarget = new Gtk.DropTarget({
|
||||||
actions: Gdk.DragAction.COPY | Gdk.DragAction.MOVE,
|
actions: Gdk.DragAction.COPY | Gdk.DragAction.MOVE,
|
||||||
preload: true,
|
|
||||||
});
|
});
|
||||||
dropTarget.set_gtypes([GObject.TYPE_STRING]);
|
dropTarget.set_gtypes([GObject.TYPE_STRING]);
|
||||||
dropTarget.connect('motion', this._onDataMotion.bind(this));
|
dropTarget.connect('motion', this._onDataMotion.bind(this));
|
||||||
dropTarget.connect('drop', this._onDataDrop.bind(this));
|
dropTarget.connect('drop', this._onDataDrop.bind(this));
|
||||||
dropTarget.connect('notify::value', this._onDropValueNotify.bind(this));
|
|
||||||
|
|
||||||
return dropTarget;
|
return dropTarget;
|
||||||
}
|
}
|
||||||
@@ -1023,36 +1017,6 @@ class ClapperWidget extends Gtk.Grid
|
|||||||
this.posY = posY;
|
this.posY = posY;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDropValueNotify(dropTarget)
|
|
||||||
{
|
|
||||||
if(!dropTarget.value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const uris = dropTarget.value.split(/\r?\n/);
|
|
||||||
const firstUri = uris[0];
|
|
||||||
|
|
||||||
if(uris.length > 1 || !Gst.uri_is_valid(firstUri))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Check if user is dragging a YouTube link */
|
|
||||||
const [isYouTubeUri, videoId] = YouTube.checkYouTubeUri(firstUri);
|
|
||||||
if(!isYouTubeUri) return;
|
|
||||||
|
|
||||||
/* Since this is a YouTube video,
|
|
||||||
* create YT client if it was not created yet */
|
|
||||||
if(!this.player.ytClient)
|
|
||||||
this.player.ytClient = new YouTube.YouTubeClient();
|
|
||||||
|
|
||||||
const { ytClient } = this.player;
|
|
||||||
|
|
||||||
/* Speed up things by prefetching new video info before drop */
|
|
||||||
if(
|
|
||||||
!ytClient.compareLastVideoId(videoId)
|
|
||||||
&& ytClient.downloadingVideoId !== videoId
|
|
||||||
)
|
|
||||||
ytClient.getVideoInfoPromise(videoId).catch(debug);
|
|
||||||
}
|
|
||||||
|
|
||||||
_onDataMotion(dropTarget, x, y)
|
_onDataMotion(dropTarget, x, y)
|
||||||
{
|
{
|
||||||
return Gdk.DragAction.MOVE;
|
return Gdk.DragAction.MOVE;
|
||||||
|
1013
src/youtube.js
1013
src/youtube.js
File diff suppressed because it is too large
Load Diff
@@ -1,85 +0,0 @@
|
|||||||
var QualityType = {
|
|
||||||
0: 'normal',
|
|
||||||
1: 'hfr',
|
|
||||||
};
|
|
||||||
|
|
||||||
const Itags = {
|
|
||||||
video: {
|
|
||||||
h264: {
|
|
||||||
normal: {
|
|
||||||
240: 133,
|
|
||||||
360: 134,
|
|
||||||
480: 135,
|
|
||||||
720: 136,
|
|
||||||
1080: 137,
|
|
||||||
},
|
|
||||||
hfr: {
|
|
||||||
720: 298,
|
|
||||||
1080: 299,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
audio: {
|
|
||||||
aac: [140],
|
|
||||||
opus: [249, 250, 251],
|
|
||||||
},
|
|
||||||
combined: {
|
|
||||||
360: 18,
|
|
||||||
720: 22,
|
|
||||||
},
|
|
||||||
hls: {
|
|
||||||
240: 92,
|
|
||||||
360: 93,
|
|
||||||
480: 94,
|
|
||||||
720: 95,
|
|
||||||
1080: 96,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function _appendItagArray(arr, opts, formats)
|
|
||||||
{
|
|
||||||
const keys = Object.keys(formats);
|
|
||||||
|
|
||||||
for(let fmt of keys) {
|
|
||||||
arr.push(formats[fmt]);
|
|
||||||
|
|
||||||
if(
|
|
||||||
fmt >= opts.height
|
|
||||||
|| Math.floor(fmt * 16 / 9) >= opts.width
|
|
||||||
)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDashItags(opts)
|
|
||||||
{
|
|
||||||
const allowed = {
|
|
||||||
video: [],
|
|
||||||
audio: (opts.codec === 'h264')
|
|
||||||
? Itags.audio.aac
|
|
||||||
: Itags.audio.opus
|
|
||||||
};
|
|
||||||
const types = Object.keys(Itags.video[opts.codec]);
|
|
||||||
|
|
||||||
for(let type of types) {
|
|
||||||
const formats = Itags.video[opts.codec][type];
|
|
||||||
_appendItagArray(allowed.video, opts, formats);
|
|
||||||
|
|
||||||
if(type === opts.type)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCombinedItags(opts)
|
|
||||||
{
|
|
||||||
return _appendItagArray([], opts, Itags.combined);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getHLSItags(opts)
|
|
||||||
{
|
|
||||||
return _appendItagArray([], opts, Itags.hls);
|
|
||||||
}
|
|
@@ -195,31 +195,6 @@
|
|||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
|
||||||
<object class="AdwPreferencesGroup">
|
|
||||||
<property name="title" translatable="no">YouTube</property>
|
|
||||||
<child>
|
|
||||||
<object class="ClapperPrefsSwitch">
|
|
||||||
<property name="title" translatable="yes">Prefer adaptive streaming</property>
|
|
||||||
<property name="schema-name">yt-adaptive-enabled</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="ClapperPrefsCombo">
|
|
||||||
<property name="title" translatable="yes">Max quality</property>
|
|
||||||
<property name="schema-name">yt-quality-type</property>
|
|
||||||
<property name="model">
|
|
||||||
<object class="GtkStringList">
|
|
||||||
<items>
|
|
||||||
<item translatable="yes">Normal</item>
|
|
||||||
<item translatable="no">HFR</item>
|
|
||||||
</items>
|
|
||||||
</object>
|
|
||||||
</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
|
Reference in New Issue
Block a user