mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-30 16:02:00 +02:00
YT: auto select best matching resolution for used monitor
This commit is contained in:
@@ -19,6 +19,7 @@ class ClapperPlayer extends PlayerBase
|
|||||||
this.needsFastSeekRestore = false;
|
this.needsFastSeekRestore = false;
|
||||||
this.customVideoTitle = null;
|
this.customVideoTitle = null;
|
||||||
|
|
||||||
|
this.windowMapped = false;
|
||||||
this.canAutoFullscreen = false;
|
this.canAutoFullscreen = false;
|
||||||
this.playOnFullscreen = false;
|
this.playOnFullscreen = false;
|
||||||
this.quitOnStop = false;
|
this.quitOnStop = false;
|
||||||
@@ -54,7 +55,11 @@ class ClapperPlayer extends PlayerBase
|
|||||||
if(!this.ytClient)
|
if(!this.ytClient)
|
||||||
this.ytClient = new YouTube.YouTubeClient();
|
this.ytClient = new YouTube.YouTubeClient();
|
||||||
|
|
||||||
this.ytClient.getPlaybackDataAsync(videoId)
|
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 => {
|
.then(data => {
|
||||||
this.customVideoTitle = data.title;
|
this.customVideoTitle = data.title;
|
||||||
super.set_uri(data.uri);
|
super.set_uri(data.uri);
|
||||||
@@ -121,10 +126,9 @@ class ClapperPlayer extends PlayerBase
|
|||||||
this.playlistWidget.addItem(uri);
|
this.playlistWidget.addItem(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstTrack = this.playlistWidget.get_row_at_index(0);
|
/* If not mapped yet, first track will play after map */
|
||||||
if(!firstTrack) return;
|
if(this.windowMapped)
|
||||||
|
this._playFirstTrack();
|
||||||
firstTrack.activate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
set_subtitles(source)
|
set_subtitles(source)
|
||||||
@@ -291,6 +295,14 @@ class ClapperPlayer extends PlayerBase
|
|||||||
: Gst.filename_to_uri(source);
|
: Gst.filename_to_uri(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_playFirstTrack()
|
||||||
|
{
|
||||||
|
const firstTrack = this.playlistWidget.get_row_at_index(0);
|
||||||
|
if(!firstTrack) return;
|
||||||
|
|
||||||
|
firstTrack.activate();
|
||||||
|
}
|
||||||
|
|
||||||
_performCloseCleanup(window)
|
_performCloseCleanup(window)
|
||||||
{
|
{
|
||||||
window.disconnect(this.closeRequestSignal);
|
window.disconnect(this.closeRequestSignal);
|
||||||
@@ -522,6 +534,12 @@ class ClapperPlayer extends PlayerBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onWindowMap(window)
|
||||||
|
{
|
||||||
|
this.windowMapped = true;
|
||||||
|
this._playFirstTrack();
|
||||||
|
}
|
||||||
|
|
||||||
_onCloseRequest(window)
|
_onCloseRequest(window)
|
||||||
{
|
{
|
||||||
this._performCloseCleanup(window);
|
this._performCloseCleanup(window);
|
||||||
|
@@ -578,6 +578,8 @@ class ClapperWidget extends Gtk.Grid
|
|||||||
|
|
||||||
surface.connect('notify::state', this._onStateNotify.bind(this));
|
surface.connect('notify::state', this._onStateNotify.bind(this));
|
||||||
surface.connect('layout', this._onLayoutUpdate.bind(this));
|
surface.connect('layout', this._onLayoutUpdate.bind(this));
|
||||||
|
|
||||||
|
this.player._onWindowMap(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
_clearTimeout(name)
|
_clearTimeout(name)
|
||||||
|
@@ -3,6 +3,7 @@ const Dash = imports.src.dash;
|
|||||||
const Debug = imports.src.debug;
|
const Debug = imports.src.debug;
|
||||||
const FileOps = imports.src.fileOps;
|
const FileOps = imports.src.fileOps;
|
||||||
const Misc = imports.src.misc;
|
const Misc = imports.src.misc;
|
||||||
|
const YTItags = imports.src.youtubeItags;
|
||||||
const YTDL = imports.src.assets['node-ytdl-core'];
|
const YTDL = imports.src.assets['node-ytdl-core'];
|
||||||
|
|
||||||
const debug = Debug.ytDebug;
|
const debug = Debug.ytDebug;
|
||||||
@@ -304,7 +305,7 @@ var YouTubeClient = GObject.registerClass({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPlaybackDataAsync(videoId)
|
async getPlaybackDataAsync(videoId, monitor)
|
||||||
{
|
{
|
||||||
const info = await this.getVideoInfoPromise(videoId).catch(debug);
|
const info = await this.getVideoInfoPromise(videoId).catch(debug);
|
||||||
|
|
||||||
@@ -312,7 +313,13 @@ var YouTubeClient = GObject.registerClass({
|
|||||||
throw new Error('no YouTube video info');
|
throw new Error('no YouTube video info');
|
||||||
|
|
||||||
let uri = null;
|
let uri = null;
|
||||||
const dashInfo = await this.getDashInfoAsync(info).catch(debug);
|
const itagOpts = {
|
||||||
|
width: monitor.geometry.width * monitor.scale_factor,
|
||||||
|
height: monitor.geometry.height * monitor.scale_factor,
|
||||||
|
codec: 'h264',
|
||||||
|
types: ['standard', 'hfr'],
|
||||||
|
};
|
||||||
|
const dashInfo = await this.getDashInfoAsync(info, itagOpts).catch(debug);
|
||||||
|
|
||||||
if(dashInfo) {
|
if(dashInfo) {
|
||||||
debug('parsed video info to dash info');
|
debug('parsed video info to dash info');
|
||||||
@@ -333,7 +340,7 @@ var YouTubeClient = GObject.registerClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!uri)
|
if(!uri)
|
||||||
uri = this.getBestCombinedUri(info);
|
uri = this.getBestCombinedUri(info, itagOpts);
|
||||||
|
|
||||||
if(!uri)
|
if(!uri)
|
||||||
throw new Error('no YouTube video URI');
|
throw new Error('no YouTube video URI');
|
||||||
@@ -349,7 +356,7 @@ var YouTubeClient = GObject.registerClass({
|
|||||||
return { uri, title };
|
return { uri, title };
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDashInfoAsync(info)
|
async getDashInfoAsync(info, itagOpts)
|
||||||
{
|
{
|
||||||
if(
|
if(
|
||||||
!info.streamingData
|
!info.streamingData
|
||||||
@@ -360,20 +367,10 @@ var YouTubeClient = GObject.registerClass({
|
|||||||
|
|
||||||
/* TODO: Options in prefs to set preferred video formats and adaptive streaming */
|
/* TODO: Options in prefs to set preferred video formats and adaptive streaming */
|
||||||
const isAdaptiveEnabled = settings.get_boolean('yt-adaptive-enabled');
|
const isAdaptiveEnabled = settings.get_boolean('yt-adaptive-enabled');
|
||||||
const allowedFormats = {
|
|
||||||
video: [
|
debug(`obtaining dash itags for resolution: ${itagOpts.width}x${itagOpts.height}`);
|
||||||
133,
|
const dashItags = YTItags.getDashItags(itagOpts);
|
||||||
134,
|
debug(`dash itags: ${JSON.stringify(dashItags)}`);
|
||||||
135,
|
|
||||||
136,
|
|
||||||
137,
|
|
||||||
298,
|
|
||||||
299,
|
|
||||||
],
|
|
||||||
audio: [
|
|
||||||
140,
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
const filteredStreams = {
|
const filteredStreams = {
|
||||||
video: [],
|
video: [],
|
||||||
@@ -382,11 +379,11 @@ var YouTubeClient = GObject.registerClass({
|
|||||||
|
|
||||||
for(let fmt of ['video', 'audio']) {
|
for(let fmt of ['video', 'audio']) {
|
||||||
debug(`filtering ${fmt} streams`);
|
debug(`filtering ${fmt} streams`);
|
||||||
let index = allowedFormats[fmt].length;
|
let index = dashItags[fmt].length;
|
||||||
|
|
||||||
while(index--) {
|
while(index--) {
|
||||||
const itag = allowedFormats[fmt][index];
|
const itag = dashItags[fmt][index];
|
||||||
const foundStream = info.streamingData.adaptiveFormats.find(stream => (stream.itag == itag));
|
const foundStream = info.streamingData.adaptiveFormats.find(stream => stream.itag == itag);
|
||||||
if(foundStream) {
|
if(foundStream) {
|
||||||
/* Parse and convert mimeType string into object */
|
/* Parse and convert mimeType string into object */
|
||||||
foundStream.mimeInfo = this._getMimeInfo(foundStream.mimeType);
|
foundStream.mimeInfo = this._getMimeInfo(foundStream.mimeType);
|
||||||
@@ -440,16 +437,33 @@ var YouTubeClient = GObject.registerClass({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getBestCombinedUri(info)
|
getBestCombinedUri(info, itagOpts)
|
||||||
{
|
{
|
||||||
debug('obtaining best combined URL');
|
debug(`obtaining best combined URL for resolution: ${itagOpts.width}x${itagOpts.height}`);
|
||||||
|
|
||||||
if(!info.streamingData.formats.length)
|
if(!info.streamingData.formats.length)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
const combinedStream = info.streamingData.formats[
|
let combinedStream;
|
||||||
|
|
||||||
|
const combinedItags = YTItags.getCombinedItags(itagOpts);
|
||||||
|
let index = combinedItags.length;
|
||||||
|
|
||||||
|
while(index--) {
|
||||||
|
const itag = combinedItags[index];
|
||||||
|
combinedStream = info.streamingData.formats.find(stream => stream.itag == itag);
|
||||||
|
if(combinedStream) {
|
||||||
|
debug(`found best combined itag: ${combinedStream.itag}`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!combinedStream) {
|
||||||
|
debug('trying any combined stream as last resort');
|
||||||
|
combinedStream = info.streamingData.formats[
|
||||||
info.streamingData.formats.length - 1
|
info.streamingData.formats.length - 1
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
|
||||||
if(!combinedStream || !combinedStream.url)
|
if(!combinedStream || !combinedStream.url)
|
||||||
return null;
|
return null;
|
||||||
|
67
src/youtubeItags.js
Normal file
67
src/youtubeItags.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
const Itags = {
|
||||||
|
video: {
|
||||||
|
h264: {
|
||||||
|
standard: {
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
for(let type of opts.types) {
|
||||||
|
const formats = Itags.video[opts.codec][type];
|
||||||
|
_appendItagArray(allowed.video, opts, formats);
|
||||||
|
}
|
||||||
|
|
||||||
|
return allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCombinedItags(opts)
|
||||||
|
{
|
||||||
|
const arr = [];
|
||||||
|
_appendItagArray(arr, opts, Itags.combined);
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
Reference in New Issue
Block a user