Chromecast – How to differentiate main and alternate media tracks from HLS medias? - google-cast

I'm currently facing an issue in Chromecast related to the management of alternate audio and text tracks.
I'm trying to differentiate media tracks (both audio and text) with the same language, but with different roles. According to Cast SDK's documentation, we can determine this through the roles attribute: main and alternate, subtitle and caption.
When loading HLS manifests with multiple media tracks, however, the roles attribute is always returned as undefined, so we can't determine if the role of an audio track is main or alternate, neither can we identify if the role of a text track is subtitle or caption. This error does not occur when loading DASH manifests.
Is there any other way of identifying whether an audio track's role is main or alternate in an HLS media? Following that, is there any other way of identifying whether a text track present in an HLS media is subtitle or caption?
I've created an issue on Google's IssueTracker related to this problem, but I've yet to receive any return regarding this.
Below, a demonstration of the problem. The manifests being used have two audio tracks, one main and another alternate, and two text tracks, one subtitle and the other caption. The manifest is in accordance with HTTP Live Streaming 2nd Edition.
HLS
Manifest
#EXTM3U
#EXT-X-VERSION:4
(version=1.11.14-26090)
# AUDIO groups
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-aacl-128",LANGUAGE="pt",NAME="Portuguese",DEFAULT=YES,AUTOSELECT=YES,CHANNELS="2",URI="9645657-tst007-manifest-audio_por=128000.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-aacl-128",LANGUAGE="pt",NAME="Portuguese (describes-video)",AUTOSELECT=YES,CHARACTERISTICS="public.accessibility.describes-video",CHANNELS="2",URI="9645657-tst007-manifest-audio_por_1=128000.m3u8"
# SUBTITLES groups
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="textstream",LANGUAGE="pt",NAME="Portuguese (caption)",DEFAULT=YES,AUTOSELECT=YES,CHARACTERISTICS="public.accessibility.describes-spoken-dialog,public.accessibility.describes-music-and-sound",URI="9645657-tst007-manifest-textstream_por=1000.m3u8"
# variants
#EXT-X-STREAM-INF:BANDWIDTH=1294000,CODECS="mp4a.40.2,avc1.64001F",RESOLUTION=854x480,AUDIO="audio-aacl-128",SUBTITLES="textstream",CLOSED-CAPTIONS=NONE
9645657-tst007-manifest-video_por=1097000.m3u8
# keyframes
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=146000,CODECS="avc1.64001F",RESOLUTION=854x480,URI="keyframes/9645657-tst007-manifest-video_por=1097000.m3u8"
Audio Tracks
# Calling Receiver.playerManager.AudioTracksManager.getTracks()
{
{
assocLanguage:undefined,
customData: undefined,
forced: undefined,
isInband: undefined,
language: "pt",
name: "Português"›
roles: undefined,
subtype: undefined,
trackContentId: undefined,
trackContentType: "audio/mp4",
trackId: 2,
type: "AUDIO"
},
{
assocLanguage: undefined,
customData: undefined,
forced: undefined,
isInband: undefined,
language: "pt",
name: "Português"›
roles: undefined,
subtype: undefined,
trackContentId: undefined,
trackContentType: "audio/mp4",
trackId: 3,
type: "AUDIO"
}
}
Text Tracks
# Calling Receiver.playerManager.TextTracksManager.getTracks()
{
{
assocLanguage: null,
customData: undefined,
forced: false,
isInband: undefined,
language: "pt",
name: "Português"›
roles: undefined,
subtype: undefined,
trackContentId: undefined,
trackContentType: "text/vtt",
trackId: 4,
type: "TEXT"
},
{
assocLanguage: null,
customData: undefined,
forced: false,
isInband: undefined,
language: "pt",
name: "Português"›
roles: undefined,
subtype: undefined,
trackContentId: undefined,
trackContentType: "text/vtt",
trackId: 5,
type: "TEXT"
}
}
DASH
Audio Tracks
# Calling Receiver.playerManager.AudioTracksManager.getTracks()
{
{
assocLanguage:undefined,
customData: undefined,
forced: undefined,
isInband: undefined,
language: "pt",
name: "Português"›
roles: ["alternate"],
subtype: undefined,
trackContentId: undefined,
trackContentType: "mp4a.40.2",
trackId: 1,
type: "AUDIO"
},
{
assocLanguage: undefined,
customData: undefined,
forced: undefined,
isInband: undefined,
language: "pt",
name: "Português"›
roles: ["main"],
subtype: undefined,
trackContentId: undefined,
trackContentType: "mp4a.40.2",
trackId: 2,
type: "AUDIO"
}
}
Text Tracks
# When calling Receiver.playerManager.TextTracksManager.getTracks()
{
{
assocLanguage:undefined,
customData: undefined,
forced: undefined,
isInband: undefined,
language: "pt",
name: "Português"›
roles: ["subtitle"],
subtype: "SUBTITLE",
trackContentId: undefined,
trackContentType: "application/mp4",
trackId: 3,
type: "TEXT"
},
{
assocLanguage: undefined,
customData: undefined,
forced: undefined,
isInband: undefined,
language: "pt",
name: "Português"›
roles: ["caption", "subtitle"],
subtype: "SUBTITLE",
trackContentId: undefined,
trackContentType: "application/mp4",
trackId: 4,
type: "TEXT"
}
}

Yes it's all good. Let me please cast. We're trying to enjoy this show.please

Related

How to differentiate folders from files in storage.getFiles() response?

This is what I have in my bucket:
images/
image1.jpeg
I'm using the storage.bucket().getFiles() method:
const images = await storage.bucket().getFiles({ prefix: 'images' });
console.log(images);
And this is the response I'm getting:
You can see that I get 2 items.
1 for the images folder itself
And 1 for the actual image1.jpeg file
[
[
File {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
metadata: [Object],
baseUrl: '/o',
parent: [Bucket],
id: 'images%2F',
createMethod: undefined,
methods: [Object],
interceptors: [],
projectId: undefined,
create: undefined,
bucket: [Bucket],
storage: [Storage],
kmsKeyName: undefined,
userProject: undefined,
name: 'images/',
acl: [Acl],
instanceRetryValue: true,
instancePreconditionOpts: undefined,
[Symbol(kCapture)]: false
},
File {
_events: [Object: null prototype] {},
_eventsCount: 0,
_maxListeners: undefined,
metadata: [Object],
baseUrl: '/o',
parent: [Bucket],
id: 'images%2Fimage1.jpeg',
createMethod: undefined,
methods: [Object],
interceptors: [],
projectId: undefined,
create: undefined,
bucket: [Bucket],
storage: [Storage],
kmsKeyName: undefined,
userProject: undefined,
name: 'images/image1.jpeg',
acl: [Acl],
instanceRetryValue: true,
instancePreconditionOpts: undefined,
[Symbol(kCapture)]: false
}
]
]
I would like to loop over the files in images[0] and be able to differentiate the folder from the files. How can I do it?
Tried this on live data in this document
with { prefix: 'images' } the entire bucket under the prefix is returned.
with { prefix: 'images/' } the entire folder under the prefix is returned.
with { prefix: 'images/image'} restrict the results to only the "files" in the given "folder"
If you want to list only files in the given folder try changing the prefix like this:
const images = await storage.bucket().getFiles({ prefix: 'images/image' });

Channel cache is incoherent

I've built a few functions to table channel & category information, one of which runs when the bot starts to make sure everything is synced.
The problem I've run into is that bot.channels.cache contains channels that no longer exist, or states of a channel that no longer exists. For example, I only have one channel in the server called "general". Yet, there are 3 separate entries for that channel by name, and only one contains the ID (711043006781849686) of the current "general" channel:
import Discord from 'discord.js'
import config from '../config.js'
const bot = new Discord.Client({ partials: ['MESSAGE', 'CHANNEL', 'REACTION'] })
bot.login(config.botToken)
bot.on('ready', async () => {
console.log(bot.channels.cache)
}
-- returns --
Collection(46) [Map] {
...
'711043006781849686' => <ref *16> TextChannel {
type: 'text',
deleted: false,
id: '711043006781849686',
name: 'general',
rawPosition: 10,
parentID: '711043007197216880',
permissionOverwrites: Collection(3) [Map] {
'711043006253367426' => [PermissionOverwrites],
'711043006295179347' => [PermissionOverwrites],
'861109585930747934' => [PermissionOverwrites]
},
topic: 'General chat channel.',
nsfw: false,
lastMessageID: '860794574707752980',
rateLimitPerUser: 0,
lastPinTimestamp: null,
guild: Guild {
members: [GuildMemberManager],
channels: [GuildChannelManager],
roles: [RoleManager],
presences: [PresenceManager],
voiceStates: [VoiceStateManager],
deleted: false,
available: true,
id: '711043006253367426',
shardID: 0,
name: 'Omegabox',
icon: null,
splash: null,
discoverySplash: null,
region: 'us-central',
memberCount: 5,
large: false,
features: [Array],
applicationID: null,
afkTimeout: 900,
afkChannelID: '711043009944223832',
systemChannelID: '711043006781849686',
embedEnabled: undefined,
premiumTier: 0,
premiumSubscriptionCount: 0,
verificationLevel: 'NONE',
explicitContentFilter: 'DISABLED',
mfaLevel: 0,
joinedTimestamp: 1589597389528,
defaultMessageNotifications: 'ALL',
systemChannelFlags: [SystemChannelFlags],
maximumMembers: 100000,
maximumPresences: null,
approximateMemberCount: null,
approximatePresenceCount: null,
vanityURLCode: null,
vanityURLUses: null,
description: null,
banner: null,
rulesChannelID: null,
publicUpdatesChannelID: null,
preferredLocale: 'en-US',
ownerID: '598729034867933195',
emojis: [GuildEmojiManager]
},
messages: MessageManager {
cacheType: [class LimitedCollection extends Collection],
cache: [LimitedCollection [Map]],
channel: [Circular *16]
},
_typing: Map(0) {}
},
...
'827343616678559757' => <ref *33> TextChannel {
type: 'text',
deleted: false,
id: '827343616678559757',
name: 'general',
rawPosition: 0,
parentID: '827343616678559755',
permissionOverwrites: Collection(0) [Map] {},
topic: null,
lastMessageID: '830245759152291860',
rateLimitPerUser: 0,
lastPinTimestamp: null,
guild: Guild {
members: [GuildMemberManager],
channels: [GuildChannelManager],
roles: [RoleManager],
presences: [PresenceManager],
voiceStates: [VoiceStateManager],
deleted: false,
available: true,
id: '827343616678559754',
shardID: 0,
name: 'Megabox Emojis 1',
icon: null,
splash: null,
discoverySplash: null,
region: 'us-west',
memberCount: 3,
large: false,
features: [],
applicationID: null,
afkTimeout: 300,
afkChannelID: null,
systemChannelID: '827343616678559757',
embedEnabled: undefined,
premiumTier: 0,
premiumSubscriptionCount: 0,
verificationLevel: 'NONE',
explicitContentFilter: 'DISABLED',
mfaLevel: 0,
joinedTimestamp: 1617380998194,
defaultMessageNotifications: 'ALL',
systemChannelFlags: [SystemChannelFlags],
maximumMembers: 100000,
maximumPresences: null,
approximateMemberCount: null,
approximatePresenceCount: null,
vanityURLCode: null,
vanityURLUses: null,
description: null,
banner: null,
rulesChannelID: null,
publicUpdatesChannelID: null,
preferredLocale: 'en-US',
ownerID: '598729034867933195',
emojis: [GuildEmojiManager]
},
messages: MessageManager {
cacheType: [class LimitedCollection extends Collection],
cache: [LimitedCollection [Map]],
channel: [Circular *33]
},
nsfw: false,
_typing: Map(0) {}
},
...
'827344454259703842' => <ref *34> TextChannel {
type: 'text',
deleted: false,
id: '827344454259703842',
name: 'general',
rawPosition: 0,
parentID: '827344454259703840',
permissionOverwrites: Collection(0) [Map] {},
topic: null,
lastMessageID: '827580681730261032',
rateLimitPerUser: 0,
lastPinTimestamp: null,
guild: Guild {
members: [GuildMemberManager],
channels: [GuildChannelManager],
roles: [RoleManager],
presences: [PresenceManager],
voiceStates: [VoiceStateManager],
deleted: false,
available: true,
id: '827344454259703838',
shardID: 0,
name: 'Megabox Emojis 2',
icon: null,
splash: null,
discoverySplash: null,
region: 'us-west',
memberCount: 3,
large: false,
features: [],
applicationID: null,
afkTimeout: 300,
afkChannelID: null,
systemChannelID: '827344454259703842',
embedEnabled: undefined,
premiumTier: 0,
premiumSubscriptionCount: 0,
verificationLevel: 'NONE',
explicitContentFilter: 'DISABLED',
mfaLevel: 0,
joinedTimestamp: 1617381010142,
defaultMessageNotifications: 'ALL',
systemChannelFlags: [SystemChannelFlags],
maximumMembers: 100000,
maximumPresences: null,
approximateMemberCount: null,
approximatePresenceCount: null,
vanityURLCode: null,
vanityURLUses: null,
description: null,
banner: null,
rulesChannelID: null,
publicUpdatesChannelID: null,
preferredLocale: 'en-US',
ownerID: '598729034867933195',
emojis: [GuildEmojiManager]
},
messages: MessageManager {
cacheType: [class LimitedCollection extends Collection],
cache: [LimitedCollection [Map]],
channel: [Circular *34]
},
nsfw: false,
_typing: Map(0) {}
}
}
I'm aware that there is a category named "General" (ID: 711043007197216880), but the casing is different which is maintained in an entry. I've filtered that one out, along with everything else that isn't "general" from the above block.
What am I missing here? Possible to sync things up?
Always have an epiphany right after finally making a post.
Been at this for hours and never realized that in this specific call I'm not specifying what guild, or rather what server. The bot is in multiple servers, and I've already got the current server's ID in the config I'm working with.
Changed bot.channels.cache to bot.guilds.cache.get(config.guildId).channels.cache.

console.log returns "Object" for some fields, instead of body data [spotify-web-api-node]

I'm using the spotify-web-api-node and when I do api calls, the returned data looks like that in the console:
Album information {
album_type: 'album',
artists: [
{
external_urls: [Object],
href: 'https://api.spotify.com/v1/artists/2hazSY4Ef3aB9ATXW7F5w3',
id: '2hazSY4Ef3aB9ATXW7F5w3',
name: 'IZAL',
type: 'artist',
uri: 'spotify:artist:2hazSY4Ef3aB9ATXW7F5w3'
}
],
...
label: 'Hook Ediciones Musicales',
name: 'Agujeros de Gusano',
popularity: 0,
release_date: '2014-01-25',
release_date_precision: 'day',
total_tracks: 13,
tracks: {
offset=0&limit=50',
items: [
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object], [Object],
[Object]
],
limit: 50,
next: null,
offset: 0,
previous: null,
total: 13
},
type: 'album',
uri: 'spotify:album:5U4W9E5WsYb2jUQWePT8Xm'
}
As you can see, some fields like external_urls: and (later) items: have [Object] returned, instead of the actual data.
The (node.js) code for that particular example is the following:
spotifyApi.getAlbum('5U4W9E5WsYb2jUQWePT8Xm')
.then(function(data) {
console.log('Album information',data.body);
}, function(err) {
console.error(err);
});
If I change the data.body part in console.log to JSON.stringify(data.body) it is somewhat better, as it does return the data, but as a string (obviously), which not exactly... useable:
Album information {"album_type":"album","artists":[{"external_urls":{"spotify":"https://open.spotify.com/artist/2hazSY4Ef3aB9ATXW7F5w3"},"href":"https://api.spotify.com/v1/artists/2hazSY4Ef3aB9ATXW7F5w3","id":"2hazSY4Ef3aB9ATXW7F5w3","name":"IZAL","type":"artist","uri":"spotify:artist:2hazSY4Ef3aB9ATXW7F5w3"}],"available_markets":[],"copyrights":[{"text":"2013 Hook Ediciones Musicales","type":"C"},{"text":"2013 Hook Ediciones Musicales","type":"P"}],
etc etc. Also tried JSON.parse(JSON.stringify(data.body)) but I get the same output as with console.log.
How can I get the field's actual data, without losing its format?
You can use console.log(JSON.stringify(data.body, null, 2)).
That prints JSON in tree without losing its content like you see in [Object].
Following #snak 's comment, I changed the console.log line to:
console.log('Album information',util.inspect(data.body, false, null, true));
and it seems to return everything fine, no [Object].
(Make sure to include const util = require('util'); and install it with npm install util --save.)

Nodejs spread operator return some unexpected keys in returned object [duplicate]

This question already has answers here:
How do you turn a Mongoose document into a plain object?
(9 answers)
Closed 2 years ago.
There I have res is original object
{
time: 2020-07-26T10:39:38.135Z,
isTransfered: true,
_id: 5f1d5d6b60755e75b48770a6,
receiverAccountNumber: '12345678',
transfererAccountNumber: '11111111',
receiverName: 'Lê Công Tuyền',
transfererName: 'Nguyễn Thanh Tuấn',
amount: 1000000,
content: "test chuyefo'seajfg",
payFeeBy: 'transferer',
type: { name: 'internal', bankCode: 'TUB' },
__v: 0
}
And I got this result (called res2) is returned object using spread operator res2 = {...res} :
{
'$__': InternalCache {
strictMode: true,
selected: {},
// alot of key-value
'$setCalled': Set(0) {},
ownerDocument: undefined,
fullPath: undefined,
emitter: EventEmitter {
_events: [Object: null prototype],
_eventsCount: 2,
_maxListeners: 0,
[Symbol(kCapture)]: false
},
'$options': { skipId: true, isNew: false, willInit: true }
},
isNew: false,
errors: undefined,
_doc: {
time: 2020-07-26T10:39:38.135Z,
isTransfered: true,
_id: 5f1d5d6b60755e75b48770a6,
receiverAccountNumber: '12345678',
transfererAccountNumber: '11111111',
receiverName: 'Lê Công Tuyền',
transfererName: 'Nguyễn Thanh Tuấn',
amount: 1000000,
content: "test chuyefo'seajfg",
payFeeBy: 'transferer',
type: { name: 'internal', bankCode: 'TUB' },
__v: 0
},
'$locals': {},
'$op': null,
'$init': true
}
I really dont know about this behavior of spread operator, that a lot of new key-value generated and the object I want to get is in _doc key.
Code is run on Nodejs v12 (nvm use 12)
This is probably because the res here is a mongoose document which has these values.
When you are doing a mongoose query by default it returns a document object. In order to receive a plain object use lean(). If you use lean then you won't be getting these unnecessary data when using spread operator.
Schema.findOne(query).lean()
However, if you need a document object from mongoose then in this case you can try the following to get rid of other values you don't need.
let res2 = { ...res.toObject() };

Javascript Object properties (in Node) not getting logged or logged with a different name

I am using Node's formidable package from felixge. This is more a Javascript question than one specific to formidable, or so I think.
If I do a console.log on MYOBJ, I get the following:
{ file1:
File {
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
size: 62464,
path: 'myDir/upload_e79d8d551721e2f399afbc39d5d5eaab.doc',
name: 'somefile.doc',
type: 'application/msword',
hash: null,
lastModifiedDate: Thu May 19 2016 20:22:24 GMT+0530 (IST),
_writeStream:
WriteStream {
_writableState: [Object],
writable: true,
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
path: 'uploadDir/upload_1e0e9625e27f5c5172eaf5d18172f946.doc',
fd: null,
flags: 'w',
mode: 438,
start: undefined,
pos: undefined,
bytesWritten: 62464,
closed: true } } }
If I do a console.log, like so:
for (var filename in MYOBJ)
console.log(MYOBJ[filename]);
I get the following:
{ size: 62464,
path: 'myDir/upload_e79d8d551721e2f399afbc39d5d5eaab.doc',
name: 'somefile.doc',
type: 'application/msword',
mtime: '2016-05-19T14:52:24.129Z' }
My obvious question is:
Why are the other properties of "file1" not displayed? May be they are not the Object's "ownProperty"? Even so, why does "lastModifiedDate" become "mtime?"
Let me guess ... probably implementation for cosnole.log uses Object.getOwnPropertyDescriptor() and can list all non-enumerable values
var o = {}
Object.defineProperty(o, 'nonEnumerableValue', {value: 1})
console.log(o) // {}
console.log(o.nonEnumerableValue) // 1

Resources