Skip to content

Commit

Permalink
data: rewrite twitch-stream model
Browse files Browse the repository at this point in the history
  • Loading branch information
bastimeyer committed Jan 12, 2022
1 parent 41eb149 commit 2c79f3f
Show file tree
Hide file tree
Showing 6 changed files with 381 additions and 388 deletions.
14 changes: 13 additions & 1 deletion src/app/data/models/twitch/stream/adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,17 @@ import TwitchAdapter from "data/models/twitch/adapter";

export default TwitchAdapter.extend({
coalesceFindRequests: true,
findManyIdString: "channel"
findIdParam: "user_id",

/**
* @param {DS.Store} store
* @param {DS.Model} type
* @param {Object} query
* @return {Promise}
*/
queryRecord( store, type, query ) {
const url = this._buildURL( type );

return this.ajax( url, "GET", { data: query } );
}
});
167 changes: 73 additions & 94 deletions src/app/data/models/twitch/stream/model.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,56 @@
import { get, computed } from "@ember/object";
import { and } from "@ember/object/computed";
import { inject as service } from "@ember/service";
import attr from "ember-data/attr";
import Model from "ember-data/model";
import { belongsTo } from "ember-data/relationships";
import { DEFAULT_VODCAST_REGEXP } from "data/models/settings/streams/fragment";


/**
* @typedef {Object} FPSRange
* @property {Number} target
* @property {Number} min
* @property {Number} max
*/

/**
* Try to fix the weird fps numbers received from twitch...
* Define a list of common stream frame rates and give each one a min and max value range.
* @type {FPSRange[]}
*/
const fpsRanges = [
{ target: 24, min: 22, max: 24.5 },
{ target: 25, min: 24.5, max: 27 },
{ target: 30, min: 28, max: 32 },
{ target: 45, min: 43, max: 46.5 },
{ target: 48, min: 46.5, max: 49.5 },
{ target: 50, min: 49.5, max: 52 },
{ target: 60, min: 58, max: 62.5 },
{ target: 72, min: 70, max: 74 },
{ target: 90, min: 88, max: 92 },
{ target: 100, min: 98, max: 102 },
{ target: 120, min: 118, max: 122 },
{ target: 144, min: 142, max: 146 }
];

const reRerun = /rerun|watch_party/;


export default Model.extend({
const reLang = /^([a-z]{2})(:?-([a-z]{2}))?$/;


// noinspection JSValidateTypes
export default Model.extend( /** @class TwitchStream */ {
/** @type {IntlService} */
intl: service(),
/** @type {SettingsService} */
settings: service(),


average_fps: attr( "number" ),
broadcast_platform: attr( "string" ),
channel: belongsTo( "twitchChannel", { async: false } ),
created_at: attr( "date" ),
delay: attr( "number" ),
game: attr( "string" ),
//is_playlist: attr( "boolean" ),
preview: belongsTo( "twitchImage", { async: false } ),
stream_type: attr( "string" ),
video_height: attr( "number" ),
viewers: attr( "number" ),


/** @type {TwitchUser} */
user: belongsTo( "twitch-user", { async: true } ),
/** @type {TwitchChannel} */
channel: belongsTo( "twitch-channel", { async: true } ),
/** @type {string} */
user_login: attr( "string" ),
/** @type {string} */
user_name: attr( "string" ),
/** @type {TwitchGame} */
game: belongsTo( "twitch-game", { async: true } ),
/** @type {string} */
game_name: attr( "string" ),
/** @type {string} */
type: attr( "string" ),
/** @type {string} */
title: attr( "string" ),
/** @type {number} */
viewer_count: attr( "number" ),
/** @type {Date} */
started_at: attr( "date" ),
/** @type {string} */
language: attr( "string" ),
/** @type {string} */
thumbnail_url: attr( "string" ),
//** @type {TwitchStreamTag[]} */
//tag_ids: hasMany( "twitch-stream-tag", { async: true } ),
/** @type {boolean} */
is_mature: attr( "boolean" ),


/** @type {(null|RegExp)} */
reVodcast: computed( "settings.content.streams.vodcast_regexp", function() {
const vodcast_regexp = get( this, "settings.content.streams.vodcast_regexp" );
/** @this {TwitchStream} */
const vodcast_regexp = this.settings.content.streams.vodcast_regexp;

if ( vodcast_regexp.length && !vodcast_regexp.trim().length ) {
return null;
}
Expand All @@ -68,69 +61,55 @@ export default Model.extend({
}
}),

// both properties are not documented in the v5 API
isVodcast: computed(
"broadcast_platform",
"stream_type",
"reVodcast",
"channel.status",
function() {
if (
reRerun.test( get( this, "broadcast_platform" ) )
|| reRerun.test( get( this, "stream_type" ) )
) {
return true;
}

const reVodcast = get( this, "reVodcast" );
const status = get( this, "channel.status" );

return reVodcast && status
? reVodcast.test( status )
: false;
}
),


hasFormatInfo: and( "video_height", "average_fps" ),
/** @type {boolean} */
isVodcast: computed( "reVodcast", "title", function() {
/** @this {TwitchStream} */
const { reVodcast, title } = this;

return reVodcast && title && reVodcast.test( title );
}),

/** @type {string} */
titleCreatedAt: computed( "intl.locale", "created_at", function() {
/** @this {TwitchStream} */
const { created_at } = this;
const last24h = new Date() - created_at < 24 * 3600 * 1000;
return last24h
? this.intl.t( "models.twitch.stream.created-at.less-than-24h", { created_at } )
: this.intl.t( "models.twitch.stream.created-at.more-than-24h", { created_at } );
}),

titleViewers: computed( "intl.locale", "viewers", function() {
return this.intl.t( "models.twitch.stream.viewers", { count: this.viewers } );
/** @type {string} */
titleViewers: computed( "intl.locale", "viewer_count", function() {
/** @this {TwitchStream} */
return this.intl.t( "models.twitch.stream.viewers", { count: this.viewer_count } );
}),

resolution: computed( "video_height", function() {
// assume 16:9
const video_height = get( this, "video_height" );
const width = Math.round( ( 16 / 9 ) * video_height );
const height = Math.round( video_height );
/** @type {boolean} */
hasLanguage: computed( "language", function() {
/** @this {TwitchStream} */
const { language } = this;

return `${width}x${height}`;
return !!language && language !== "other";
}),

fps: computed( "average_fps", function() {
const average_fps = get( this, "average_fps" );

if ( !average_fps ) { return null; }

const fpsRange = fpsRanges.find( fpsRange =>
average_fps > fpsRange.min
&& average_fps <= fpsRange.max
);

return fpsRange
? fpsRange.target
: Math.floor( average_fps );
/** @type {boolean} */
hasBroadcasterLanguage: computed( "language", "channel.broadcaster_language", function() {
/** @this {TwitchStream} */
const { language } = this;
// Ember.get() required here for ObjectProxy access
const broadcaster_language = get( this, "channel.broadcaster_language" );

const mLanguage = reLang.exec( language );
const mBroadcaster = reLang.exec( broadcaster_language );

// show the broadcaster_language only if it is set and
// 1. the language is not set or
// 2. the language differs from the broadcaster_language
// WITHOUT comparing both lang variants
return !!mBroadcaster && ( !mLanguage || mLanguage[1] !== mBroadcaster[1] );
})

}).reopenClass({
toString() { return "kraken/streams"; }
toString() { return "helix/streams"; }
});
24 changes: 6 additions & 18 deletions src/app/data/models/twitch/stream/serializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,15 @@ import TwitchSerializer from "data/models/twitch/serializer";

export default TwitchSerializer.extend({
modelNameFromPayloadKey() {
return "twitchStream";
},

attrs: {
channel: { deserialize: "records" },
preview: { deserialize: "records" }
return "twitch-stream";
},

normalize( modelClass, resourceHash, prop ) {
const foreignKeyChannel = this.store.serializerFor( "twitchChannel" ).primaryKey;
const foreignKeyImage = this.store.serializerFor( "twitchImage" ).primaryKey;

// get the id of the embedded TwitchChannel record and apply it here
// this is required for refreshing the record so that the correct id is being used
const id = resourceHash.channel[ foreignKeyChannel ];
resourceHash[ this.primaryKey ] = id;

// apply the id of this record on the embedded TwitchImage record (preview)
if ( resourceHash.preview ) {
resourceHash.preview[ foreignKeyImage ] = `stream/preview/${id}`;
}
resourceHash.user = resourceHash.user_id;
resourceHash.channel = resourceHash.user_id;
resourceHash.game = resourceHash.game_id;
delete resourceHash[ "user_id" ];
delete resourceHash[ "game_id" ];

return this._super( modelClass, resourceHash, prop );
}
Expand Down
82 changes: 0 additions & 82 deletions src/test/fixtures/data/models/twitch/stream.json

This file was deleted.

Loading

0 comments on commit 2c79f3f

Please sign in to comment.