Skip to content

Commit

Permalink
Merge pull request #241 from internet4000/feat/track-dead-link
Browse files Browse the repository at this point in the history
feat: add notification on track when its media is not available
  • Loading branch information
hugurp committed Jul 16, 2018
2 parents a9ea840 + 5fb1063 commit be32b86
Show file tree
Hide file tree
Showing 14 changed files with 128 additions and 26 deletions.
6 changes: 6 additions & 0 deletions app/components/track-contextual/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,11 @@
<optgroup label="Manage">
<option value="editTrack">Edit</option>
</optgroup>
{{#if track.mediaNotAvailable}}
<optgroup label="Suggestions">
<option value="editTrack">-> Fix broken media URL</option>
</optgroup>
{{/if}}
{{/if}}

</select>
10 changes: 10 additions & 0 deletions app/components/track-edit/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import TrackFormComponent from 'radio4000/components/track-form/component'

const {
get,
set,
computed} = Ember

export default TrackFormComponent.extend({
Expand All @@ -15,6 +16,10 @@ export default TrackFormComponent.extend({

disableSubmit: computed.oneWay('track.validations.isInvalid'),

youtubeSearchUrl: computed('track.title', function() {
return `https://www.youtube.com/results?search_query=${this.get('track.title')}`
}),

submitTask: task(function * (event) {
event.preventDefault()
yield get(this, 'track.update').perform()
Expand All @@ -27,5 +32,10 @@ export default TrackFormComponent.extend({
yield get(this, 'track.delete').perform()
get(this, 'flashMessages').success('Track deleted')
yield get(this, 'onDelete')()
}).drop(),

setMediaAvailable: task(function * (event) {
event.preventDefault()
yield set(this, 'track.mediaNotAvailable', false)
}).drop()
})
23 changes: 18 additions & 5 deletions app/components/track-edit/template.hbs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{{#form-group
model=track
valuePath="url"
label="URL"
hint="Paste in the URL of a YouTube video" as |value|}}
model=track
valuePath="url"
label="URL"
hint="Paste in the URL of a YouTube video" as |value|}}
{{focus-input
type="url"
value=value
Expand All @@ -12,6 +12,19 @@
autoSelect=true}}
{{/form-group}}

{{#if track.mediaNotAvailable}}
<p>
This <a href={{track.url}} target="_blank" rel="noopener">URL</a> seems to be broken.<br>
<a href={{youtubeSearchUrl}} target="_blank" rel="noopener">Find a new one</a> or
<button
class="Btn Btn--small"
title="Signal that the link is ok and maybe just unavailable temporarily (ex: in the country you are currently)."
onclick={{perform setMediaAvailable}}>
ignore
</button>
</p>
{{/if}}

{{#form-group
model=track
valuePath="title"
Expand Down Expand Up @@ -39,7 +52,7 @@
type="submit"
class="Btn Btn--primary"
title="Save the track"
disabled={{submitTask.isRunning}}>
disabled={{submitDisabled}}>
{{if submitTask.isIdle "Save" "Saving…"}}
</button>

Expand Down
23 changes: 10 additions & 13 deletions app/components/track-form/component.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import Ember from 'ember';
import config from 'radio4000/config/environment';
import youtubeUrlToId from 'radio4000/utils/youtube-url-to-id';
import {fetchTitle} from 'radio4000/utils/youtube-api';
import {task, timeout} from 'ember-concurrency';

const {Component, debug, get, set, observer} = Ember;
const {Component, get, set, observer, computed} = Ember;

export default Component.extend({
tagName: 'form',
Expand Down Expand Up @@ -43,19 +43,16 @@ export default Component.extend({
}
}),

submitDisabled: computed('track.hasDirtyAttributes', 'submitTask', function() {
return !this.get('track.hasDirtyAttributes') || this.get('submitTask.isRunning')
}),

fetchTitle: task(function * () {
yield timeout(250); // throttle
const track = get(this, 'track');
const ytid = track.get('ytid');
const url = `https://www.googleapis.com/youtube/v3/videos?id=${ytid}&key=${config.youtubeApiKey}&fields=items(id,snippet(title))&part=snippet`;
const response = yield fetch(url);
const data = yield response.json();
if (!data.items.length) {
debug('Could not find title for track');
return;
}
const title = data.items[0].snippet.title;
track.set('title', title);
const track = get(this, 'track')
const ytid = track.get('ytid')
let title = yield fetchTitle(ytid)
track.set('title', title)
}).restartable(),

submitTask: task(function * () {
Expand Down
8 changes: 6 additions & 2 deletions app/components/track-item/component.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Ember from 'ember'

const { Component, get, set, inject } = Ember
const { Component, get, set, inject, computed } = Ember

export default Component.extend({
player: inject.service(),
Expand All @@ -11,14 +11,18 @@ export default Component.extend({
// 'isCurrent',
'track.liveInCurrentPlayer:Track--live',
'track.playedInCurrentPlayer:Track--played',
'track.finishedInCurrentPlayer:Track--finished'
'track.finishedInCurrentPlayer:Track--finished',
'mediaNotAvailable:Track--mediaNotAvailable'
],

attributeBindings: [
'track.ytid:data-pid',
'track.id:data-track-id'
],

canEdit: computed.reads('track.channel.canEdit'),
mediaNotAvailable: computed.and('track.mediaNotAvailable', 'canEdit'),

actions: {
onEdit(track) {
// get(this, 'router').transitionTo('channel.tracks.track.edit', track)
Expand Down
2 changes: 1 addition & 1 deletion app/components/track-item/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

{{track-contextual
track=track
canEdit=track.channel.canEdit
canEdit=canEdit
onEdit=(action "onEdit")
onCopyTrack=(action "copyTrack")}}

Expand Down
6 changes: 6 additions & 0 deletions app/components/x-playback/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ export default Component.extend({
const options = {passive: false}
player.addEventListener('trackChanged', event => this.onTrackChanged(event), options)
player.addEventListener('trackEnded', event => this.onTrackEnded(event), options)
player.addEventListener('mediaNotAvailable', event => this.onMediaNotAvailable(event), options)
player.addEventListener('channelChanged', event => this.onChannelChanged(event), options)
},

/* events */
onTrackChanged(event) {
get(this, 'player').onTrackChanged(event.detail[0]);
},
Expand All @@ -35,6 +37,10 @@ export default Component.extend({
get(this, 'player').onTrackEnded(event.detail[0]);
},

onMediaNotAvailable(event) {
get(this, 'player').onMediaNotAvailable(event.detail[0]);
},

actions: {
toggleMinimizedFormat() {
get(this, 'uiStates').toggleMinimizedFormat();
Expand Down
2 changes: 1 addition & 1 deletion app/components/x-playback/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
track-id={{track.id}}
title="Radio4000 player. [⌨ g c = current channel] [⌨ g x = curent track]"
></radio4000-player>
{{!-- <script src="http://localhost:4002/radio4000-player.min.js"></script> --}}
<!-- <script src="http://localhost:4002/radio4000-player.min.js"></script> -->
</div>

<div class="Playback-layoutButtons">
Expand Down
12 changes: 12 additions & 0 deletions app/models/track.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import firebase from 'firebase';
import {task} from 'ember-concurrency';
import {validator, buildValidations} from 'ember-cp-validations';
import youtubeUrlToId from 'radio4000/utils/youtube-url-to-id';
import {fetchTrackAvailability} from 'radio4000/utils/youtube-api';
import format from 'npm:date-fns/format';

const {Model, attr, belongsTo} = DS;
Expand Down Expand Up @@ -45,6 +46,10 @@ export default Model.extend(Validations, {
body: attr('string'),
ytid: attr('string'),

mediaNotAvailable: attr('boolean', {
defaultValue: false
}),

// relationships
channel: belongsTo('channel', {
async: true,
Expand Down Expand Up @@ -87,10 +92,17 @@ export default Model.extend(Validations, {
},

// In case url changed, we need to set the ytid.
// and also check if the track is available on the provider
update: task(function * () {
if (!get(this, 'hasDirtyAttributes')) {
return
}

const ytid = this.get('ytid')

let isAvailable = yield fetchTrackAvailability(ytid)
this.set('mediaNotAvailable', !isAvailable)

yield this.updateYoutubeId();
yield this.save()
}).drop(),
Expand Down
13 changes: 13 additions & 0 deletions app/services/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,19 @@ export default Service.extend({
});
},

onMediaNotAvailable(event) {
let channel = this.get('session.currentUser.channels.firstObject')
get(this, 'store').findRecord('track', event.track.id)
.then(track => {
// only set media as not-available for track owner (for now)
const isOwner = channel.get('id') === track.get('channel.id')
if (isOwner) {
track.set('mediaNotAvailable', true);
track.save();
}
});
},

channelChanged(previousChannelId, channelId) {
// set previous channel as not active
if (previousChannelId !== undefined) {
Expand Down
3 changes: 3 additions & 0 deletions app/styles/components/_track.scss
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@
.Track--finished {
border-right-color: $green;
}
.Track--mediaNotAvailable {
border-right-color: $red;
}

.Track-title {
@extend %font-regular;
Expand Down
38 changes: 38 additions & 0 deletions app/utils/youtube-api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Ember from 'ember';
import config from 'radio4000/config/environment';
const {debug} = Ember;

const fetchYoutubeTrack = async (ytid, fields) => {
if (!ytid) {
throw Error('You need to provide a youtube video to fetchYoutubeTrack')
}
if (!fields) {
fields = `items(id,snippet,contentDetails,statistics)&part=snippet,contentDetails,statistics`;
}

let rootUrl = `https://www.googleapis.com/youtube/v3/videos?key=${config.youtubeApiKey}`
let url = rootUrl + `&id=${ytid}&fields=${fields}`
let response = await fetch(url);
let data = await response.json();
return data
}

const fetchTitle = async function (ytid) {
let data = await fetchYoutubeTrack(ytid, 'items(id,snippet(title))&part=snippet');

if (!data.items.length) {
debug('Could not find title for track');
return;
}

return data.items[0] && data.items[0].snippet.title
}

const fetchTrackAvailability = async function (ytid) {
// let data = await fetchYoutubeTrack(ytid, 'items(id,status)&part=status')
let data = await fetchYoutubeTrack(ytid, 'items(id,snippet(title))&part=snippet')

return Boolean(data.items.length)
}

export {fetchTitle, fetchTrackAvailability};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"emberfire": "^2.0.10",
"js-cookie": "^2.2.0",
"lazysizes": "^4.0.1",
"radio4000-player": "^0.5.0",
"radio4000-player": "^0.5.2",
"torii": "^0.10.1",
"youtube-regex": "^1.0.4"
}
Expand Down
6 changes: 3 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6856,9 +6856,9 @@ qunit@^2.5.0:
resolve "1.5.0"
walk-sync "0.3.2"

radio4000-player@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/radio4000-player/-/radio4000-player-0.5.0.tgz#07f79102b1ef01a644fb0d0f17e9dea5d470ccb8"
radio4000-player@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/radio4000-player/-/radio4000-player-0.5.2.tgz#962610cba7b9e4a3d7d41d556c76e7a9af23a753"
dependencies:
document-register-element "^1.8.1"
lodash.debounce "^4.0.8"
Expand Down

0 comments on commit be32b86

Please sign in to comment.