chore(api): rename schedule models fields

This commit is contained in:
jo 2022-07-17 22:27:57 +02:00 committed by Kyle Robbertze
parent 8ceb1419a0
commit 57046e2a9d
8 changed files with 224 additions and 334 deletions

View File

@ -2,8 +2,14 @@ from django.db import models
class Schedule(models.Model):
starts = models.DateTimeField()
ends = models.DateTimeField()
starts_at = models.DateTimeField(db_column="starts")
ends_at = models.DateTimeField(db_column="ends")
instance = models.ForeignKey(
"schedule.ShowInstance",
on_delete=models.DO_NOTHING,
)
file = models.ForeignKey(
"storage.File",
on_delete=models.DO_NOTHING,
@ -16,23 +22,52 @@ class Schedule(models.Model):
blank=True,
null=True,
)
clip_length = models.DurationField(blank=True, null=True)
length = models.DurationField(blank=True, null=True, db_column="clip_length")
fade_in = models.TimeField(blank=True, null=True)
fade_out = models.TimeField(blank=True, null=True)
cue_in = models.DurationField()
cue_out = models.DurationField()
media_item_played = models.BooleanField(blank=True, null=True)
instance = models.ForeignKey("schedule.ShowInstance", on_delete=models.DO_NOTHING)
playout_status = models.SmallIntegerField()
broadcasted = models.SmallIntegerField()
class PositionStatus(models.IntegerChoices):
FILLER = -1, "Filler" # Used to fill a show that already started
OUTSIDE = 0, "Outside" # Is outside the show time frame
INSIDE = 1, "Inside" # Is inside the show time frame
BOUNDARY = 2, "Boundary" # Is at the boundary of the show time frame
position = models.IntegerField()
position_status = models.SmallIntegerField(
choices=PositionStatus.choices,
default=PositionStatus.INSIDE,
db_column="playout_status",
)
# Broadcasted is set to 1 when a live source is not
# on. Used for the playout history.
broadcasted = models.SmallIntegerField()
played = models.BooleanField(
blank=True,
null=True,
db_column="media_item_played",
)
@property
def overbooked(self):
"""
A schedule item is overbooked if it starts after the end of the show
instance it is in.
Related to self.position_status
"""
return self.starts_at >= self.instance.ends_at
def get_owner(self):
return self.instance.get_owner()
def get_cueout(self):
def get_cue_out(self):
"""
Returns a scheduled item cueout that is based on the current show instance.
Returns a scheduled item cue out that is based on the current show
instance.
Cue out of a specific item can potentially overrun the show that it is
scheduled in. In that case, the cue out should be the end of the show.
@ -49,13 +84,17 @@ class Schedule(models.Model):
- When the schedule starts after the end of the show instance,
return the stored cue_out even if the schedule WILL NOT BE PLAYED.
"""
if self.starts < self.instance.ends and self.instance.ends < self.ends:
return self.instance.ends - self.starts
if (
self.starts_at < self.instance.ends_at
and self.instance.ends_at < self.ends_at
):
return self.instance.ends_at - self.starts_at
return self.cue_out
def get_ends(self):
def get_ends_at(self):
"""
Returns a scheduled item ends that is based on the current show instance.
Returns a scheduled item ends that is based on the current show
instance.
End of a specific item can potentially overrun the show that it is
scheduled in. In that case, the end should be the end of the show.
@ -72,17 +111,9 @@ class Schedule(models.Model):
- When the schedule starts after the end of the show instance,
return the show instance ends.
"""
if self.instance.ends < self.ends:
return self.instance.ends
return self.ends
@property
def is_valid(self):
"""
A schedule item is valid if it starts before the end of the show instance
it is in
"""
return self.starts < self.instance.ends
if self.instance.ends_at < self.ends_at:
return self.instance.ends_at
return self.ends_at
class Meta:
managed = False

View File

@ -7,29 +7,30 @@ class ScheduleSerializer(serializers.HyperlinkedModelSerializer):
file_id = serializers.IntegerField(source="file.id", read_only=True)
stream_id = serializers.IntegerField(source="stream.id", read_only=True)
instance_id = serializers.IntegerField(source="instance.id", read_only=True)
cue_out = serializers.DurationField(source="get_cueout", read_only=True)
ends = serializers.DateTimeField(source="get_ends", read_only=True)
cue_out = serializers.DurationField(source="get_cue_out", read_only=True)
ends_at = serializers.DateTimeField(source="get_ends_at", read_only=True)
class Meta:
model = Schedule
fields = [
"item_url",
"id",
"starts",
"ends",
"starts_at",
"ends_at",
"instance",
"instance_id",
"file",
"file_id",
"stream",
"stream_id",
"clip_length",
"length",
"fade_in",
"fade_out",
"cue_in",
"cue_out",
"media_item_played",
"instance",
"instance_id",
"playout_status",
"broadcasted",
"position",
"position_status",
"broadcasted",
"played",
"overbooked",
]

View File

@ -9,49 +9,49 @@ class TestSchedule(TestCase):
@classmethod
def setUpTestData(cls):
cls.show_instance = ShowInstance(
created=datetime(year=2021, month=10, day=1, hour=12),
starts=datetime(year=2021, month=10, day=2, hour=1),
ends=datetime(year=2021, month=10, day=2, hour=2),
created_at=datetime(year=2021, month=10, day=1, hour=12),
starts_at=datetime(year=2021, month=10, day=2, hour=1),
ends_at=datetime(year=2021, month=10, day=2, hour=2),
)
cls.length = timedelta(minutes=10)
cls.cue_in = timedelta(seconds=1)
cls.cue_out = cls.length - timedelta(seconds=4)
def create_schedule(self, starts):
def create_schedule(self, starts_at):
return Schedule(
starts=starts,
ends=starts + self.length,
starts_at=starts_at,
ends_at=starts_at + self.length,
cue_in=self.cue_in,
cue_out=self.cue_out,
instance=self.show_instance,
)
def test_get_cueout(self):
def test_get_cue_out(self):
# No overlapping schedule datetimes, normal usecase:
item1_starts = datetime(year=2021, month=10, day=2, hour=1, minute=30)
item1 = self.create_schedule(item1_starts)
self.assertEqual(item1.get_cueout(), self.cue_out)
self.assertEqual(item1.get_ends(), item1_starts + self.length)
self.assertEqual(item1.get_cue_out(), self.cue_out)
self.assertEqual(item1.get_ends_at(), item1_starts + self.length)
# Mixed overlapping schedule datetimes (only ends is overlapping):
item_2_starts = datetime(year=2021, month=10, day=2, hour=1, minute=55)
item_2 = self.create_schedule(item_2_starts)
self.assertEqual(item_2.get_cueout(), timedelta(minutes=5))
self.assertEqual(item_2.get_ends(), self.show_instance.ends)
self.assertEqual(item_2.get_cue_out(), timedelta(minutes=5))
self.assertEqual(item_2.get_ends_at(), self.show_instance.ends_at)
# Fully overlapping schedule datetimes (starts and ends are overlapping):
item3_starts = datetime(year=2021, month=10, day=2, hour=2, minute=1)
item3 = self.create_schedule(item3_starts)
self.assertEqual(item3.get_cueout(), self.cue_out)
self.assertEqual(item3.get_ends(), self.show_instance.ends)
self.assertEqual(item3.get_cue_out(), self.cue_out)
self.assertEqual(item3.get_ends_at(), self.show_instance.ends_at)
def test_is_valid(self):
def test_overbooked(self):
# Starts before the schedule ends
item1_starts = datetime(year=2021, month=10, day=2, hour=1, minute=30)
item1 = self.create_schedule(item1_starts)
self.assertTrue(item1.is_valid)
self.assertFalse(item1.overbooked)
# Starts after the schedule ends
item_2_starts = datetime(year=2021, month=10, day=2, hour=3)
item_2 = self.create_schedule(item_2_starts)
self.assertFalse(item_2.is_valid)
self.assertTrue(item_2.overbooked)

View File

@ -25,13 +25,13 @@ class TestScheduleViewSet(APITestCase):
)
show = baker.make(
"schedule.ShowInstance",
starts=datetime.now(tz=timezone.utc) - timedelta(minutes=5),
ends=datetime.now(tz=timezone.utc) + timedelta(minutes=5),
starts_at=datetime.now(tz=timezone.utc) - timedelta(minutes=5),
ends_at=datetime.now(tz=timezone.utc) + timedelta(minutes=5),
)
schedule_item = baker.make(
"schedule.Schedule",
starts=datetime.now(tz=timezone.utc),
ends=datetime.now(tz=timezone.utc) + file.length,
starts_at=datetime.now(tz=timezone.utc),
ends_at=datetime.now(tz=timezone.utc) + file.length,
cue_out=file.cue_out,
instance=show,
file=file,
@ -41,7 +41,7 @@ class TestScheduleViewSet(APITestCase):
self.assertEqual(response.status_code, 200)
result = response.json()
self.assertEqual(
dateparse.parse_datetime(result[0]["ends"]), schedule_item.ends
dateparse.parse_datetime(result[0]["ends_at"]), schedule_item.ends_at
)
self.assertEqual(dateparse.parse_duration(result[0]["cue_out"]), file.cue_out)
@ -56,13 +56,13 @@ class TestScheduleViewSet(APITestCase):
)
show = baker.make(
"schedule.ShowInstance",
starts=datetime.now(tz=timezone.utc) - timedelta(minutes=5),
ends=datetime.now(tz=timezone.utc) + timedelta(seconds=20),
starts_at=datetime.now(tz=timezone.utc) - timedelta(minutes=5),
ends_at=datetime.now(tz=timezone.utc) + timedelta(seconds=20),
)
schedule_item = baker.make(
"schedule.Schedule",
starts=datetime.now(tz=timezone.utc),
ends=datetime.now(tz=timezone.utc) + file.length,
starts_at=datetime.now(tz=timezone.utc),
ends_at=datetime.now(tz=timezone.utc) + file.length,
instance=show,
file=file,
)
@ -70,11 +70,11 @@ class TestScheduleViewSet(APITestCase):
response = self.client.get(self.path)
self.assertEqual(response.status_code, 200)
result = response.json()
self.assertEqual(dateparse.parse_datetime(result[0]["ends"]), show.ends)
expected = show.ends - schedule_item.starts
self.assertEqual(dateparse.parse_datetime(result[0]["ends_at"]), show.ends_at)
expected = show.ends_at - schedule_item.starts_at
self.assertEqual(dateparse.parse_duration(result[0]["cue_out"]), expected)
self.assertNotEqual(
dateparse.parse_datetime(result[0]["ends"]), schedule_item.ends
dateparse.parse_datetime(result[0]["ends_at"]), schedule_item.ends_at
)
def test_schedule_item_invalid(self):
@ -88,33 +88,33 @@ class TestScheduleViewSet(APITestCase):
)
show = baker.make(
"schedule.ShowInstance",
starts=datetime.now(tz=timezone.utc) - timedelta(minutes=5),
ends=datetime.now(tz=timezone.utc) + timedelta(minutes=5),
starts_at=datetime.now(tz=timezone.utc) - timedelta(minutes=5),
ends_at=datetime.now(tz=timezone.utc) + timedelta(minutes=5),
)
schedule_item = baker.make(
"schedule.Schedule",
starts=datetime.now(tz=timezone.utc),
ends=datetime.now(tz=timezone.utc) + file.length,
starts_at=datetime.now(tz=timezone.utc),
ends_at=datetime.now(tz=timezone.utc) + file.length,
cue_out=file.cue_out,
instance=show,
file=file,
)
invalid_schedule_item = baker.make( # pylint: disable=unused-variable
"schedule.Schedule",
starts=show.ends + timedelta(minutes=1),
ends=show.ends + timedelta(minutes=1) + file.length,
starts_at=show.ends_at + timedelta(minutes=1),
ends_at=show.ends_at + timedelta(minutes=1) + file.length,
cue_out=file.cue_out,
instance=show,
file=file,
)
self.client.credentials(HTTP_AUTHORIZATION=f"Api-Key {self.token}")
response = self.client.get(self.path, {"is_valid": True})
response = self.client.get(self.path, {"overbooked": False})
self.assertEqual(response.status_code, 200)
result = response.json()
# The invalid item should be filtered out and not returned
self.assertEqual(len(result), 1)
self.assertEqual(
dateparse.parse_datetime(result[0]["ends"]), schedule_item.ends
dateparse.parse_datetime(result[0]["ends_at"]), schedule_item.ends_at
)
self.assertEqual(dateparse.parse_duration(result[0]["cue_out"]), file.cue_out)
@ -131,21 +131,21 @@ class TestScheduleViewSet(APITestCase):
show = baker.make(
"schedule.ShowInstance",
starts=filter_point - timedelta(minutes=5),
ends=filter_point + timedelta(minutes=5),
starts_at=filter_point - timedelta(minutes=5),
ends_at=filter_point + timedelta(minutes=5),
)
schedule_item = baker.make(
"schedule.Schedule",
starts=filter_point,
ends=filter_point + file.length,
starts_at=filter_point,
ends_at=filter_point + file.length,
cue_out=file.cue_out,
instance=show,
file=file,
)
previous_item = baker.make( # pylint: disable=unused-variable
"schedule.Schedule",
starts=filter_point - timedelta(minutes=5),
ends=filter_point - timedelta(minutes=5) + file.length,
starts_at=filter_point - timedelta(minutes=5),
ends_at=filter_point - timedelta(minutes=5) + file.length,
cue_out=file.cue_out,
instance=show,
file=file,
@ -156,12 +156,13 @@ class TestScheduleViewSet(APITestCase):
)
range_end = (filter_point + timedelta(minutes=1)).isoformat(timespec="seconds")
response = self.client.get(
self.path, {"starts__range": f"{range_start},{range_end}"}
self.path,
{"starts_after": range_start, "starts_before": range_end},
)
self.assertEqual(response.status_code, 200)
result = response.json()
# The previous_item should be filtered out and not returned
self.assertEqual(len(result), 1)
self.assertEqual(
dateparse.parse_datetime(result[0]["starts"]), schedule_item.starts
dateparse.parse_datetime(result[0]["starts_at"]), schedule_item.starts_at
)

View File

@ -1,42 +1,33 @@
from django.db.models import F
from drf_spectacular.utils import OpenApiParameter, extend_schema, extend_schema_view
from django.db import models
from django_filters import rest_framework as filters
from rest_framework import viewsets
from ..._constants import FILTER_NUMERICAL_LOOKUPS
from ..models import Schedule
from ..serializers import ScheduleSerializer
@extend_schema_view(
list=extend_schema(
parameters=[
OpenApiParameter(
name="is_valid",
description="Filter on valid instances",
required=False,
type=bool,
),
]
)
)
class ScheduleFilter(filters.FilterSet):
starts = filters.DateTimeFromToRangeFilter(field_name="starts_at")
ends = filters.DateTimeFromToRangeFilter(field_name="ends_at")
position_status = filters.NumberFilter()
broadcasted = filters.NumberFilter()
overbooked = filters.BooleanFilter(method="overbooked_filter")
# pylint: disable=unused-argument
def overbooked_filter(self, queryset, name, value):
# TODO: deduplicate code using the overbooked property
if value:
return queryset.filter(starts_at__gte=models.F("instance__ends_at"))
return queryset.filter(starts_at__lt=models.F("instance__ends_at"))
class Meta:
model = Schedule
fields = [] # type: ignore
class ScheduleViewSet(viewsets.ModelViewSet):
queryset = Schedule.objects.all()
serializer_class = ScheduleSerializer
filterset_fields = {
"starts": FILTER_NUMERICAL_LOOKUPS,
"ends": FILTER_NUMERICAL_LOOKUPS,
"playout_status": FILTER_NUMERICAL_LOOKUPS,
"broadcasted": FILTER_NUMERICAL_LOOKUPS,
}
filterset_class = ScheduleFilter
model_permission_name = "schedule"
def get_queryset(self):
filter_valid = self.request.query_params.get("is_valid")
if filter_valid is None:
return self.queryset.all()
filter_valid = filter_valid.strip().lower() in ("true", "yes", "1")
if filter_valid:
return self.queryset.filter(starts__lt=F("instance__ends"))
return self.queryset.filter(starts__gte=F("instance__ends"))

View File

@ -2540,134 +2540,33 @@ paths:
schema:
type: integer
- in: query
name: broadcasted__gt
schema:
type: integer
- in: query
name: broadcasted__gte
schema:
type: integer
- in: query
name: broadcasted__lt
schema:
type: integer
- in: query
name: broadcasted__lte
schema:
type: integer
- in: query
name: broadcasted__range
schema:
type: array
items:
type: integer
description: Multiple values may be separated by commas.
explode: false
style: form
- in: query
name: ends
name: ends_after
schema:
type: string
format: date-time
- in: query
name: ends__gt
name: ends_before
schema:
type: string
format: date-time
- in: query
name: ends__gte
schema:
type: string
format: date-time
- in: query
name: ends__lt
schema:
type: string
format: date-time
- in: query
name: ends__lte
schema:
type: string
format: date-time
- in: query
name: ends__range
schema:
type: array
items:
type: string
format: date-time
description: Multiple values may be separated by commas.
explode: false
style: form
- in: query
name: is_valid
name: overbooked
schema:
type: boolean
description: Filter on valid instances
- in: query
name: playout_status
name: position_status
schema:
type: integer
- in: query
name: playout_status__gt
schema:
type: integer
- in: query
name: playout_status__gte
schema:
type: integer
- in: query
name: playout_status__lt
schema:
type: integer
- in: query
name: playout_status__lte
schema:
type: integer
- in: query
name: playout_status__range
schema:
type: array
items:
type: integer
description: Multiple values may be separated by commas.
explode: false
style: form
- in: query
name: starts
name: starts_after
schema:
type: string
format: date-time
- in: query
name: starts__gt
name: starts_before
schema:
type: string
format: date-time
- in: query
name: starts__gte
schema:
type: string
format: date-time
- in: query
name: starts__lt
schema:
type: string
format: date-time
- in: query
name: starts__lte
schema:
type: string
format: date-time
- in: query
name: starts__range
schema:
type: array
items:
type: string
format: date-time
description: Multiple values may be separated by commas.
explode: false
style: form
tags:
- schedule
security:
@ -6553,13 +6452,19 @@ components:
id:
type: integer
readOnly: true
starts:
starts_at:
type: string
format: date-time
ends:
ends_at:
type: string
format: date-time
readOnly: true
instance:
type: string
format: uri
instance_id:
type: integer
readOnly: true
file:
type: string
format: uri
@ -6574,7 +6479,7 @@ components:
stream_id:
type: integer
readOnly: true
clip_length:
length:
type: string
nullable: true
fade_in:
@ -6590,27 +6495,25 @@ components:
cue_out:
type: string
readOnly: true
media_item_played:
type: boolean
nullable: true
instance:
type: string
format: uri
instance_id:
type: integer
readOnly: true
playout_status:
type: integer
maximum: 32767
minimum: -32768
broadcasted:
type: integer
maximum: 32767
minimum: -32768
position:
type: integer
maximum: 2147483647
minimum: -2147483648
position_status:
allOf:
- $ref: "#/components/schemas/PositionStatusEnum"
minimum: -32768
maximum: 32767
broadcasted:
type: integer
maximum: 32767
minimum: -32768
played:
type: boolean
nullable: true
overbooked:
type: string
readOnly: true
PatchedServiceRegister:
type: object
properties:
@ -7381,6 +7284,13 @@ components:
- item_url
- podcast
- published_at
PositionStatusEnum:
enum:
- -1
- 0
- 1
- 2
type: integer
Preference:
type: object
properties:
@ -7433,13 +7343,19 @@ components:
id:
type: integer
readOnly: true
starts:
starts_at:
type: string
format: date-time
ends:
ends_at:
type: string
format: date-time
readOnly: true
instance:
type: string
format: uri
instance_id:
type: integer
readOnly: true
file:
type: string
format: uri
@ -7454,7 +7370,7 @@ components:
stream_id:
type: integer
readOnly: true
clip_length:
length:
type: string
nullable: true
fade_in:
@ -7470,40 +7386,38 @@ components:
cue_out:
type: string
readOnly: true
media_item_played:
type: boolean
nullable: true
instance:
type: string
format: uri
instance_id:
type: integer
readOnly: true
playout_status:
type: integer
maximum: 32767
minimum: -32768
broadcasted:
type: integer
maximum: 32767
minimum: -32768
position:
type: integer
maximum: 2147483647
minimum: -2147483648
position_status:
allOf:
- $ref: "#/components/schemas/PositionStatusEnum"
minimum: -32768
maximum: 32767
broadcasted:
type: integer
maximum: 32767
minimum: -32768
played:
type: boolean
nullable: true
overbooked:
type: string
readOnly: true
required:
- broadcasted
- cue_in
- cue_out
- ends
- ends_at
- file_id
- id
- instance
- instance_id
- item_url
- playout_status
- overbooked
- position
- starts
- starts_at
- stream_id
ServiceRegister:
type: object

View File

@ -27,16 +27,17 @@ def get_schedule(api_client: ApiClient):
schedule = api_client.services.schedule_url(
params={
"ends__range": (f"{current_time_str}Z,{end_time_str}Z"),
"is_valid": True,
"playout_status__gt": 0,
"ends_after": f"{current_time_str}Z",
"ends_before": f"{end_time_str}Z",
"overbooked": False,
"position_status__gt": 0,
}
)
events = {}
for item in schedule:
item["starts"] = isoparse(item["starts"])
item["ends"] = isoparse(item["ends"])
item["starts_at"] = isoparse(item["starts_at"])
item["ends_at"] = isoparse(item["ends_at"])
show_instance = api_client.services.show_instance_url(id=item["instance_id"])
show = api_client.services.show_url(id=show_instance["show_id"])
@ -62,8 +63,8 @@ def generate_file_events(
"""
events = {}
schedule_start_event_key = datetime_to_event_key(schedule["starts"])
schedule_end_event_key = datetime_to_event_key(schedule["ends"])
schedule_start_event_key = datetime_to_event_key(schedule["starts_at"])
schedule_end_event_key = datetime_to_event_key(schedule["ends_at"])
events[schedule_start_event_key] = {
"type": EventKind.FILE,
@ -102,15 +103,15 @@ def generate_webstream_events(
"""
events = {}
schedule_start_event_key = datetime_to_event_key(schedule["starts"])
schedule_end_event_key = datetime_to_event_key(schedule["ends"])
schedule_start_event_key = datetime_to_event_key(schedule["starts_at"])
schedule_end_event_key = datetime_to_event_key(schedule["ends_at"])
events[schedule_start_event_key] = {
"type": EventKind.STREAM_BUFFER_START,
"independent_event": True,
"row_id": schedule["id"],
"start": datetime_to_event_key(schedule["starts"] - timedelta(seconds=5)),
"end": datetime_to_event_key(schedule["starts"] - timedelta(seconds=5)),
"start": datetime_to_event_key(schedule["starts_at"] - timedelta(seconds=5)),
"end": datetime_to_event_key(schedule["starts_at"] - timedelta(seconds=5)),
"uri": webstream["url"],
"id": webstream["id"],
}
@ -127,7 +128,8 @@ def generate_webstream_events(
"show_name": show["name"],
}
# NOTE: stream_*_end were previously triggerered 1 second before the schedule end.
# NOTE: stream_*_end were previously triggered 1 second before
# the schedule end.
events[schedule_end_event_key] = {
"type": EventKind.STREAM_BUFFER_END,
"independent_event": True,

View File

@ -3,92 +3,50 @@ from libretime_playout.schedule import get_schedule
class ApiClientServicesMock:
@staticmethod
def schedule_url(_post_data=None, params=None, **kwargs):
def schedule_url(*args, **kwargs):
return [
{
"item_url": "http://192.168.10.100:8081/api/v2/schedule/17/",
"id": 17,
"starts": "2022-03-04T15:30:00Z",
"ends": "2022-03-04T15:33:50.674340Z",
"starts_at": "2022-03-04T15:30:00Z",
"ends_at": "2022-03-04T15:33:50.674340Z",
"file": "http://192.168.10.100:8081/api/v2/files/1/",
"file_id": 1,
"stream": None,
"clip_length": "00:03:50.674340",
"fade_in": "00:00:00.500000",
"fade_out": "00:00:00.500000",
"cue_in": "00:00:01.310660",
"cue_out": "00:03:51.985000",
"media_item_played": False,
"instance": "http://192.168.10.100:8081/api/v2/show-instances/3/",
"instance_id": 3,
"playout_status": 1,
"broadcasted": 0,
"position": 0,
},
{
"item_url": "http://192.168.10.100:8081/api/v2/schedule/18/",
"id": 18,
"starts": "2022-03-04T15:33:50.674340Z",
"ends": "2022-03-04T16:03:50.674340Z",
"starts_at": "2022-03-04T15:33:50.674340Z",
"ends_at": "2022-03-04T16:03:50.674340Z",
"file": None,
"stream": "http://192.168.10.100:8081/api/v2/webstreams/1/",
"stream_id": 1,
"clip_length": "00:30:00",
"fade_in": "00:00:00.500000",
"fade_out": "00:00:00.500000",
"cue_in": "00:00:00",
"cue_out": "00:30:00",
"media_item_played": False,
"instance": "http://192.168.10.100:8081/api/v2/show-instances/3/",
"instance_id": 3,
"playout_status": 1,
"broadcasted": 0,
"position": 1,
},
]
@staticmethod
def show_instance_url(_post_data=None, params=None, **kwargs):
def show_instance_url(*args, **kwargs):
return {
"item_url": "http://192.168.10.100:8081/api/v2/show-instances/3/",
"id": 3,
"description": "",
"starts": "2022-03-04T15:30:00Z",
"ends": "2022-03-04T16:30:00Z",
"record": 0,
"rebroadcast": 0,
"time_filled": "00:33:50.674340",
"created": "2022-03-04T15:05:36Z",
"last_scheduled": "2022-03-04T15:05:46Z",
"modified_instance": False,
"autoplaylist_built": False,
"show": "http://192.168.10.100:8081/api/v2/shows/3/",
"show_id": 3,
"instance": None,
"file": None,
}
@staticmethod
def show_url(_post_data=None, params=None, **kwargs):
def show_url(*args, **kwargs):
return {
"item_url": "http://192.168.10.100:8081/api/v2/shows/3/",
"id": 3,
"name": "Test",
"url": "",
"genre": "",
"description": "",
"color": "",
"background_color": "",
"linked": False,
"is_linkable": True,
"image_path": "",
"has_autoplaylist": False,
"autoplaylist_repeat": False,
"autoplaylist": None,
}
@staticmethod
def file_url(_post_data=None, params=None, **kwargs):
def file_url(*args, **kwargs):
return {
"id": 1,
"url": None,
@ -100,19 +58,11 @@ class ApiClientServicesMock:
}
@staticmethod
def webstream_url(_post_data=None, params=None, **kwargs):
def webstream_url(*args, **kwargs):
return {
"item_url": "http://192.168.10.100:8081/api/v2/webstreams/1/",
"id": 1,
"name": "Test",
"description": "",
"url": "http://some-other-radio:8800/main.ogg",
"length": "00:30:00",
"creator_id": 1,
"mtime": "2022-03-04T13:11:20Z",
"utime": "2022-03-04T13:11:20Z",
"lptime": None,
"mime": "application/ogg",
}