@saooti/octopus-sdk 36.0.9 → 36.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/public/img/tempRadio.jpg +0 -0
- package/src/assets/general.scss +9 -0
- package/src/assets/iframe.scss +11 -0
- package/src/assets/share.scss +38 -0
- package/src/assets/transition.scss +2 -3
- package/src/components/display/edit/EditBoxRadio.vue +11 -0
- package/src/components/display/list/SwiperList.vue +109 -0
- package/src/components/display/live/LiveItem.vue +26 -242
- package/src/components/display/live/LiveList.vue +111 -189
- package/src/components/display/live/RadioCurrently.vue +108 -0
- package/src/components/display/live/RadioImage.vue +84 -0
- package/src/components/display/live/RadioItem.vue +44 -0
- package/src/components/display/live/RadioList.vue +72 -0
- package/src/components/display/playlist/PlaylistList.vue +1 -0
- package/src/components/display/podcasts/PodcastImage.vue +15 -48
- package/src/components/display/podcasts/PodcastItem.vue +3 -0
- package/src/components/display/podcasts/PodcastSwiperList.vue +9 -80
- package/src/components/display/sharing/ShareButtons.vue +1 -0
- package/src/components/display/sharing/SharePlayer.vue +2 -10
- package/src/components/display/sharing/SharePlayerColors.vue +1 -1
- package/src/components/display/sharing/SharePlayerRadio.vue +102 -0
- package/src/components/misc/LeftMenu.vue +1 -1
- package/src/components/misc/ProgressBar.vue +4 -0
- package/src/components/misc/TopBar.vue +1 -1
- package/src/components/misc/modal/ShareModalPlayer.vue +2 -2
- package/src/components/misc/player/PlayerCompact.vue +1 -0
- package/src/components/mixins/player/playerDisplay.ts +27 -10
- package/src/components/mixins/radio/fetchRadioData.ts +47 -0
- package/src/components/pages/Lives.vue +22 -48
- package/src/components/pages/Playlist.vue +9 -2
- package/src/components/pages/Radio.vue +112 -0
- package/src/locale/de.ts +8 -3
- package/src/locale/en.ts +10 -5
- package/src/locale/es.ts +8 -3
- package/src/locale/fr.ts +10 -5
- package/src/locale/it.ts +8 -3
- package/src/locale/sl.ts +8 -3
- package/src/router/router.ts +10 -0
- package/src/stores/ParamSdkStore.ts +12 -12
- package/src/stores/PlayerStore.ts +8 -0
- package/src/stores/class/general/player.ts +9 -7
- package/src/stores/class/general/playlist.ts +2 -0
- package/src/stores/class/radio/canal.ts +9 -0
- package/src/stores/class/radio/live.ts +34 -0
- package/src/stores/class/radio/mix.ts +23 -0
- package/src/stores/class/radio/playlistMedia.ts +10 -0
- package/src/stores/class/radio/recurrence.ts +76 -0
|
@@ -1,35 +1,45 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div
|
|
3
3
|
v-if="filterOrgaId || organisationId"
|
|
4
|
-
class="d-flex flex-column align-items-
|
|
4
|
+
class="d-flex flex-column align-items-start mt-3"
|
|
5
5
|
>
|
|
6
|
+
<div class="d-flex justify-content-between amlign-items-center">
|
|
7
|
+
<h2 class="mb-0 big-h2 mb-3">{{ $t('Live') }}</h2>
|
|
8
|
+
<router-link
|
|
9
|
+
v-if="liveRight && !isPodcastmaker"
|
|
10
|
+
to="/main/priv/edit/live"
|
|
11
|
+
>
|
|
12
|
+
<button class="btn btn-primary">
|
|
13
|
+
{{ $t('Launch a new live') }}
|
|
14
|
+
</button>
|
|
15
|
+
</router-link>
|
|
16
|
+
</div>
|
|
17
|
+
<ClassicSelect
|
|
18
|
+
v-if="lives.length || 'ALL'!==selectedStatus"
|
|
19
|
+
v-model:textInit="selectedStatus"
|
|
20
|
+
id-select="status-live-chooser-select"
|
|
21
|
+
:label="$t('Selection by status')"
|
|
22
|
+
:displayLabel="false"
|
|
23
|
+
:options="statusArraySelect"
|
|
24
|
+
class="mb-3"
|
|
25
|
+
/>
|
|
6
26
|
<ClassicLoading
|
|
7
27
|
:loading-text="loading?$t('Loading lives...'):undefined"
|
|
8
|
-
:error-text="
|
|
28
|
+
:error-text="0===lives.length?$t('No live currently'):undefined"
|
|
9
29
|
/>
|
|
10
|
-
<
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
</p>
|
|
24
|
-
<LiveItem
|
|
25
|
-
v-for="(l, index) in live.lives"
|
|
26
|
-
:key="l.podcastId"
|
|
27
|
-
class="mt-3"
|
|
28
|
-
:fetch-conference="l"
|
|
29
|
-
:index="index"
|
|
30
|
-
@deleteItem="deleteLive(indexLive, $event)"
|
|
31
|
-
/>
|
|
32
|
-
</template>
|
|
30
|
+
<template v-if="lives.length">
|
|
31
|
+
<SwiperList
|
|
32
|
+
v-if="!loading"
|
|
33
|
+
:listObject="lives"
|
|
34
|
+
>
|
|
35
|
+
<template #octopusSlide="{option, index}">
|
|
36
|
+
<LiveItem
|
|
37
|
+
:fetch-conference="option"
|
|
38
|
+
@deleteItem="deleteLive(index)"
|
|
39
|
+
@updateItem="updateLive($event, index)"
|
|
40
|
+
/>
|
|
41
|
+
</template>
|
|
42
|
+
</SwiperList>
|
|
33
43
|
</template>
|
|
34
44
|
</div>
|
|
35
45
|
</template>
|
|
@@ -37,12 +47,13 @@
|
|
|
37
47
|
<script lang="ts">
|
|
38
48
|
import ClassicLoading from '../../form/ClassicLoading.vue';
|
|
39
49
|
import LiveItem from './LiveItem.vue';
|
|
50
|
+
import ClassicSelect from '../../form/ClassicSelect.vue';
|
|
51
|
+
import SwiperList from '../list/SwiperList.vue';
|
|
40
52
|
import { handle403 } from '../../mixins/handle403';
|
|
53
|
+
import { orgaComputed } from '../../mixins/orgaComputed';
|
|
41
54
|
import octopusApi from '@saooti/octopus-api';
|
|
42
|
-
import dayjs from 'dayjs';
|
|
43
|
-
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
|
44
|
-
dayjs.extend(localizedFormat);
|
|
45
55
|
import { useFilterStore } from '@/stores/FilterStore';
|
|
56
|
+
import { useAuthStore } from '@/stores/AuthStore';
|
|
46
57
|
import { mapState } from 'pinia';
|
|
47
58
|
import { state } from '../../../stores/ParamSdkStore';
|
|
48
59
|
import { Conference } from '@/stores/class/conference/conference';
|
|
@@ -52,207 +63,118 @@ export default defineComponent({
|
|
|
52
63
|
name: 'LiveList',
|
|
53
64
|
components: {
|
|
54
65
|
LiveItem,
|
|
55
|
-
ClassicLoading
|
|
66
|
+
ClassicLoading,
|
|
67
|
+
SwiperList,
|
|
68
|
+
ClassicSelect
|
|
56
69
|
},
|
|
57
70
|
|
|
58
|
-
mixins: [handle403],
|
|
71
|
+
mixins: [handle403, orgaComputed],
|
|
59
72
|
|
|
60
73
|
props: {
|
|
61
|
-
conferenceWatched: { default: () => [], type: Array as ()=>Array<Conference>},
|
|
62
74
|
organisationId: { default: undefined, type: String},
|
|
63
75
|
},
|
|
64
|
-
emits: ['initConferenceIds'],
|
|
65
76
|
data() {
|
|
66
77
|
return {
|
|
67
78
|
loading: true as boolean,
|
|
68
79
|
loaded: true as boolean,
|
|
69
|
-
|
|
80
|
+
lives: [] as Array<Conference>,
|
|
81
|
+
isLiveAuthorized: false as boolean,
|
|
82
|
+
statusClassic: ["RECORDING", "PENDING", "PLANNED"] as Array<string>,
|
|
83
|
+
statusAdmin: ["DEBRIEFING", "ERROR", "PUBLISHING"] as Array<string>,
|
|
84
|
+
selectedStatus: "ALL" as string
|
|
70
85
|
};
|
|
71
86
|
},
|
|
72
87
|
|
|
73
88
|
computed: {
|
|
74
89
|
...mapState(useFilterStore, ['filterOrgaId']),
|
|
75
|
-
|
|
76
|
-
return [
|
|
77
|
-
{status: "RECORDING", title:this.$t('In live'), lives:[]},
|
|
78
|
-
{status: "PENDING", title:this.$t('This live is not started yet'), lives:[]},
|
|
79
|
-
{status: "PLANNED", title:this.$t('Live to be'), lives:[]},
|
|
80
|
-
{status: "DEBRIEFING", title:this.$t('Live terminated'), lives:[]},
|
|
81
|
-
{status: "PUBLISHING", title:this.$t('Publishing'), lives:[]},
|
|
82
|
-
{status: "ERROR", title:this.$t('In error'), lives:[]}
|
|
83
|
-
];
|
|
84
|
-
},
|
|
85
|
-
isNoLive(): boolean{
|
|
86
|
-
return this.loaded && !this.livesArray[0].lives.length && !this.livesArray[2].lives.length && !this.livesArray[3].lives.length;
|
|
87
|
-
},
|
|
90
|
+
...mapState(useAuthStore, ['authOrganisation']),
|
|
88
91
|
filterOrgaUsed(): string|undefined {
|
|
89
92
|
return this.filterOrgaId?this.filterOrgaId:this.organisationId;
|
|
90
93
|
},
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const isLive = await octopusApi.fetchData<boolean>(0, 'organisation/liveEnabled/'+this.organisationId);
|
|
113
|
-
if (isLive) {
|
|
114
|
-
if(!this.loading){
|
|
115
|
-
this.fetchContent();
|
|
94
|
+
editRight(): boolean {
|
|
95
|
+
return (true ===this.authenticated && this.myOrganisationId === this.filterOrgaUsed) ||true===state.generalParameters.isAdmin;
|
|
96
|
+
},
|
|
97
|
+
liveRight(): boolean {
|
|
98
|
+
return (state.generalParameters.isRoleLive as boolean)&& true===this.authOrganisation.attributes?.['live.active'];
|
|
99
|
+
},
|
|
100
|
+
isPodcastmaker(): boolean {
|
|
101
|
+
return (state.generalParameters.podcastmaker as boolean);
|
|
102
|
+
},
|
|
103
|
+
statusArraySelect(): Array<{title: string, value:string}>{
|
|
104
|
+
const statusArray =[{title: this.$t('All lives'), value:'ALL'}];
|
|
105
|
+
for(let status of this.statusFetched){
|
|
106
|
+
let title ="";
|
|
107
|
+
switch (status) {
|
|
108
|
+
case "RECORDING": title = this.$t('In live'); break;
|
|
109
|
+
case "PENDING": title = this.$t('live upcoming'); break;
|
|
110
|
+
case "PLANNED": title = this.$t('live in few time'); break;
|
|
111
|
+
case "DEBRIEFING": title = this.$t('In debriefing'); break;
|
|
112
|
+
case "PUBLISHING": title = this.$t('In the process of being published'); break;
|
|
113
|
+
case "ERROR": title = this.$t('In error'); break;
|
|
114
|
+
default: break;
|
|
116
115
|
}
|
|
117
|
-
|
|
118
|
-
this.initArrays();
|
|
119
|
-
this.loading = false;
|
|
120
|
-
this.loaded = true;
|
|
116
|
+
statusArray.push({title: title, value: status});
|
|
121
117
|
}
|
|
118
|
+
return statusArray;
|
|
122
119
|
},
|
|
123
|
-
|
|
124
|
-
if(
|
|
125
|
-
this.
|
|
120
|
+
statusFetched(): Array<string>{
|
|
121
|
+
if(this.editRight){
|
|
122
|
+
return this.statusClassic.concat(this.statusAdmin);
|
|
126
123
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
124
|
+
return this.statusClassic;
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
watch: {
|
|
128
|
+
filterOrgaUsed: {
|
|
129
|
+
async handler(): Promise<void> {
|
|
130
|
+
await this.checkIfLiveAuthorized();
|
|
131
|
+
this.fetchContent();
|
|
131
132
|
},
|
|
132
|
-
|
|
133
|
+
immediate: true,
|
|
133
134
|
},
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
async created() {
|
|
137
|
-
if(!this.filterOrgaUsed){
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
const isLive = await octopusApi.fetchData<boolean>(0, 'organisation/liveEnabled/'+this.filterOrgaUsed);
|
|
141
|
-
if (isLive) {
|
|
135
|
+
selectedStatus(){
|
|
142
136
|
this.fetchContent();
|
|
143
|
-
} else {
|
|
144
|
-
this.loading = false;
|
|
145
|
-
this.loaded = true;
|
|
146
137
|
}
|
|
147
138
|
},
|
|
148
139
|
methods: {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
140
|
+
async checkIfLiveAuthorized(): Promise<void>{
|
|
141
|
+
if(!this.filterOrgaUsed){
|
|
142
|
+
return;
|
|
152
143
|
}
|
|
144
|
+
this.isLiveAuthorized = await octopusApi.fetchData<boolean>(0, 'organisation/liveEnabled/'+this.filterOrgaUsed);
|
|
153
145
|
},
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
return null !== p;
|
|
158
|
-
});
|
|
159
|
-
}else if("PENDING"===this.livesArray[i].status){
|
|
160
|
-
this.dataLivesToBe = dataLives;
|
|
161
|
-
for (let index = 0, len = dataLives.length; index < len; index++) {
|
|
162
|
-
if (dayjs(dataLives[index].date).isBefore(dayjs())) {
|
|
163
|
-
this.livesArray[i].lives.push(dataLives[index]);
|
|
164
|
-
indexPast = index + 1;
|
|
165
|
-
} else {break;}
|
|
166
|
-
}
|
|
167
|
-
}else{
|
|
168
|
-
this.livesArray[i].lives = this.dataLivesToBe
|
|
169
|
-
.slice(indexPast)
|
|
170
|
-
.concat(dataLives)
|
|
171
|
-
.filter((p: Conference | null) => {
|
|
172
|
-
return null !== p;
|
|
173
|
-
});
|
|
174
|
-
}
|
|
175
|
-
return indexPast;
|
|
146
|
+
endLoading():void{
|
|
147
|
+
this.loading = false;
|
|
148
|
+
this.loaded = true;
|
|
176
149
|
},
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
150
|
+
updateLive(live: Conference, index:number):void{
|
|
151
|
+
this.lives.splice(index, 1, live);
|
|
152
|
+
},
|
|
153
|
+
async fetchContent(): Promise<void> {
|
|
154
|
+
this.lives.length = 0;
|
|
155
|
+
if (!this.filterOrgaUsed || !this.isLiveAuthorized) {
|
|
156
|
+
this.endLoading();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
this.loading = true;
|
|
160
|
+
this.loaded = false;
|
|
161
|
+
try {
|
|
185
162
|
const dataLives = await octopusApi.fetchDataWithParams<Array<Conference>>(9, 'conference/list',{
|
|
186
163
|
organisationId: this.filterOrgaUsed,
|
|
187
164
|
withPodcastId: true,
|
|
188
|
-
status: this.
|
|
165
|
+
status: "ALL"===this.selectedStatus ? this.statusFetched : this.selectedStatus,
|
|
166
|
+
});
|
|
167
|
+
this.lives = dataLives.filter((p: Conference | null) => {
|
|
168
|
+
return null !== p;
|
|
189
169
|
});
|
|
190
|
-
indexPast = this.liveTreatement(i, dataLives, indexPast);
|
|
191
|
-
}
|
|
192
|
-
},
|
|
193
|
-
async fetchContent(): Promise<void> {
|
|
194
|
-
try {
|
|
195
|
-
this.initArrays();
|
|
196
|
-
if (!this.filterOrgaUsed) {
|
|
197
|
-
this.loading = false;
|
|
198
|
-
this.loaded = true;
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
this.loading = true;
|
|
202
|
-
this.loaded = false;
|
|
203
|
-
await this.fetchLives();
|
|
204
|
-
const listIds = this.livesArray[0].lives
|
|
205
|
-
.concat(this.livesArray[1].lives)
|
|
206
|
-
.concat(this.livesArray[2].lives);
|
|
207
|
-
this.$emit('initConferenceIds', listIds);
|
|
208
170
|
} catch (error) {
|
|
209
171
|
this.handle403((error as AxiosError));
|
|
210
172
|
}
|
|
211
|
-
this.
|
|
212
|
-
this.loaded = true;
|
|
213
|
-
},
|
|
214
|
-
deleteLive(indexLives: number, index: number): void {
|
|
215
|
-
this.livesArray[indexLives].lives.splice(index, 1);
|
|
173
|
+
this.endLoading();
|
|
216
174
|
},
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
let index = 0, len = this.conferenceWatched.length;
|
|
220
|
-
index < len;
|
|
221
|
-
index++
|
|
222
|
-
) {
|
|
223
|
-
const element = this.conferenceWatched[index];
|
|
224
|
-
const indexLivesToBe = this.livesArray[1].lives.findIndex(
|
|
225
|
-
(el: Conference) => el.conferenceId === element.conferenceId
|
|
226
|
-
);
|
|
227
|
-
if (-1 === indexLivesToBe) {
|
|
228
|
-
const indexLives = this.livesArray[0].lives.findIndex(
|
|
229
|
-
(el: Conference) => el.conferenceId === element.conferenceId
|
|
230
|
-
);
|
|
231
|
-
if (-1 === indexLives || 'DEBRIEFING' !== element.status) continue;
|
|
232
|
-
const newConf = this.livesArray[0].lives[indexLives];
|
|
233
|
-
newConf.status = element.status;
|
|
234
|
-
this.livesArray[0].lives.splice(indexLives, 1);
|
|
235
|
-
this.livesArray[3].lives.push(newConf);
|
|
236
|
-
break;
|
|
237
|
-
}
|
|
238
|
-
if ('RECORDING' !== element.status) continue;
|
|
239
|
-
const newConf = this.livesArray[1].lives[indexLivesToBe];
|
|
240
|
-
newConf.status = element.status;
|
|
241
|
-
this.livesArray[1].lives.splice(indexLivesToBe, 1);
|
|
242
|
-
this.livesArray[0].lives.push(newConf);
|
|
243
|
-
break;
|
|
244
|
-
}
|
|
175
|
+
deleteLive(index: number): void {
|
|
176
|
+
this.lives.splice(index, 1);
|
|
245
177
|
},
|
|
246
178
|
},
|
|
247
179
|
})
|
|
248
180
|
</script>
|
|
249
|
-
|
|
250
|
-
<style lang="scss">
|
|
251
|
-
.octopus-app{
|
|
252
|
-
.live-list-category {
|
|
253
|
-
align-self: flex-start;
|
|
254
|
-
text-transform: uppercase;
|
|
255
|
-
font-weight: bold;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
</style>
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="d-flex align-items-center">
|
|
3
|
+
<div v-if="currentlyPlayingString.length || podcastRadio" class="me-2 fw-bold">{{$t('Currently') +' : '}}</div>
|
|
4
|
+
<router-link
|
|
5
|
+
v-if="podcastRadio"
|
|
6
|
+
class="d-flex align-items-center"
|
|
7
|
+
:to="{
|
|
8
|
+
name: 'podcast',
|
|
9
|
+
params: { podcastId: podcastRadio.podcastId },
|
|
10
|
+
query: { productor: filterOrgaId },
|
|
11
|
+
}">
|
|
12
|
+
<img
|
|
13
|
+
v-lazy="proxyImageUrl(podcastRadio.imageUrl, '80')"
|
|
14
|
+
width="80"
|
|
15
|
+
height="80"
|
|
16
|
+
class="small-img-box"
|
|
17
|
+
:title="$t('Episode name image', {name:podcastRadio.title})"
|
|
18
|
+
:alt="$t('Episode name image', {name:podcastRadio.title})"
|
|
19
|
+
>
|
|
20
|
+
<div>{{podcastRadio.title}}</div>
|
|
21
|
+
</router-link>
|
|
22
|
+
<div v-else-if="currentlyPlayingString.length">{{currentlyPlayingString}}</div>
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<script lang="ts">
|
|
27
|
+
import { usePlayerStore } from '@/stores/PlayerStore';
|
|
28
|
+
import { useFilterStore } from '@/stores/FilterStore';
|
|
29
|
+
import { mapState } from 'pinia';
|
|
30
|
+
import imageProxy from '../../mixins/imageProxy';
|
|
31
|
+
import {fetchRadioData} from '../../mixins/radio/fetchRadioData';
|
|
32
|
+
import { defineComponent } from 'vue';
|
|
33
|
+
import { Canal } from '@/stores/class/radio/canal';
|
|
34
|
+
import { MediaRadio } from '@/stores/class/general/player';
|
|
35
|
+
import { Podcast } from '@/stores/class/general/podcast';
|
|
36
|
+
export default defineComponent({
|
|
37
|
+
name: 'RadioItem',
|
|
38
|
+
|
|
39
|
+
components: {
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
mixins: [imageProxy, fetchRadioData],
|
|
43
|
+
|
|
44
|
+
props: {
|
|
45
|
+
radio: { default: undefined, type: Object as ()=>Canal},
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
data() {
|
|
49
|
+
return {
|
|
50
|
+
currentMetadata: undefined as undefined|MediaRadio,
|
|
51
|
+
currentPodcast: undefined as undefined|Podcast,
|
|
52
|
+
radioInterval: undefined as ReturnType<typeof setTimeout>|undefined,
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
computed:{
|
|
57
|
+
...mapState(usePlayerStore, ['playerRadio']),
|
|
58
|
+
...mapState(useFilterStore, ['filterOrgaId']),
|
|
59
|
+
playingRadio(){
|
|
60
|
+
return this.playerRadio && this.playerRadio.canalId === this.radio?.id;
|
|
61
|
+
},
|
|
62
|
+
podcastRadio():Podcast|undefined{
|
|
63
|
+
if(this.playingRadio){
|
|
64
|
+
return this.playerRadio?.podcast;
|
|
65
|
+
}
|
|
66
|
+
return this.currentPodcast;
|
|
67
|
+
},
|
|
68
|
+
currentlyPlayingString(): string{
|
|
69
|
+
if(this.playingRadio && this.playerRadio){
|
|
70
|
+
return this.displayTitle(this.playerRadio.metadata);
|
|
71
|
+
}
|
|
72
|
+
if(this.currentMetadata){
|
|
73
|
+
return this.displayTitle(this.currentMetadata);
|
|
74
|
+
}
|
|
75
|
+
return "";
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
mounted() {
|
|
80
|
+
this.fetchCurrentlyPlaying();
|
|
81
|
+
this.radioInterval = setInterval(() => {
|
|
82
|
+
this.fetchCurrentlyPlaying();
|
|
83
|
+
}, 1000);
|
|
84
|
+
},
|
|
85
|
+
methods: {
|
|
86
|
+
async fetchCurrentlyPlaying(): Promise<void>{
|
|
87
|
+
if(!this.radio || this.playingRadio){return;}
|
|
88
|
+
this.fetchRadioMetadata(this.radio.id,this.currentMetadata?.title??"", this.updateMetadata);
|
|
89
|
+
},
|
|
90
|
+
updateMetadata(metadata: MediaRadio, podcast?:Podcast): void{
|
|
91
|
+
this.currentMetadata=metadata;
|
|
92
|
+
this.currentPodcast=podcast;
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
})
|
|
96
|
+
</script>
|
|
97
|
+
<style lang="scss">
|
|
98
|
+
.octopus-app{
|
|
99
|
+
.small-img-box{
|
|
100
|
+
height: 80px;
|
|
101
|
+
width: 80px;
|
|
102
|
+
border-radius: 0.2rem;
|
|
103
|
+
overflow: hidden;
|
|
104
|
+
flex-shrink: 0;
|
|
105
|
+
margin: 0.5rem;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
</style>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="img-box position-relative flex-shrink-0 mb-3 me-3 float-start">
|
|
3
|
+
<img
|
|
4
|
+
v-lazy="radio.imageUrl ?proxyImageUrl(radio.imageUrl, '330') :'/img/tempRadio.jpg'"
|
|
5
|
+
width="330"
|
|
6
|
+
height="330"
|
|
7
|
+
class="img-box"
|
|
8
|
+
:title="$t('Image')+ ' '+radio.name"
|
|
9
|
+
:alt="$t('Image')+ ' '+radio.name"
|
|
10
|
+
>
|
|
11
|
+
<button
|
|
12
|
+
class="image-play-button"
|
|
13
|
+
@click="playRadio"
|
|
14
|
+
>
|
|
15
|
+
<div class="icon-container">
|
|
16
|
+
<div
|
|
17
|
+
v-if="!playingRadio"
|
|
18
|
+
:title="$t('Play')"
|
|
19
|
+
class="saooti-play"
|
|
20
|
+
/>
|
|
21
|
+
<div
|
|
22
|
+
v-else
|
|
23
|
+
class="bloc-paddle"
|
|
24
|
+
>
|
|
25
|
+
<span class="paddle1" />
|
|
26
|
+
<span class="paddle2" />
|
|
27
|
+
<span class="paddle3" />
|
|
28
|
+
</div>
|
|
29
|
+
<div class="ms-2">
|
|
30
|
+
{{ playText }}
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</button>
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script lang="ts">
|
|
38
|
+
import { usePlayerStore } from '@/stores/PlayerStore';
|
|
39
|
+
import { useFilterStore } from '@/stores/FilterStore';
|
|
40
|
+
import { mapState, mapActions } from 'pinia';
|
|
41
|
+
import imageProxy from '../../mixins/imageProxy';
|
|
42
|
+
import { defineComponent } from 'vue';
|
|
43
|
+
import { Canal } from '@/stores/class/radio/canal';
|
|
44
|
+
export default defineComponent({
|
|
45
|
+
name: 'RadioImage',
|
|
46
|
+
|
|
47
|
+
components: {
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
mixins: [imageProxy],
|
|
51
|
+
|
|
52
|
+
props: {
|
|
53
|
+
radio: { default: undefined, type: Object as ()=>Canal},
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
computed:{
|
|
57
|
+
...mapState(usePlayerStore, ['playerRadio', 'playerStatus']),
|
|
58
|
+
...mapState(useFilterStore, ['filterOrgaId']),
|
|
59
|
+
playingRadio(){
|
|
60
|
+
return this.playerRadio && this.playerRadio.canalId === this.radio?.id;
|
|
61
|
+
},
|
|
62
|
+
playText(): string {
|
|
63
|
+
return this.playingRadio && "PLAYING"===this.playerStatus ? this.$t('Pause'):this.$t('Play');
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
methods: {
|
|
68
|
+
...mapActions(usePlayerStore, ['playerPlay', 'playerChangeStatus']),
|
|
69
|
+
playRadio(): void{
|
|
70
|
+
if(!this.radio){return;}
|
|
71
|
+
if (this.playingRadio) {
|
|
72
|
+
this.playerChangeStatus("PLAYING"===this.playerStatus);
|
|
73
|
+
} else {
|
|
74
|
+
this.playerPlay({
|
|
75
|
+
canalId: this.radio.id,
|
|
76
|
+
url: "https://"+this.radio.url + "/live.m3u8",
|
|
77
|
+
metadata : ""
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
})
|
|
83
|
+
</script>
|
|
84
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="radio" class="d-flex border w-100 p-3">
|
|
3
|
+
<RadioImage :radio="radio"/>
|
|
4
|
+
<router-link
|
|
5
|
+
:to="{
|
|
6
|
+
name: 'radio',
|
|
7
|
+
params: { canalId: radio.id },
|
|
8
|
+
query: { productor: filterOrgaId },
|
|
9
|
+
}"
|
|
10
|
+
class="text-dark emission-item-text"
|
|
11
|
+
>
|
|
12
|
+
<div class="emission-name mb-2">{{ radio.name }}</div>
|
|
13
|
+
<div v-if="radio.description" class="ten-line-clamp">{{ radio.description }}</div>
|
|
14
|
+
<RadioCurrently :radio="radio"/>
|
|
15
|
+
</router-link>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script lang="ts">
|
|
20
|
+
import { useFilterStore } from '@/stores/FilterStore';
|
|
21
|
+
import { mapState } from 'pinia';
|
|
22
|
+
import imageProxy from '../../mixins/imageProxy';
|
|
23
|
+
import RadioImage from './RadioImage.vue';
|
|
24
|
+
import RadioCurrently from './RadioCurrently.vue';
|
|
25
|
+
import { defineComponent } from 'vue';
|
|
26
|
+
import { Canal } from '@/stores/class/radio/canal';
|
|
27
|
+
export default defineComponent({
|
|
28
|
+
name: 'RadioItem',
|
|
29
|
+
|
|
30
|
+
components: {
|
|
31
|
+
RadioCurrently,
|
|
32
|
+
RadioImage
|
|
33
|
+
},
|
|
34
|
+
mixins: [imageProxy],
|
|
35
|
+
|
|
36
|
+
props: {
|
|
37
|
+
radio: { default: undefined, type: Object as ()=>Canal},
|
|
38
|
+
},
|
|
39
|
+
computed:{
|
|
40
|
+
...mapState(useFilterStore, ['filterOrgaId']),
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
</script>
|
|
44
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="(filterOrgaId || organisationId) && radio.length"
|
|
4
|
+
class="d-flex flex-column align-items-start mt-3"
|
|
5
|
+
>
|
|
6
|
+
<h2 class="mb-0 big-h2 mb-3">{{ $t('Radio') }}</h2>
|
|
7
|
+
<template v-if="radio.length">
|
|
8
|
+
<RadioItem
|
|
9
|
+
v-for="radioItem in radio"
|
|
10
|
+
:key="radioItem.id"
|
|
11
|
+
:radio="radioItem"
|
|
12
|
+
/>
|
|
13
|
+
</template>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script lang="ts">
|
|
18
|
+
import RadioItem from './RadioItem.vue';
|
|
19
|
+
import { handle403 } from '../../mixins/handle403';
|
|
20
|
+
import { orgaComputed } from '../../mixins/orgaComputed';
|
|
21
|
+
import octopusApi from '@saooti/octopus-api';
|
|
22
|
+
import { useFilterStore } from '@/stores/FilterStore';
|
|
23
|
+
import { mapState } from 'pinia';
|
|
24
|
+
import { Canal } from '@/stores/class/radio/canal';
|
|
25
|
+
import { defineComponent } from 'vue'
|
|
26
|
+
import { AxiosError } from 'axios';
|
|
27
|
+
export default defineComponent({
|
|
28
|
+
name: 'ecbd98d9-79bd-4312-ad5e-fc7c1c4a191c',
|
|
29
|
+
components: {
|
|
30
|
+
RadioItem,
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
mixins: [handle403, orgaComputed],
|
|
34
|
+
|
|
35
|
+
props: {
|
|
36
|
+
organisationId: { default: undefined, type: String},
|
|
37
|
+
},
|
|
38
|
+
data() {
|
|
39
|
+
return {
|
|
40
|
+
radio: [] as Array<Canal>
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
computed: {
|
|
45
|
+
...mapState(useFilterStore, ['filterOrgaId']),
|
|
46
|
+
filterOrgaUsed(): string|undefined {
|
|
47
|
+
return this.filterOrgaId?this.filterOrgaId:this.organisationId;
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
watch: {
|
|
51
|
+
filterOrgaUsed: {
|
|
52
|
+
async handler(): Promise<void> {
|
|
53
|
+
this.fetchContent();
|
|
54
|
+
},
|
|
55
|
+
immediate: true,
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
methods: {
|
|
59
|
+
async fetchContent(): Promise<void> {
|
|
60
|
+
this.radio.length = 0;
|
|
61
|
+
if (!this.filterOrgaUsed) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
this.radio = await octopusApi.fetchData<Array<Canal>>(14, 'canal/orga/'+this.filterOrgaUsed+'/');
|
|
66
|
+
} catch (error) {
|
|
67
|
+
this.handle403((error as AxiosError));
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
</script>
|